コンピュータプログラミング 同志社大学工学部 水島二郎 平成 16 年 7 月 16 日 i 目次 第 1章 1.1 コンピュータの仕組みと動作および操作 コンピュータの構成 : : : : : : : : : : : : : : : 1.1.1 ハード ウェアとソフトウェア : : : : : : 1.1.2 ハード ウェアの構造 : : : : : : : : : : : 1.1.3 コンピュータの動作原理とソフトウェア 1.1.4 コンピュータの歴史 : : : : : : : : : : : 1.1.5 オペレーティングシステムの歴史 : : : : 1.1.6 高級言語 : : : : : : : : : : : : : : : : : : 1.2 Fortran 1.2.1 Fortran の歴史 1 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 第 2章 2.1 2.2 第 3章 第 4章 3.1 3.2 3.3 3.4 3.5 4.1 4.2 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : コマンド 入力によるコンピュータの操作 MSDOS におけるデ ィレクトリ・ファイルとコマンド : : : : : : : : : : : : : : : : 2.1.1 GUI と CUI : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 2.1.2 ディレクトリとファイル : : : : : : : : : : : : : : : : : : : : : : : : : : : : 2.1.3 基本的な DOS コマンド : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 2.1.4 リダ イレクションとパイプ : : : : : : : : : : : : : : : : : : : : : : : : : : : プログラミングとコンパイル : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 変数と演算 Fortran プログラムの原型 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 変数と定数 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 宣言文と実行文 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 算術式と算術演算子 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : デフォルト入出力 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 3.5.1 read 文と write 文 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 組み込み関数 組込み関数 : : : : : : : : : : : 書式付き入出力 : : : : : : : : : 4.2.1 read 文と format 文の例 4.2.2 write 文と format 文の例 1 1 2 2 3 3 4 6 6 9 9 9 10 11 11 12 15 15 17 18 19 19 20 23 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 23 25 26 28 ii 第 5章 手続き型プログラミングと PAD 図・逐次構造 5.1 アルゴ リズムの作成ー概略設計から詳細設計へ( 段階的詳細化) 5.2 3 つの基本的アルゴ リズム 5.2.1 逐次構造 (Sequence,連接) 5.2.2 選択構造 (Selection) 5.2.3 反復構造 (Repetition,Iteration) 5.3 呼出箱と関数 (function)・サブルーティン (subroutine) 5.4 概略 PAD 図と詳細 PAD 図 5.5 逐次構造プログラミング 35 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 第 6章 第 7章 第 第 第 6.1 6.2 6.3 6.4 7.1 8章 8.1 8.2 9章 9.1 選択構造 論理式と関係演算子 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 論理 if 文 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : ブロック if 文 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : ラベル (文番号) と go to 文 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 反復構造 反復構造と do 文 および continue 文 関数 文関数 : : : : : : 関数副プログラム : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : サブルーティン サブルーティンの記述と呼び出し : : : : : : : : : : : : : : : : : : : : : : : : : : : 10.1 配列 10.2 common 文 (共通記憶領域) 10.3 文字列 10.3.1 文字型変数に関する組み込み関数 10.4 文字列の大小関係 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 11 章 ファイル入出力 12.1 12.2 59 60 外部ファイルと2つのファイル形式 ファイルのオープンと入出力 : : : : ポスト スクリプト による描画法 ポストスクリプトによる図の描き方 図形の描き方{2 次元グラフィックス 67 75 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 12 章 53 67 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 第 43 44 46 50 59 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 10 章 配列・文字列 11.1 11.2 43 53 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 第 35 36 36 36 37 37 37 38 75 79 81 82 83 89 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 89 89 97 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 97 101 1 1 第 章 コンピュータの仕組みと動作および操作 この章では,コンピュータの構成と動作について説明した後,コンピュータとオペレーティング システムの歴史を簡単に述べる.コンピュータを操作する方法には2つの方法があり,1つは主 にウィンド ウズのアイコンをマウスによりクリックやド ラッグをすることによりコンピュータを 操作する方法 (GUI, Graphical User Interface の略称) で,もう1つはコマンド (command) とよ ぶ文字列をユーザーがキーボード から命令を入力することによりコンピュータに動作を指示する 方法である (CUI, Character User Interface の略称) である.この章では主に GUI について学び, 次章で CUI について学ぶ. 1.1 1.1.1 コンピュータの構成 ハード ウェアとソフト ウェア コンピュータは大きく分けると,ハードウェアとソフトウェアから成り立っている.ハードウェアと は私たちが `形がある' と考えるものであり,コンピュータの中心部である CPU(Central Processing Unit,中央演算装置) や情報を一時的に保存するメモリー,ディスプレイ (液晶画面あるいは CRT Cathode-Ray Tube]) などの周辺装置などである.ソフトウェアとは目に見えないものであり,オ ペレーティングシステム (OS, Operating System) や応用ソフトウェアなどである.このほかにも, ハード ウェアとソフトウェアの中間に位置するファームウェア (Firmware) とよばれるものもあり, これはソフトウェアを ROM (Read Only Memory) に記憶したものであり,主にコンピュータの 起動のためのソフトウェアが記憶されている. 入力装置 ! 中央処理装置 " # 記憶装置 図 1.1: ハード ウェアの構造 ! 出力装置 2 1.1.2 第 1 章 コンピュータの仕組みと動作および操作 ハード ウェアの構造 ハード ウェアの構造を模式化して表すと,図 1.1 のようになる.コンピュータの心臓部は中央演 算装置 (CPU) であり,CPU と記憶装置 (RAM,Random Access Memory,ここに一時的に情報 を蓄える) の間はバスとよばれる情報伝達路で結ばれている.コンピュータの周辺装置は入力装置 と出力装置に大別されるが,ハードディスク (Hard Disk) など 入力装置でありしかも出力装置で もあるものも多い.これに対して,マウスやキーボードは入力装置であり,ディスプレ イ (出力画 面) やプリンタは出力装置である (図 1.2 参照). 入力装置 (ファ イル ) jj マウス キーボード フロッピーデ ィスク ハードデ ィスクMO ZIP CD 図 1.1.3 1.2: ! CPU ! " # メモリ (データとプ ログラム ) 出力装置 (ファイル) jj 画面 (CRT,液晶) プ リンタ フロッピーデ ィスク ハードデ ィスクMO ZIP CD コンピュータの構造とデータの流れ コンピュータの動作原理とソフト ウェア 図 1.2 は図 1.1 を少し詳しく表したものである.コンピュータのソフトウェアはオペレーティン グシステム (OS) と応用ソフトに分けられるが,コンピュータに電源を入れるとファームウェア (ROM) に書かれている手順に従って入力装置であるハードディスクよりオペレーティングシステ ム (たとえば,Windows XP や Linux など ) が CPU を通してメモりにコピーされる.オペレーティ ングシステムはコンピュータ全体の管理を行うプログラムであり,マウスやキーボードからの入力 を解釈し,適当な処理をした後,必要があれば出力を行う.応用ソフトウェアはコンピュータの利 用者 (User) がマウスやキーボードから命令を与えると起動する.このときも,応用ソフトはハー ドディスクからメモリに一時的にコピーされて,CPU はメモリにコピーされた応用ソフトに含ま れている命令を一つづつ読み出して,解釈し必要な処理をした後,結果を出力装置に出力する. このように,コンピュータは CPU を中心として動作し,メモリに蓄えた命令群を順番に一つづ つ処理をしていく.現在実用的に使われているコンピュータはほぼすべてがこのような動作を行 い,フォン・ノイマン式計算機とよばれる (これ以外に非ノイマン式計算機も開発されているが広 くは実用化されていない). 1.1. 3 コンピュータの構成 1944 年 1946 年 MARK I (ハーバード 大学) リレー式計算機 ENIAC (ペンシルベニア大学) 真空管 (19000 本, 30m × 1m × 3 m, 30 tons) 配線プログラム EDVAC (ペンシルベニア大学) ノイマン方式 プログ 1949 年 第1世代 1955 ∼ 第2世代 1959 1960 ∼ 1964 第3世代 1964 ∼ ラム内臓 真空管,磁気ド ラム,演算速度ミリ秒 (1kips) トランジスタ,磁気コア,ド ラム,オペレーティングシ ステム (1Mips, 32kwords) (1980) IBM360,IC,(103 Mips, データ通信,16MB(24 ビット ) 表 1.1.4 1.1: コンピュータの歴史 コンピュータの歴史 世界で最初に開発されたコンピュータは,1944 年ハーバード 大学における MARK というリレー 式計算機であるとされている.その後,真空管やトランジスタを使用したコンピュータを経て,IC を用いたコンピュータ IBM360(1964) ができて,ほぼ現在の形のコンピュータとなった.初期の コンピュータから IBM360 ができるまでをまとめると表 1.1 のようになる. コンピュータは IBM360 に代表される汎用機,ミニコンピュータ,ワークステーション,スー パーコンピュータ,マイクロコンピュータ (パーソナルコンピュータ) などに分類することができる. マイクロコンピュータが最初にできたのは,1971 年でインテル社の 4004 という4ビット 1の CPU を使っていた.そのころのマイクロコンピュータにはオペレーティングシステムはなく,BASIC と呼ばれる言語が搭載されており,この言語がオペレーティングシステムの役割も果たしていた. その後,1981年に IBM が IBM PC を発売し,それと前後して NEC が1982年に PC9801 を発売するようになったのち,今の形のマイクロコンピュータとなってきた.アップル社がマッキ ントッシュを発売したのは1984年である. 1.1.5 オペレーティングシステムの歴史 最初にオペレーティングシステムが登場したのは IBM360 の OS としてであり,OS/360 という 名前がつけられた.初めは1つのコンピュータでは1つの仕事 (Job) しかできなかったが,やが ていくつかの仕事が並行してできるようになり,(同時に何人もの) 人間とも対話的に動作するよ うになった (TSS). 初期のマイクロコンピュータにはオペレーティングシステムが搭載されておらず,BASIC とい うインタプリタ型の言語が OS の役割も果たしていた.その後,1974年に Degital Research 社 が CP/M という OS を販売するようになって以降,マイクロコンピュータでも OS を使用するよ 1 コンピュータは 0 と 1 の2つの状態を区別することによって情報を処理する.0 と 1 いずれかを表す情報の量が1 ビットである.8ビットを1バイトともよぶ. 4 第 1 章 コンピュータの仕組みと動作および操作 IBM 1964 年 OS/360 ! PCP ! MVT ! TSO ! SVS ! MVS マルチタスク 多重仮想記憶 MV AOS=V S 1964 年 1974 年頃 BASIC ! CP/M ! CP/M86 1981 年 MSDOS ! OS/2 1990 年 Windows3.0 ! Windows95 ! Windows98 ! Windows XP ! Windows2000 ! WindowsXP Multics 1969 年 1973 年 1978 年 1982 年 1983 年 1986 年 unix ! Version5 ! Version7 ! システム III ! システム V ! システム V リリース 3 1991 年 Linux 誕生 図 1.3: オペレーティングシステムの歴史 うになってきた.現在よく使用されている OS は Windows XP,Linux,Mac OS X などである. これらの中でも Windows XP は普及率が高い.この OS は IBM PC のためのオペレーティング システムであった MS-DOS の流れを汲むものである.この OS は多くの機能をワークステーショ ンやミニコンピュータの OS であった Unix から模倣して設計されてきた. 1.1.6 高級言語 コンピュータの CPU が解釈できる言語を機械語という.機械語は CPU の種類ごとに異なる. 人間が CPU に合わせて機械語で命令を書き,プログラミングを行うのは非常に長い期間の訓練を 要するので,もう少し人間の言語に近い言語が開発されてきた.コンピュータの機能 (CPU の速 度,メモリの量) が低い頃は機械語と1対1に対応するアセンブラーという言語が使われたがその 後,より人間の言語に近い高級言語が用いられるようになってきた. アメリカ合衆国では高級言語として Fortran (Formula Translating system) や COBOL(COmmon Buisiness Oriented Language) などが初期に開発され,ヨーロッパでは Algol という言語が開発さ れて用いられてきた.現在では,工学的な計算や制御には主に C 言語が用いられ,コンピュータ の OS や応用ソフトには C++がよく用いられる.また,ネットワーク関連のソフトやホームペー 1.1. 5 コンピュータの構成 COBOL ||||||||||||||||||||||||||||! (米国防省) 1964 年 BASIC |||||||||-! BASIC の復権 ダートマス Kemeny & Kurtz 1956 年 PL/I FORTRAN |||||{! FORTRAN77 |||-! FORTRAN90 {! FORTRAN95 {! ALGOL ||||||||-! PASCAL ||||||||||||||||||! ヨーロッパ 1960 年 Ada ||||||||||||-! o B |||||||||||! C |||||||||-! C++ |||||||||-! 1970 年 1972 年 1982 年 Ken Thompson Dennis Ritchie Stroustrup # Java 1995 年サンマイクロ 図 1.4: 高級言語の歴史 ジには Java が使われている.高級言語の開発・発展の変遷を図 1.4 にまとめておく. 高級言語である Fortran や C あるいは Pascal などは手続き型の言語といわれ,これらの言語を 用いてプログラミングを行うときにはアルゴ リズム (計算法,処理手順) が重要となる.一方,そ れらの言語の発展形である C++ や Java などはオブジェクト指向プログラミング型言語である. オブジェクト指向プログラミングにおいてはオブジェクトの原型となるクラスの設計が重要であ り,プログラムはオブジェクトにメッセージを送ることによって進行するので,Windows プログ ラミングのように複雑な処理を行うのに適している. 高級言語には BASIC に代表されるインタープ リタ型の言語と Fortran や C に代表されるコン パイラ型の言語がある.インタープ リタ型の言語は CPU が命令を1つづつ機械語に翻訳しなが ら命令を実行する.それに対して,コンパイラ型の言語では人間が行ったプログラムを一括して 機械語に翻訳 (Compile,コンパイル ) していったん機械語のプログラムをディスクに保存した後, CPU が機械語を1つづつ解釈し ,実行する. 6 第 1 章 コンピュータの仕組みと動作および操作 Fortran 66 Fortran Fortran 77 1966 年に ASA よって制定. (American Standards Association) に 1978 年に制定. 90 ISO (International Standard Organization) 委員会 WG5 の下で ANSI (American National Standard Institute) 公 認委員会 J3 が開発.配列、構造体など の機能が強化さ れている.1991 年制定. Fortran 95 Fortran 90 の修正版.1995 年完成.ISO によって 1997 年に採用. 表 1.2: Fortran の変遷 1.2 Fortran 1.2.1 Fortran の歴史 Fortran は,アメリカ合衆国 IBM 社の John. W. Backus 氏が中心となって,1954 年から 1957 年にかけて IBM 社の IBM704 計算機で動作するように開発された.世界初の成功した高級言語 と言われている.Fortran という名は,FORmula TRANslating system( 数式変換システム)の 略称から名付けられており,数式が分かり易く書けることを示している.主として科学技術のた めに開発された言語であり,科学技術の数値計算においてはもっともよく使われてきた言語であ る.Fortran は歴史が長い言語であり,何世代かの標準語が存在する.その変遷を表 1.2 にまとめ ておく. Fortran 77 は機能が低く制約の多い言語であり,コンピュータプログラミングのアルゴ リズム (計算法,処理手順) の研究や学習を目指す人にとっては不満の残る言語である.しかし,コンピュー タプログラミングを専門とするのではなく,簡単な計算手段と考える人にとっては Fortran 77 の 制約が多くて機能が低いことが,始めて学ぶときには利点ともなる.Fortran 77 が発展してでき た Fortran 90 や Fortran 95 には C 言語の便利な機能や特徴が取り入れられて比較的自由度の高 いプログラミングが可能となっている.ただし,自由度の高い分だけ初学者にとっては学ぶことが 多く,ある処理をするためにいくつかの同じ機能をもつ命令のどれを使うか迷うことも多いと思 われる.したがって,コンピュータプログラミングの初学者を対象とするこの説明書では Fortran 77 のさらにその1部に限定して説明を行うことによって学び易く,書かれていることがらのすべ てを完全に理解することを目標としている. 1.2. Fortran 7 第1章 演習問題 問題 1. ブラウザ ( インターネットエックスプローラーなど ) を起動し ,アドレスバーに同志社大 学のド メイン \www.doshisha.ac.jp" をキーボード から入力することにより,同志社大学の ホームページを表示せよ. 問題 2. Windows のファイラー (エクスプローラー,マイコンピュータ) を起動し,ディスクド ラ イブ D のルート (¥) に \Computer" という名前のフォルダー (デ ィレクトリ) を作成せよ. 問題 3. エディタ (秀丸もしくはノートパッド ) を起動して,自分の学籍番号,名前,このコンピュー タプログラミングで学びたいことがらを入力し,\names.txt " というファイル名でディスク D のフォルダー \Computer" に保存せよ. 問題 4. ファイル \names.txt " に書かれている内容を指示するメイルアドレスにメイルとして送れ. 問題 5. 問題 3 で保存したファイルをフロッピーデ ィスクまたは MO ディスクにコピーせよ. 問題 6. 問題 3 で作成したド ライブ 去せよ. D 内のファイル \names.txt " とフォルダー \Computer" を消 9 2 第 章 コマンド 入力によるコンピュータの操作 この章では,コマンド (command) とよぶ文字列をユーザーがキーボードから命令を入力すること によりコンピュータに動作を指示する方法である (CUI, Character User Interface の略称) につい て説明する. 2.1 MSDOS におけるディレクト リ・ファイルとコマンド 2.1.1 GUI と CUI コンピュータはデータ処理機であり,入力されたデータを人間の指示に従って処理して出力を する.このとき,人間の指示はコンピュータと対話的 ( インタラクティブ ) に与えられる場合と一 括して (バッチとして ) 与えられる場合がある. 人がコンピュータに対話的に指示を与える方法には2つの方法がある.1つは,ディスプレイ上 に表示されたアイコン (それぞれの応用ソフトを象徴するマーク) を表示し ,マウスでアイコンを クリックすることによってコンピュータに指示する方法であり,このような方法を GUI(Graphical User Interface) という.一方,キーボード からコマンド とよばれる文字列を入力することによっ てコンピュータに指示を与える方法を CUI(Charactar-based User Interface) という.GUI によっ てコンピュータに指示を与える方法は人間感覚に訴えるので,初心者にも分かりやすく,画面上 から目的の情報を見つけ出したり,マウスの操作によって情報を取り出したりすることも簡単に できる.これにより,現在では GUI によりコンピュータを使う方法が一般的である.しかし,プ ログラミングにおいては多くの場合にコマンドを入力する CUI が好んで用いられる.これは,何 度も繰り返し同じ作業をするためには GUI よりも CUI の方が効率的で人間の疲れが少ないため であると考えられる. 対話的にコンピュータに指示を与えるのではなく,一括して与えるためにはコンピュータへの 指示をハードディスクを代表とする記憶装置に記憶しておく必要がある.このとき,コンピュー タへの指示は多くの場合コンピュータに備え付けられている CPU (Central Processing Unit,中 央演算装置) が直接解釈できる機械語で書かれている.しかし,人間が機械語でコンピュータへの 指示をすべて書いて記憶装置に記憶するのにはかなりの訓練を必要とし,あまり効率的ではない. そのため,高級言語とよばれる人間に理解しやすい言語 (Fortran, C, C++, Java, Pascal など ) が 開発され,これらの高級言語でプログラミングをして,そのプログラムをコンパイラーとよぶ変 換プログラムにより機械語に翻訳し ,翻訳した結果を記憶装置に記憶するのが一般的である. この章では CUI によりコンピュータに指示をするためのコマンドと高級言語を用いたプログラ 10 第 2 章 コマンド 入力によるコンピュータの操作 ムを機械語に翻訳する方法について簡単に説明する. 2.1.2 ディレクト リとファイル ファイルとはデータや文章,プログラムなどの集まりのことである.この集まりに名前を付け ることができる.名前は半角 (英数字) で 255 文字以内であり,ピリオドを含みピリオド の後ろに はファイルの種類 (実行ファイル,データファイル,画像ファイルなど ) を表す文字 (拡張子) をつ けることができる.CUI でコンピュータを使う場合には,ファイルの名前には 8 文字以内の英数 字を用い,拡張子には 3 文字以内の英数字を用いるのが便利である.GUI では1つのファイルに 1つのアイコンが割り当てられる.これらを整理しておくと ファイル :データや文章,プログラムの集まり. ファイル名:半角で 255 文字以下.ピリオド 以下は拡張子という (原則 3 文字以下). となる.ファイル名の例として (例)prog.for, prog.f90,prog.obj, prog.exe, prog.c, prog.cpp results.dat, document.txt などが考えられる.Fortran90 で書かれたプログラムには f90,Fortran77 には for や f77 などの 拡張子を用いる.obj という拡張子がついたファイルは Fortran などの高級言語で書かれたファイ ルを機械語に翻訳したファイルであるが,このファイルに書かれた機械語プログラムを実行する ことはできず,あとに説明するリンク (ライブラリとの結合) を行ってできる exe という拡張子の ついたファイルに変換すれば実行できるファイルとなる. ディレクトリはファイルを集めたひとかたまりのものである.あるデ ィレ クトリの中にはディ レ クトリも含むことができる.含まれるディレクトリをサブディレクトリという.ディレクトリ は GUI でいうフォルダーのことである.まとめると, = ”がある. ディレクトリ:デ ィスクド ライブにはルートデ ィレクトリ“ Y = ”の下にサブ・デ ィレクトリを作ることができる. “Y となる.デ ィレクトリにも 255 文字以内の名前をつけることができる.例として C:Y =prog, C:Y =progY =data などが考えられる. CUI コマンド を用いてコンピュータを操作するときには,いろいろなディレクトリの間を `cd' というコマンド を用いて「往ったり来たり」することになる.このとき,現在自分が居るディレ クトリをカレントデ ィレクトリとよぶ.特に指定しない限り (デフォルトで ),操作できるのはこ のカレントディレクトリに含まれているファイルである.カレントディレクトリ以外のディレク トリにあるファイルを指定するのにはパス (path) という経路を指定する.パスとは外部記憶装置 2.1. MSDOS におけるデ ィレクトリ・ファイルとコマンド 11 内でファイルやディレクトリの所在を示す文字列であり,ファイルやディレクトリのコンピュー タ内での住所にあたる.MS-DOS や Windows では「 Y = 」でディレクトリ名を区切る.記憶装置は, ド ライブ名 (「 C:¥」など ) を頂点とする木構造になっており,これに沿って頂点から目的のファ イルやディレ クトリまでのすべての道筋を記述するのが「絶対パス」である.これに対し ,起点 となる現在位置から,目的のファイルやディレクトリまでの道筋を記述するのが「相対パス」で ある.相対パスでは,起点となるデ ィレクトリを「 . 」で,上位デ ィレクトリを「 .. 」であらわす. 絶対パスの例として, C:Y =progY =problemsY =prob1.f77 などのように指定できる. 2.1.3 基本的な DOS コマンド CUI コマンドは使っているコンピュータの OS(オペレーションシステム) によって異なる.Windows XP などのマイクロソフト社の OS では MS-DOS コマンドと呼ばれる次のようなコマンドが ある. dir (dir/w)............. ファイルとディレクトリ名のリストを表示する formatA: .............. A ド ライブに入ってるデ ィスケットをフォーマットする mkdirdirect1 .......... デ ィレクトリ direct1 を新しく作る rmdirdirect1 .......... デ ィレクトリ direct1 を削除する デ ィレクトリ direct1 に移動する cddirect1 ............. copyfile1file2 ....... ファイル file1 をファイル file2 にコピーする delfile1 .............. ファイル file1 を消去する ファイル file1 の内容を表示する typefile1 ............. printfile1 ............ ファイル file1 の内容を印刷する help ................... 主な DOS コマンド の説明を表示する 2.1.4 リダイレクションとパイプ MS-DOS には Unix と呼ばれる OS から取り込んだリダ イレ クションとパイプという機能があ る.リダ イレクションはプログラムやコマンド の出力 (ただし,標準出力とよばれる出力のみ) を ファイルに書き出す機能であり,パイプはプログラムやコマンド を実行した結果を他のプログラ ムやコマンドに引き渡す機能である.まとめると, ・リダ イレクション (標準出力をファイルに受け渡す) C:Y => numeric > results.dat (numeric の実行結果 (画面出力) を results.dat に書き込む) ・パイプ (コマンド を連続して実行する) C:Y => dir | sort (dir コマンドを実行した後,その出力を入力にデータとして sort コマンド を実行する) 12 第 2 章 コマンド 入力によるコンピュータの操作 となる.MS-DOS コマンドとリダ イレクション・パイプについては MS-DOS のマニュアルを参考 にすること. 2.2 プログラミングとコンパイル コンピュータに一括して指示を与える方法のひとつに,高級言語によるプログラミングがある. ここでは,Fortran77 によるプログラミングの手順を簡単に紹介する. コンピュータを使って問題を解こうとする場合,その問題に関する知識が重要である.問題を 解析し,解く手順 (アルゴ リズム) を PAD 図 (Problem Analysis Diagram,第5章参照) に表現す る.次に,エディタを用いて Fortran のプログラムを入力し,ファイル (たとえば prog.for) を作 る (このファイルをソースファイルという).ソースファイルをコンパイル (機械語に翻訳) し ,実 行ファイル (たとえば prog.exe) を作る.この実行ファイルを実行することにより目的の結果を得 る.もし ,コンパイル時にエラーが生じたり実行結果が誤っているときは再びエディタを用いて Fortran のソースファイルを修正した後に同様の手順でコンパイルと実行を行う.まとめると次の ような手順になる. プログラムの設計 ? プログラムの作成 ? コンパイル ? 実行 問題を解析し ,解く手順を考える (PAD 図) 秀丸エデ ィタまたはメモ帳により Fortran のプログラムを入力し ,prog.for を作成 C:Y =>f77 prog.for C:Y =>prog ? 結果を見る この手順を実行した結果,目的の結果が得られる. ここで行ったこととその結果できるファイルについてもう一度整理をしておこう.Fortran 77 でプログラミングを行う人がエデ ィタを使って作るファイルが prog.for (prog はプログラマが自 2.2. プログラミングとコンパイル 13 由につけることのできる名前.for はあらかじめ決められた就職子) である.このファイルをコン パイル (機械語に翻訳) すると実行ファイル prog.exe ができる (コンパイラが作る).このプログラ ムを実行するためには c:Y =>prog と入力する必要がある. 14 第 2 章 コマンド 入力によるコンピュータの操作 第2章 演習問題 問題 1. エディタ (秀丸もしくはノートパッド ) 起動して,自分の将来の夢について200字程度に まとめ,その内容を自分のフロッピーディスクまたは MO の中にファイル名 \mydream.txt" で保存せよ. 問題 2. DOS コマンド (mkdir) を用いて,E ド ライブのルートデ ィレクトリ (Y =) の下にディレク トリ (フォルダー)computer を作り,その下にディレクトリ programing を作成せよ.問題 1 で保存したフロッピーディスクまたは MO ディスク内のファイル \mydream.txt" をディレ =computerY =programingY = 中にコピーせよ. クトリ E:Y 問題 3. DOS(rename) コマンドを用いて,ファイル E:Y =computerY =programingY =mydream.txt の =computerY =programingY =comestrue.txt に変更せよ. 名前を E:Y 問題 4. エデ ィタ (秀丸もし くはノートパッド ) 起動して,つぎ のテキストを入力し ,その内容を ファイル名 \hello.for" で保存せよ. programsimplest write(*,*)'HelloFortran' write(*,*)'ThisisthesimplestprograminFortran' end 問題 5. DOS コマンド (f77) を用いて,Fortran ソースファイル \hello.for" をコンパイルせよ.コ ンパイルが成功したら,実行 (hello) を行え. 15 3 第 章 変数と演算 この章では簡単なプログラムの例を通してプログラミングの基本的な方法について学ぶ.プログ ラミングを行って,その結果を見るために出力 (ディスプレ イまたはプ リンタ出力) をする必要が ある.出力の方法には書式なし出力と書式付き出力があるが,この章では書式なし出力について 説明する.書式なし出力については非常に簡単に使えるが,自分の思うような形で出力しようと するときには限界がある.書式付き出力は次章で説明する. 3.1 Fortran プログラムの原型 Fortran プログラムの書き方には2通りの方法がある.1つは固定形式であり,Fortran77 より も古い規格の Fortran ではこの固定形式を使う.もう1つは自由形式とよばれる形式で Fortran90 よりも新しい規格の Fortran で用いられる.ここでは,Fortran77 を使うことを想定して固定形式 についてのみ説明を行う.Fortran のプログラムの原型はつぎのようなプログラムである. プログラム例 3.1 programsimplest write(*,*)'HelloFortran' write(*,*)'ThisisthesimplestprograminFortran' stop end Fortran のプログラムはプログラム名 program name で始まり,end で終わる.ここで,プログラ ム名 name はプログラミングをする人が6文字以内の英字で自由につけることができる (現在使わ れているコンパイラは Fortran90 仕様なので今後この例のように実際には31文字まで使っても よい).固定形式では,行の初め6桁 (カラム) には通常スペースを入力する.第1桁に `C' または `*' を入れておくと,Fortran のコンパイラーはその行の解釈を行わず無視する.したがって,そ のような行はプログラマーの注釈 (コメント ) として使うことができる.また,第2∼5桁は文番 号 (ラベル ) を入れるための桁であり,第6桁に空白または0以外の文字を入れておくとその行は 前の行の続きであると見なされる.Fortran のプログラムで1行は80桁まで文字を入れることが できるが,後ろの8桁はプログラムの番号を入れるのに使われるので実質的には1行に72桁目 までしか使うことができない.72桁以上に長い命令文を入力するときに第6桁に `*' などの文字 を入れるのである.end 文にはプログラム名を書くこともできる.したがって,end simplest とす 16 第3章 変数と演算 ることもできる.また,end の前に stop という文を書く.この stop 文が実行されるとプログラム の実行が停止する. 本来の Fortran77 では,全て大文字で書く決まりだが,大文字と小文字は区別されないので,例 にあるように小文字で書くことも多い.また,基本的に使用できる文字は半角文字の英数字 (A-Z, 0-9) と特殊文字 ( = + * / ( ) , . ' : 空白 ¥) である.コメント文には漢字 (コード ) も書くこ とができるが,プログラム中に漢字の空白コードが入るとコンパイル時にエラーとなり,そのエ ラーの原因を探すのが難しくなるため漢字の使用は控える方がよい. プログラム例の第2行目にある write 文は液晶画面などの標準出力とよばれる出力装置に出力 する 命令 である.コンピュータでは入力装置や出力装置もファイルとして取り扱う.標準出力と いう装置は1つのファイルであり,これをいろいろな出力装置に割り当てて使うことができる.通 常は標準出力は液晶画面などの画面出力に割り当てられている.write 文の後ろのかっこのなかに は2つの `*' がコンマで区切られて書かれている.最初の `*' 印は標準出力への出力であることを 示し,後ろの `*' 印は `書式' なし出力であることを示している.書式については次章で詳しく説明 するので,ここでは `write(*,*)' と書くものとしておく.`write(*,*)' の後ろには引用符号 (シング ルクォテーションマークまたはアポストロフィー) で挟まれたいくつかの文字が書かれている. 引 用符号で挟まれたいくつかの文字は文字列といい文字の集まりである.引用符号にはダブルクォ テーションマークという符号もあり,文字列を表すのにこちらを用いてもよい.このようにして 出力したい文字や文章を文字列として出力することができる.例にあるように write 文を2つ書く と,2つの文字列の出力の間には改行コードが入り,2行の文が出力される. ; 基本データ型 integer real (または real*4) double precision (または real*8) character complex (complex*8) logical 表 3.1: データ長 意味 4バイト 整 数 型( 最 大 2147483647 ) 4バイト 8バイト 1バイト 8バイト 1バイト 10 桁 ,-2147483648 ∼ 実 数 型( 有 効 数 字 7 桁 ,絶 対 値 の 範 囲 は 1.175494e-38 ∼ 3.402823e+38 ) 倍精度実数型(有効数字 16 桁,絶対値の範囲は 2.225074d-308 ∼ 1.797693d+308 ) 文字型( 処理系に応じて表現可能な文字) 複素数型( 実数部 4バイト ] と虚数部 4バイ ト ] が一対.精度は実数型データと同じ ) 論理型( .true. もしくは .false. ) 基本データ型.e-38 や d-308 などはそれぞれ 10;38 や 10;308 などを表す. 3.2. 3.2 17 変数と定数 変数と定数 Fortran では変数や定数を定義することができる.定数にはデータの型があり,整数型 (integer), 実数型 (real),倍精度実数型 (double precision),複素数型 (complex),論理型 (logical),文字型 (character) の6種類の型がある.整数型定数は小数点を含まない数字で,2134 や ;4356 などで ある.整数型定数で表すことができる数には最大値と最小値があり,これを越える数を表そうと すると正しい結果が得られない (どのような結果が得られるかを知るためにはコンピュータ内部で の数の表現について学ぶ必要がある).実数型定数は 1.456 や 567:32 などのような固定小数点 で表す方法と 2.569e 2 (2:569 10;2 , 0.02569) などのような浮動小数点で表す方法がある.倍精 度実数定数は 2.653d0 や 4.9832 d 4 (4:9832 10;4 , 0.00049832) のように後ろに d0 などをつけ て表す.複素数型定数は2つの実数を (3:54, 0:32) などのようにかっこでくくって表す (この例 では 3.54 i 0.32 の意味である).論理型定数は .true. または .false. で表す.文字型定数は文字 列を引用符号 (シングルクォテーションマークまたはアポストロフィー) で両端を挟んで,'ABC' などのように表す. 変数にも定数と同じデータの型があり,それらは整数型 (integer),実数型 (real),倍精度実数型 (double precision),複素数型 (complex),論理型 (logical),文字型 (character) の6種類の型であ る.これらの変数には英文字で始まる6文字以内の英数字の名前をつけることができる.原則と して大文字と小文字を区別しない.具体的に変数を定義しないで使用することも可能であり,そ のときは標準的型宣言 (デフォルト ) により,`i' から `n' までの英文字で始まる変数は整数型,`a' から `h' と `o' から `z' までの英文字で始まる変数は実数型であると解釈される. コンピュータの内部では情報を 0 と 1 で表しており,この情報量を1ビットとよぶ.英数字など の1文字 (文字型データ) は8ビットで表される.8ビットを1バイトという.整数型データは4 バイトで表し,実数型も4バイトである.倍精度型実数と複素数型データは8バイトで表現され る (表 3.1 参照).整数を定義して,その整数に数を代入し,出力する簡単なプログラム例を示す. ; ; ; ; ; プログラム例 3.2 programinteger_definition integeri,j:i と j を整数として定義 i=3:変数 i に3を代入する j=5:変数 j に5を代入する write(*,*)i,j:i と j の値を標準出力に出力する stop end プログラム例 3.2 では2行目で変数 i と j を整数として定義し,3行目と4行目でそれらに値を 代入している.Fortran では等号の記号 `=' を等しいという意味では用いず,代入するという意味 で用いる.したがって,後に説明するように,等号の両辺を等しいとおいて方程式のように解い てはいけない.たとえば,i=4, i=i+2 と書けば,変数 i が表す値は4となる. 18 3.3 第3章 変数と演算 宣言文と実行文 プログラム例 3.2 で簡単なプログラムを見てきた.そこではプログラム名に引き続いて,変数の 定義を行った.このように,整数や実数を定義するとき,それぞれ integeri,j:i と j を整数として定義 realx,y:x と y を実数として定義 のように定義する.また,1文字変数や n 文字の変数を定義するときは characterc:c を文字として定義 character*ns:s を n 文字の文字列として定義 と記述する.ただし ,実際のプログラムでは,n は具体的な整数 (たとえば 10 など ) を記入する. このような変数の定義文を型宣言文という.型宣言文の他にも dimension 文や parameter 文など のような宣言文があるが,それらについては後に (第 10 章) 説明する.一方,プログラム例 3.2 で i=3,j=5,write(*,*) i, j などは実行文という.宣言文は最初の実行文よりも前に記述する必要が ある. 3.2 節でも述べたように Fortran77 には標準的型宣言 (デフォルト ) という機能があり,あえて型 宣言を行わないときには (a{h, o{z) のいずれかの英文字から始まる変数は実数であり,(i{n) で始 まる変数は整数であると解釈される.プログラム例 3.2 ではこのデフォルトのルールと矛盾がない ように変数を定義したので,型宣言文を省略しても同じ実行結果が得られるが,プログラミング において自分が使用する変数は必ず宣言したのち使う習慣をつけておくことがよいプログラムを 書けるようになる近道である.このときも,変数名をデフォルトのルールと矛盾なくつける習慣 をつけることが望ましい.デフォルトの型宣言以外に暗黙の型宣言 ( インプ リシット ) という機能 もある.この宣言法は implicitdoubleprecision(a-h,o-z) のように用い,この例では (a-h, o-z) から始まる変数は倍精度実数とするという宣言である (implicit 文をあまり多用しないで,変数は個々に宣言するように ). 演算子の優先順位 1 2 3 表 演算子 (べき乗) , ( 乗算,除算) +,; (加算,減算) = 3.2: 算術演算子とそれらの間の優先順位 3.4. 3.4 19 算術式と算術演算子 算術式と算術演算子 ; 変数間および定数と変数の間の算術演算子はべき乗 ( ),わり算 (=),かけ算 ( ),引き算 ( ), 足し算 (+) の5種類であり,それらの間の優先順位は通常の数学における優先順位と同じである. これらの演算子は整数,実数,倍精度実数,複素数のデータ型に関わらず同じ記号を使うことが できる.これらのデータ型変数を混合して使用するときは,データ長の長い方のデータ型に変換 される.ただし ,代入文においてデータ長の長い変数を短い変数に代入すると短いデータ長に変 換されるが,あまりこの機能を使わずに後に説明する組み込み関数 (型変換) を使うよう心がける こと.算術演算子とその優先順位を表 3.2 にまとめておく. 整数 i を整数 j で割るときには注意が必要である.たとえば 2=3 は整数間のわり算であり,結果 は整数化されて 0 となる.数学で期待するようにこの商を 1:5 としたいときには 2:=3: とする.あ るいは変数同士の場合には,real(i)/real(j) のように表す.ここで,real というのは次章で説明す る組み込み関数の1つであり,整数を実数に型変換する関数である.型変換 real(i) の代わりに oat(i) を用いても同じ結果となる. 数学における計算と同様,演算の順序にも注意が必要である.たとえば,x = 3 のとき, y = 2 ;+13 + x x 3 x を計算したいときには,次のようにプログラムを書く. プログラム例 3.3 programmath_operator realx,y:x と y を実数として定義 x=3.0:変数 x に 3.0 を代入する y=(2.0*x+3.0)/(x-1.0)+x**3:y を計算する write(*,*)y:y の値を標準出力に出力する stop end このプログラムでは x=3.0 と実数であることを明確に表している.もちろん x=3 と書くことも可 能であるが,常に自分で実数は実数であることを意識してプログラミングをするように心がける こと.ここで,x 3 とは x3 のことである. 3.5 デフォルト 入出力 入力機器や出力機器もコンピュータはファイルとしてとらえる.通常標準入力はキーボード やマ ウスであり,標準出力はデ ィスプレ イである.キーボードからデータを入力をするときには read 文を用い,ディスプレ イに出力したいときには write 文を用いる. 20 第3章 変数と演算 3.5.1 read 文と write 文 最も簡単にデータを読み込むときは書式なし read 文を用いる.また,データを出力するには write 文を用いる.たとえば,整数 i と実数 x をキーボードから読み込みそれらの積 y を出力する プログラムは次のようになる. プログラム例 3.4 programread_command integeri:i を整数として定義 realx,y:x と y を実数として定義 read(*,*)i,x:i と j の値をキーボード より読む y=float(i)*x:y を計算する (i*x としてもよい) write(*,*)i,x,y:i,x,y の値を標準出力に出力する stop end このプログラムをコンパイルして実行し,キーボードから1つの整数と1つの実数を間にスペー スまたはコンマを挟んで入力すると,実行結果が出力される.しかし ,実行時にキーボード から なにを入力していいのかわかりにくい.したがって,キーボード から入力すべきデータを明示す るともっとわかりやすくなる.また,出力時にも出力する数値がなにを表すのかわかりやすく説 明しておくとよい.それらの点を修正して,つぎのようなプログラムを作る. プログラム例 3.5 programread_command integeri:i を整数として定義 realx,y:x と y を実数として定義 write(*,*)'inputiandx':変数 i と x の値の入力を促す read(*,*)i,x:i と j の値をキーボード より読む y=float(i)*x:y を計算する (i*x としてもよい) write(*,*)'i=',i,'x=x',x,'y=',y:i,x,y の値を標準出力に出力する stop end ここまでの復習を兼ねていくつかの例題を考えてみよう. 【例題 3.1 】実数 x をキーボードから読み込み,その x を出力するプログラムを作れ. program Example 3_1 real x write(*,*) 'input x' : 変数 x を実数として定義 : キーボード 入力のプロンプトを出力 3.5. 21 デフォルト入出力 read(*,*) x write(*,*) 'x=',x : 整数 x をキーボードから読み込む (例えば 2.5) : x を出力する stop end 【例題 3.2 】整数 n をキーボード から読み込み,1 から n までの和 s を式 s 算するプログラムを作れ. 【解答例】演算子の優先順位に注意してプログラムを作る. = 12 ( + 1) により計 n n program Example 3_2 integer n,s : 変数 n,s を整数として定義 write(*,*) 'input an integer' : キーボード 入力のプロンプトを出力 read(*,*) n : 整数 n をキーボードから読み込む s=n*(n+1)/2 : s を計算する write(*,*) 'n=',n,', s=',s : n= 3, s=6 のように出力する stop end 【例題 3.3 】球の半径 (実数)r をキーボードから読み込み,球の体積 v ムを作れ. 【解答例】演算子の優先順位に注意してプログラムを作る. = 43 r program Example 3_3 real r, v, pi pi=3.1415926 write(*,*) 'input a radius(real)' read(*,*) r v=4.0/3.0*pi*r**3 write(*,*) 'r=',r,' v=',v stop end : pi=4.0*atan(1.0) 3 を計算するプログラ 22 第3章 変数と演算 第3章 演習問題 問題 1. 問題 2. 学籍番号と名前をそれぞれキーボードから読み込んで,それらを画面に出力するプログラ ムを作成せよ.ただし,文字をキーボード から入力するときはクォテーション (') で両端を 囲む必要があることに注意せよ. ; 2 つの実数 a, b をキーボード より読み込み,それらの和 (a + b)・差 (a b)・積 (a b)・ a 商 ( ) を求めて出力するプログラムを作れ.b として 0 を入力するとどのような結果が得ら b れるか調べよ。 問題 3. 2 つの整数 a, b をキーボード より読み込み,それらの積 a b と商 a ÷ b および余りを求 めて出力するプログラムを作れ.このとき,b として 0 を入力するとどのような結果が得ら れるか調べよ. 問題 4. 整数 a をキーボード より読み込み,その自乗 (2乗)a2 を出力するプログラムを作り,a と していろいろな値を入力してその結果を確かめよ.自分が期待する値と異なる結果が得られ ることがある.その整数 a の値と自乗の値 a2 を書け. 問題 5. 変数 (実数)x をキーボード より読み込み,関数 f (x) = ラムを作れ. 3 2 + 2 + 5 の値を求めるプログ 2+1 x x x 23 4 第 章 組み込み関数 数学では関数という考え方を用いる.関数では独立変数の値を決めると関数値 (従属変数) が決ま る.プログラミングの過程においても,ある1つまたは1組の変数の値を与えると1つの値が決 まる関数があればプログラミングがずいぶんと容易になり,簡潔になる.Fortran ではいくつかの 関数があらかじめ用意されている.それらの関数を組み込み関数という.また,必要な関数を自 分で作ることもできる (第8章).この章では組み込み関数の使い方について説明する.書式付き 出力についても学ぶ. 4.1 組込み関数 組込み関数には,型変換関数・数学関数・文字列関数などがある.型変換関数は整数を実数に 変換 (real(i)) したり, 文字変数を整数変数に変換 (ichar(a)) したりするのに用いる.数学演算子 `+' や ` ' などが整数間の演算にも実数間の演算にも使えたように,1つの関数がいろいろな型の 変数に使える場合がある.たとえば,関数 sin(x) は変数 x (関数を呼び出すときの独立変数を引 数という) が実数型であっても,倍精度実数型でも,複素数型変数であってもよい (総称名での呼 び出しという).このとき,関数を呼び出した結果として得られる関数値の型は引数の型と同じで ある.引数と関数値の型を明確に使い分けたいときは (使い分けることを推奨する),実数型のと きには sin(x),倍精度実数のときは dsin(x),複素数型のときは csin(x) のように書く.このとき, sin(x) を総称名と呼び,引数が実数型のときの sin(x), 倍精度実数型のときの dsin(x),複素数型 のときの csin(x) を個別名と呼ぶ. この章の付録 A に組み込み関数表を付けてある.この表を見て分かるように,およそのルール として,総称名が func であれば,倍精度型変数引数をもつ倍精度型関数は dfunc であり,複素 数型変数引数をもつ複素数型関数は cfunc である.ただし,例外も多いので付録 A を参考にする こと. ; 【例題 4.1 】正弦関数である sin(x) に引数として実数型変数を代入すれば得られる関数値は実数で あり,引数に倍精度実数型変数を代入すれば得られる値は倍精度実数型となることを確かめよ. 【解答例】正弦関数 sin(x) の引数として,x= を実数型変数を代入したときと,倍精度実数型変 数を代入したときの結果を比較する. 2 program Example 4_1 real*4 a,pi : 変数 a を実数型として定義 real*8 b,dpi : 変数 b を倍精度実数型として定義 24 第4章 pi=3.1415926 : π (単精度) の値 dpi=3.14159265358979d0 : π (倍精度) の値 a=0.5*pi : a=π/2 を実数型で代入 b=0.5d0*dpi : b=π/2 を倍精度実数型で代入 write(*,*)'sin(a)=',sin(a) write(*,*)'sin(b)=',sin(b) 組み込み関数 : 実数型関数 sin(a) を標準出力 : 倍精度実数型関数 sin(b) を標準出力 stop end このプログラムをコンパイルして実行すると,実行結果として sin(a) は実数型,sin(b) は倍精度 実数型として次のように得られる. 【例 4.1 実行結果】 sin(a)= 1.000000 sin(b)= 1.00000000000000 ここで,sin(b) については dsin(b) としても同じ 結果が得られる.ただし ,注意すべきことがら として,倍精度実数型関数で引数を実数型とするとエラーとなる (例 4.1 では dsin(a) とすると間 違い). 次に,型変換の例としてを文字変数 a を整数 i に変換する関数 ichar(i) を用いたプログラムを 紹介する.コンピュータの内部では英文字や数字もアスキーコード という整数で表されている (表 4.4).関数 ichar(a) は文字 a の ASCII コード に対応する整数を返り値とする関数である. 【例題 4.2 】文字 `A' はコンピュータ内部でどのような整数 (アスキーコード ) で表されているか調 べるプログラムを作れ. 【解答例】文字変数 a に文字 `A' を代入して,文字を整数に変換する関数 ichar(a) を用いる. program Example 4_2 character a : 変数 a を文字として定義 integer i : 変数 i を整数として定義 a='A' : 文字変数に文字 A を代入 i=ichar(a) : 文字変数 a を整数 i に変換 write(*,*) i : 整数 i を標準出力に出力 stop end 4.2. 25 書式付き入出力 このプログラムをコンパイルして実行すると,実行結果として 65 が得られる.文字 `A' は ASCII コード 表では 65 で表されていることがわかる. 逆の型変換の例として整数 i を文字に変換する関数 char(i) を用いたプログラムを紹介する.関 数 char(i) は整数 i に対応する ASCII コード (文字,表 4.4 参照) を返り値とする関数である. 【例題 4.3 】標準出力にベルコード を出力することにより,コンピュータに備え付けられているス ピーカーからピッという音が出ることを確かめるプログラムを作れ. 【解答例】アスキーコード 中の制御コード を直接に文字変数に代入することはできないので,整数 を文字に変換する関数 char(i) を用いる. program Example 4_3 character a : 変数 a を文字として定義 a=char(7) : 整数 7 を文字に変換 write(*,*) a : 文字 a を標準出力に出力 stop end この例題で,整数 7 に対応する ASCII コードは BEL(ベル ) なので,英文字が標準出力に出力され る代わりにピッという音が出る. 数学関数の中には三角関数も定義されている.関数 arctan(x) は逆正接であり,x = tan(y ) を 満たす y の値 ( =2 y =2) を返す関数である.この関数を用いて の値を求めるプログラ ムを作る次の例題を考える. ; 【例題 4.4 】 の値を = arctan(1:0) 4 から計算するプログラムを作れ. 【解答例】tan = 1 であることを用いる. 4 program Example 4_4 real x : 変数 x を実数として定義 x=atan(1.0)*4.0 : 変数 x に arctan(1.0)*4.0 を代入 write(*,*) 'pi =',x : 変数 x を出力 stop end 4.2 書式付き入出力 コンピュータ内部では,英数文字アスキーコード で表現されており,整数や実数は多くの場合 IEEE の規格によって `0' と `1' のビット列 (実数なら4バイト =32ビット ) で表現されている.標 26 第4章 利用法 int(a) real(a) dble(a) cmplx(a1,a2) ichar(a) char(a) 組み込み関数 機能 引数の型 関数の型 整数型への変換 実,倍,複 整数型 実数型への変換 整,倍,複 実数型 倍精度実数型への変換 整,実,複 倍精度実数型 複素数方への変換 整,実,倍 複素数型 整数への変換 文字型 整数型 文字への変換 整 文字型 4.1: 組込み関数1.型変換.(整:整数型,実:実数型,倍:倍精度実数型,複:複素数型). complx は2つの引数を倍精度型変数として代入することも可能であるが関数値の精度は実部と虚 部がそれぞれ実数型変数と同じ精度となる (ただし,コンパイラによっては倍精度複素数型変数を サポートしている). 表 準入出力においてこれらの変数がもつ値を通常の数学で表すような10進法などで出力を行うと きはコンピュータがこれらのデータを変換して出力する.このとき,書式付き入出力を用いると データの出力形式を指定することができる.Fortran77 では書式付入出力に format 文を用いる. たとえば,実数を固定小数点で入出力するには f6.3 などのように指定する.ここで,f は固定小数 点入出力であることを表し,6 は合計6桁で入出力すること,3 はそのうち小数点以下は3桁であ ることを表している.実数を固定小数点出力するときは符号と小数点に1桁必要なので,f6.3 の ときには整数部が1桁しかないことに注意が必要である.したがって,-10.324 などの出力を f6.3 で行うと,出力桁数が不足して結果として******と出力されることがある (このときの取り扱いは コンパイラーの種類による).入力も出力も書式 format の記述法は同じである.f6.3 などの記号は 編集記述子という.主な編集記述子を表 4.3 にまとめておく. 4.2.1 read 文と format 文の例 2つの実数をそれぞれ 8 桁で間に何も区切り記号を入れずに読むプログラムと入力例を次に示す. read(*,100) x,y 100 format(2f8.5) 入力例 3.14154.2526 この例で f8.5 の前についている 2 は f8.5 を2つ繰り返して書くのと同じ意味である. 同様に,2つの整数を間に 2 つの空白をあけて読む例は次のようになる. read(*,100) m,n 100 format(i8,2x,i8) 4.2. 27 書式付き入出力 種類 利用法 最大値 最小値 平方根 指数 自然対数 常用対数 正弦 余弦 正接 逆正弦 逆余弦 逆正接 (1) 逆正接 (2) 双曲線正弦 双曲線余弦 双曲線正接 絶対値 剰余 切り捨て 四捨五入 四捨五入整数化 max(a1,a2, …) min(a1,a2, …) sqrt(a) exp(a) log(a) log10(a) sin(a) cos(a) tan(a) asin(a) acos(a) atan(a) atan2(a1,a2) sinh(a) cosh(a) tanh(a) abs(a) mod(a1,a2) aint(a) anint(a) nint(a) 機能 引数の型 関数の型 カッコの中の最大値 整,実,倍 整,実,倍 p 整,実,倍 整,実,倍 実,倍,複 実,倍,複 実,倍,複 実,倍,複 実,倍,複 実,倍,複 実,倍 実,倍 実,倍,複 実,倍,複 実,倍,複 実,倍,複 実,倍 実,倍 実,倍 実,倍 実,倍 実,倍 実,倍 実,倍 実,倍 実,倍 実,倍 実,倍 実,倍 実,倍 実,倍 実,倍 整,実,倍,複 整,実,倍,複 整数間の剰余 整,実,倍 整,実,倍 小数点以下を切り捨てる 実,倍 実,倍 四捨五入する 実,倍 実,倍 四捨五入して整数にする 実,倍 整 カッコの中の最小値 a a e ln(a) log10 (a) sin(a) cos(a) tan(a) arcsin(a) arccos(a) arctan(a) arctan2(a1/a2) sinh(a) cosh(a) tanh(a) 実数の絶対値 real*4 表 4.2: 組込み関数2.数学関数.(整:整数型,実:実数型,倍:倍精度実数型,複:複素数型) この表の利用法は総称名であり,詳細な個別名については章末の付録 A に示している. 入力例 324789 このとき,キーボードから2つの整数を324789のように入力すると 324 と 89 を入 力したことになる.この例で x は1つの空白を表し ,2x は2つの空白を表す. 文字列 (n 文字) は character*n のように定義し,n 個の文字の入出力には文字変換 an を用いる. キーボード から 8 文字を読むときは次のように書く. character*8 s integer n read(*,100) s,n 28 第4章 編集記述子 iw fw.d ew.d aw nh 1 ` 1 2 nx 実数の固定小数点表示への変換 実数の浮動小数点表示への変換 文字への変換 0 :::h n 表 100 桁数 整数への変換 h h2 : : : hn h h 意味 文字 (列) の表示 文字 (列) の表示 空白 (半角スペース) の表示 w桁 w 桁 (小数点以下 d 桁) w 桁 (小数点以下 d 桁) w桁 n桁 n桁 n桁 組み込み関数 例 i8 (12345) f8.5(-1.23456) e10.3(-0.123e-11) a3 (xy) 5habcde (abcde) `abcde' (abcde) 3x() 4.3: format 文における主な編集記述子 (これら以外についてはマニュアルを参照). format(a8,i4) 入力例 Doshisha12 このとき,キーボードから必要とする文字を (余分なスペースを空けずに ) 入力する.また,書式 なし read 文を用いたときのようにシングルクオテーションマークで囲む必要もない. 4.2.2 write 文と format 文の例 1960年代後半以降に IBM360 を代表とする汎用計算機でラインプリンタ出力をしていたこ ろは,書式付き出力を行うとき,その先頭の1文字は行送り制御のために使っており,2文字目 から以降が出力装置の左端から出力されることになっていた.現在ではラインプ リンタをほとん ど 使用しないので,最初のの1文字から有効な出力を行うが,そのころの名残として最初の一文 字を空白とすることも多い. 書式付き出力の例として次の例題を考える. ; 【例題 4.5 】x = 1 と 3 のときの exp(x) の値をそれぞれ有効数字3桁で出力するプログラムを作れ. 【解答例】実数の浮動小数点表示への変換 e10.3 を用いる. program Example 4_5 real x : 変数 x を実数として定義 x=1.0 : 変数 x に 1.0 を代入 write(*,100) x,exp(x) : 変数 x と exp(x) の値を出力する x=-3.0 write(*,100) x,exp(x) 4.2. 29 書式付き入出力 100 format(1x,'x=',e10.3,4x,'exp(x)=',e10.3) stop end この例で, 「 ' 」で囲んだ文字はそのまま出力される.また,x の値と `exp(x)=' の間に4つのスペー スを入れて,出力を見やすくしている.このような浮動小数点表示をする場合には,0.123 のよう に出力されるが,好みによっては 1.23 と表示して欲しいときもある.そのときは 1pe10.2 とする. 1p は浮動小数点出力において真数部の表示を1桁ずらせる (10 倍する) という意味である. 整数の書式付き出力の例として1から 10 までの和を計算する次の例題を考える. 【例題 4.6 】1 から 10 までの和を計算してそれぞれ2桁で出力するプログラムを作れ. 【解答例】1 から n までの和は公式 n(1 + n)=2 で求める.出力には整数への変換 i2 を用いればよい. program Example 4_6 integer n,m 100 : 変数 n と m を実数として定義 n=10 : 変数 n に 10 を代入 m=n*(n+1)/2 : m を計算 write(*,100) n,m : 変数 n と m の値を出力する format(1x,'n=',i2,4x,'m=',i2) stop end この例では,n と m ど ちらも2桁の値であったが,n が1桁の値のときもある.そのように書式 で指定した桁数より整数変数の値の桁数が小さいときは数字の前にスペースが挿入される. ここで,いくつかの書式付き出力プログラム例と出力例を示しておこう. program ex_format integer ix,iy real x,y character*19 s ix=453 iy=-9876 x=4532.12 y=-0.987209 s='Doshisha University' write(*,100) ix,iy 100 format('ix=',i8,2x,'iy=',i8) write(*,200) x,y 30 第4章 200 組み込み関数 format('x=',f8.2,2x,'y=',e13.6) write(*,300) s 300 format('s=',a20) stop end 出力例 ix=453iy=-9876 x=4532.12y=-0.987209E+00 s=DoshishaUniversity このように出力が得られる.自分の思うように出力をするには Fortran のマニュアルを参考に書 式付き出力の練習をする必要がある. 0 0 NUL 1 2 3 4 5 6 7 BEL 8 BS 9 HT a LF b VT c FF d CR e SO f SI 表 1 2 SP ! " # $ % & ' ( ) * + , 3 0 1 2 3 4 5 6 7 8 9 : " 4 @ A B C D E F G H I J K L ; = M . N / ? O < > 5 P Q R S T U V W X Y Z 6 7 ` p a q b r c s d t e u f v g w h x i y j z k f n l j ] m g ^ n ~ _ o DEL 4.4: ASCII コード 表.主なコード のみを示す. 4.2. 31 書式付き入出力 第4章 演習問題 問題 1. 実数 x をキーボード より読み込み,y =ex の値を求めるプログラムを作れ. 問題 2. 実数 x ( 0) をキーボード より読み込み,その自然対数 log (ln ) と常用対数 log10 お log を出力するプログラムを作れ. よびそれらの比 log10 3 問題 3. = 0, , , 4 2 4 の4つの の値と sin の値の表を作れ.ただし,いずれも数値は小数 > x x x x x x x x 点以下4桁まで表示すること. 問題 4. 問題 5. 問題 6. 実数 x をキーボード より読み込み,小数点以下第3桁目で四捨五入して出力するプログラ ムを作れ.ただし,結果は小数点以下2桁目までのみを出力し,3桁目以降に 0 が出力され ないように注意すること. 実数 x を読み込み,その小数部のみを出力するプログラムを作れ. ローマ字で名前 (たとえば Jiro) を読み込み,'Hello Mr. Jiro' のように出力するプログラ ムを作れ.ただし ,名前の部分を読み込むときは書式付きで読み込み (このとき名前をクォ テーション「 ' 」で囲む必要がない),読み込んだものとおき換えて出力すること. 32 付録 第4章 A 組込み関数表 種類 総称名 最大値 max 最小値 min 平方根 指数 sqrt exp 自然対数 log 常用対数 log10 正弦 sin 余弦 sin 正接 tan 逆正弦 asin 逆余弦 acos 逆正接 atan 個別名 max0 amax1 dmax1 min0 amin1 dmin1 sqrt dsqrt csqrt exp dexp cexp alog dlog clog alog10 dlog10 sin dsin csin cos dcos ccos tan dtan asin dasin acos dacos atan datan 引数の型 関数の型 整数 実数 倍精度 整数 実数 倍精度 整数 実数 倍精度 整数 実数 倍精度 実数 倍精度 複素数 実数 倍精度 複素数 実数 倍精度 複素数 実数 倍精度 複素数 実数 倍精度 複素数 実数 倍精度 複素数 実数 倍精度 実数 倍精度 実数 倍精度 複素数 実数 倍精度 複素数 実数 倍精度 複素数 実数 倍精度 複素数 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 組み込み関数 4.2. 33 書式付き入出力 種類 総称名 双曲線正弦 sinh 双曲線余弦 cosh 双曲線正接 tanh 絶対値 abs 剰余 mod 切捨て aint 四捨五入 anint 四捨五入整数化 nint 型変換 int 個別名 sinh dsinh cosh dcosh tanh dtanh iabs abs dabs cabs mod amod dmod aint dint anint dnint nint idnint int i%x idint 引数の型 関数の型 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 整数 実数 倍精度 複素数 整数 実数 倍精度 複素数 整数 実数 倍精度 整数 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 実数 倍精度 整数 実数 倍精度 整数 整数 整数 35 5 第 章 手続き型プログラミングと 構造 PAD 図・逐次 Java や C++ などのコンピュータ言語がオブジェクト指向型言語と呼ばれるのに対して,Fortran や C などは手続き型言語と呼ばれる.手続き型言語を用いるプログラミングにおいてはアルゴ リ ズムが重要であり,アルゴ リズムには3つの基本構造がある.それらは逐次構造,選択構造および 反復構造である.プログラミングの構造はなるべく逐次構造となるように設計すべきである.そ のためには構造化プログラミングという方法を用いる.構造化プログラミングを行うためには関 数とサブルーティンを用いる.この章ではアルゴ リズムの作成,PAD 図と逐次構造について学ぶ. 5.1 アルゴリズムの作成ー概略設計から詳細設計へ( 段階的詳細化) コンピュータが普及し始めた 1970 年代まで,プログラム設計にはフローチャートが用いられて きた.その後,フローチャートではアルゴ リズムの正確さを確かめるときに,すべての場合分け のチェックをする際の場合漏れを生じる可能性が指摘され,現在では PAD 図が用いられている. PAD 図 (Problem Analysis Diagram, 問題分析図)とは,アルゴ リズムをその基本3構造である 処理 (連接)・反復・選択に分解し,図形に表現することにより,問題分析やコーディングおよびプ ログラムの可読性に効果を発揮する開発技法である.PAD 図の表記には図 5.1 に示すような基本 的な箱 (処理箱,入力箱,出力箱,選択箱,反復箱)を用いる. 処理箱 入力箱 選択箱 図 出力箱 反復箱 5.1: PAD 図.基本的な箱 36 第5章 手続き型プログラミングと PAD 図・逐次構造 処理箱には計算式などの処理内容を記述し,入力箱には入力する変数,出力箱には出力する変数 や文字列を記入する.選択箱には分岐する条件 (論理式) を記入し,論理式が真 (True) と偽 (False) の場合の処理箱につなぐ.反復箱には反復変数の初期値と最終値および増分または論理式を記述 する. 5.2 3 つの基本的アルゴリズム アルゴ リズムにはできる限り逐次構造を用いるように努力する必要があるが,これだけではす べてのアルゴ リズムを記述することが不可能なので選択構造と反復構造も用いる. 5.2.1 逐次構造 (Sequence,連接) 逐次構造はトップダウン構造とも呼び,処理 A,B,C,: : : のように順次処理を行う最も単純 なアルゴ リズムである.逐次構造は図 5.2 のように表される. A B C 図 5.2.2 選択構造 5.2: 逐次構造の PAD 図 (Selection) 選択構造は与えられた条件によって処理する内容を分けるのに用いられる.処理を2つの場合 に分けることが多いが3つやそれ以上に分けることもある.選択箱には分岐する条件 p を記述し, 条件によりそれぞれ行う処理を記述する処理箱をつなげる.最も簡単な選択構造では p は論理式 であり,論理式が真 (True) の場合と偽の場合に行う処理を記述する. 5.3. 呼出箱と関数 (function)・サブルーティン (subroutine) True A False B p 図 5.2.3 5.3: 37 選択構造の PAD 図 反復構造 (Repetition,Iteration) コンピュータでの情報処理には同じ処理をパラメータを変えながら繰り返す処理が多い.この とき反復構造を用いる.反復箱には条件 p を記述し,その条件が満たされている場合に繰り返し 行う処理を処理箱として繋げる.最も簡単な繰り返し構造は,変数 i にその初期値を与えてある処 理を1度行うと i に増分を加え (引き),i があらかじめ指定された最終値になるまで繰り返すアル ゴ リズムである. p 図 5.3 呼出箱と関数 A 5.4: 反復構造の PAD 図 (function)・サブルーティン (subroutine) いくつかの処理や選択構造・反復構造などをまとめて関数やサブルーティンとすることにより, メインのアルゴ リズムを逐次構造として書き表すことができる.関数やサブルーティンは次節で 説明する概略設計と詳細設計の方法の実現手段でもある.サブルーティンの PAD 図は図 5.5 のよ うに表す. 5.4 概略 PAD 図と詳細 PAD 図 大規模プログラムを書くときには,アルゴ リズムが煩雑となることが多いので最初から詳しい PAD 図を作成することはせず,概略 PAD 図を書き,概略 PAD 図のそれぞれの部分を詳細 PAD 図で表して全体のアルゴ リズムを構成する.概略 PAD 図を書くときは,全体のアルゴ リズムのう ちであるまとまった処理を参照箱を用いて記述し ,参照箱のそれぞれについては定義箱の右側に 38 第5章 手続き型プログラミングと PAD 図・逐次構造 S Subroutine S A 図 5.5: サブルーティン (または関数) を表す PAD 図 詳細 PAD 図として記述する (図 5.6).すなわち,( メインルーティンで) 参照箱に出会ったら,そ の定義箱にある処理を実行することになる. A R R 参照箱 定義箱 B C 図 5.5 5.6: 概略 PAD 図と詳細 PAD 図 逐次構造プログラミング 問題を解析し ,その問題を解決するためにコンピュータプログラミングを始めるときにはなる べくアルゴ リズムが逐次構造となるように努める必要がある.この節ではいくつかの例を通して 逐次構造プログラミングの方法を学ぶことにしよう.いうまでもないが,コンピュータプログラ ミングによりある問題を解決しようとする場合にはプログラミングの知識や技術だけでなくその 問題についての深い洞察力が最も重要である.次の例題を考える. 【例題 5.1 】3角形の2辺の長さ a, 算するプログラムを作れ. b とその狭角 t ( ) をキーボードから読み込み,その面積 s を計 5.5. 39 逐次構造プログラミング 【解答例】2辺 a, b と狭角 t °] が与えられたとき,その3角形の面積 s は s = ab sin(t) で与えら れる.ただし,Fortran の組み込み関数 sin(t) の引数 t はラディアンで与える必要があることに注 意する. program Example 5_1 real pi,a,b,c,s,t : 変数 a,b,c,s,t を実数として定義 pi=atan(1.0)*4.0 : pi の値を求めておく write(*,*) 'Input a, b' : 2辺の長さ a,b を読み込むためのプロンプト read(*,*) a,b : 2辺の長さ a,b を読み込む write(*,*) 'Input t' : 狭角 t を読み込むためのプロンプト read(*,*) t : 狭角 t を読み込む t=pi*t/180.0 : 角 t を °からラデ ィアンに変換 s=a*b*sin(t)/2.0 : 面積 s を計算 write(*,*) 'area= ',s : 面積 s を出力 stop end p ' 1:732 であることがわかっているので,a = b = 2:0, たとえば,1辺が 2 の正三角形の面積は 3 t = 60( ) と入力すると,計算結果が area= 1.732 とほぼ正しい値が得られる. 一見すると選択構造 (if { then { else) を用いないと書けないと思われる問題もよく問題を吟味 すれば逐次構造のみで記述できる場合がある.そのようなときは逐次構造のみを用いてプログラ ミングすることが望ましい. 【例題 5.2 】キーボード より整数 n を読み込みその整数の桁数を出力するプログラムを作れ. 【解答例】整数 n の常用対数 log10 n を小数点以下切り捨てて 1 を加えた数がその整数の桁数であ ることを用いる. program Example 5_2 integer n : 変数 n を整数として定義 real x : 変数 x を実数として定義 write(*,*) 'Input an integer' : 変数 n を読み込むためのプロンプト read(*,*) n : 変数 n を読み込む x=float(n) : 変数 n を実数に変換 x=alog10(x) : x の常用対数を求め,それを新たに x とする n=int(x)+1 : 整数部の取り出し,1 を加え , 文字数を n とおく 40 第5章 write(*,*) 'figures= ',n 手続き型プログラミングと PAD 図・逐次構造 : 文字数 n を出力 stop end この例のように,選択構造を避けてプログラミングをするとアルゴ リズムが単純になり,たとえ 大きなプログラムを書くときにもデバッグ (虫取り,プログラムの誤りを見つけて訂正すること) が容易になる. 次の例題も少し考えると選択構造をさけて簡単にプログラミングができる問題である. 【例題 5.3 】小数点以下 4 桁まで含む数をキーボード より読み込み,小数点以下 2 桁を四捨五入し 小数点以下 1 桁数の数を出力するプログラムを作れ. 【解答例】四捨五入のための Fortran の組み込み関数 anint(x) は小数点以下 1 桁目を四捨五入する ため,キーボード より読み込んだ実数 x を 10 倍して四捨五入した後 10 で割ることにより小数点 以下 2 桁を四捨五入した数を得る. program Example 5_3 : 変数 x を実数として定義 real x 100 write(*,*) 'Input a number' : 変数 x を読み込むためのプロンプト read(*,*) x : 変数 x を読み込む x=10.0*x : 変数 x を 10 倍する x=anint(x) : x の小数点以下を四捨五入する x=x/10.0 : 変数 x を 10 で割る write(*,100) x : 数 x を出力 format(1x,'rounded number= ',f10.1): 数 x を小数点以下 1 桁で出力する書式 stop end このプログラムで用いた組み込み関数 anint(x) については表 4.2 を参照のこと. 5.5. 41 逐次構造プログラミング 第5章 演習問題 問題 1. xy 平面上の2点の座標 (x1 y1 ) と (x2 y2 ) が与えられたとき,それらの2点を結ぶ線分の 長さ ` を計算するプログラムを作れ. 問題 2. xy 平面上の3点の座標 (x1 y1 ),(x2 y2 ),(x3 y3 ) が与えられたとき,それらの3点を頂 点とする三角形の重心と面積を計算するプログラムを作れ. 問題 3. 実数 x を読み込み,x > 0 なら 1,x < 0 なら 1 を出力するプログラムを作れ.ただし , x = 0 の場合は考えない.また,次章で説明する選択構造 (if { then { else) を用いないこと. 問題 4. ラジウム 226 88 Ra の半減期は 1602 年である,x g] のラジウムが y g] (y < x) になるまでの 年数を計算して出力するプログラムを作れ.ただし ,1年以下の期間は切り捨てとする. 問題 5. 長さ ` m] の軽い糸に結ばれた質点が単振動をするときの周期 T を計算するプログラムを 作れ.ただし,重力加速度を g = 9:8 m/s2 とする. ; 43 第 6 章 選択構造 選択構造はある定められた条件により異なる処理を行うために用いる.条件の判断には論理式を 用いる.論理式が真 (True) のときと偽 (False) のときにそれぞれの処理を行う.昔,アルゴ リズ ムをフローチャートで表していたときは,選択構造は処理の流れを分岐させるために用いられて いた.この章では論理式の作り方と選択構造プログラミングの基本について学ぶ. 6.1 論理式と関係演算子 論理式というのは真 (.true., T と略す) または偽 (.false., F と略す) の値をもつ式であり,多く の場合その値は関係演算子 (.eq., .ne., .lt., .le., .gt., .ge.) を用いて,数値や文字 (列) の大小 (順 序) 関係によって決められる.たとえば,変数 p を論理変数と定義して,整数 i が 0 であるとき真 (.true.),0 でないとき偽 (.false.) とするプログラムは次のように書ける. プログラム例 6.1 programlogics logicalp:p を論理変数として定義 integeri:i を整数として定義 i=1 p=i.eq.0:i が 0 に等しいとき真,そうでないと偽 write(*,*)p:p は偽 (F) stop end 6 このプログラム例では i = 0 なので,結果は `F' と出力される.ここで,`.eq. ' はこの記号の左 右にある式または変数の値が等しいかど うか調べる関係演算子であり,i と 0 が等しいとき真,等 しくないとき偽を与える.これ以外にも,表 6.1 に示す関係演算子がある. 算術演算子 (文字演算を含む) と関係演算子が混合して用いられているときには,算術演算子が 優先する.たとえば, i+5.eq.j は (i+5).eq.j を意味する. 論理演算子を使うと論理変数または論理式の間の演算を行うことができ,いくつかの条件があ るときにそれらの条件が同時に満たされているかど うかを調べることができる.論理演算子には表 6.2 に示されるように (.not., .and., .or., .eqv., .neqv.) がある.この中で.not. のみは単項演算子で あり,その他は2項演算子である.p と q が論理変数で定義されているとする.そのとき,.not.p は p が真のとき偽,p が偽のとき真を与える.また,p.and.q は p と q が共に真のときのみ真であ 44 第6章 関係演算子 .eq. .ne. .lt. .le. .gt. .ge. 意味 数学記号 = 6 = 等しい 等しくない < 小さい 小さいか等しい 大きい 大きいか等しい 表 6.1: > 選択構造 f90 での表し方 == /= < <= > >= 関係演算子. り,それ以外はすべて偽となる.演算子 .xor. は排他的論理和 (Exclusive Or) と呼ばれ,p と q の論理値が異なるときのみ真であり,同じとき偽となる.したがって,p.xor.q は p.neqv.q と同 じ論理値となる.これらの関係演算子を用いたときの真理値表は表 6.3 のようになる.次のプログ ラム例では p が真,q が偽であり p.and.q は偽となる. プログラム例 6.2 programlogics1 logicalp,q integeri,j i=0:p と q を論理変数として定義 j=1 p=i.eq.0:p は真 (T) q=j.eq.5:q は偽 (F) write(*,*)p.and.q:p.and.q は偽 (F) stop end 関係演算子と論理演算子が混合して用いられているときには,関係演算子が優先的に評価され た後,論理演算子が評価される.たとえば,i.gt.2 .and. j.le.3 は (i.gt.2) .and. (j.le.3) と同じであ る.また,論理演算子が2つ以上あるときには,.not., .and., .or., .neqv., .xor., .eqv. の順に優先 度が高い. 6.2 論理 if 文 論理 if 文は最も単純な選択構造であり,論理式 の形式は if ( p ) A p が真であるときに実行文 A を実行する.そ 6.2. 論理 if 文 45 論理演算子 意味 (:) 論理積 (^) 論理和 (_) 論理等価 () 論理非等価 (6=) .not. .and. .or. .eqv. .neqv. .xor. 表 p T T F F 否定 排他的論理和 6.2: 論理演算子. q .not.p p.and.q p.or.q p.eqv.q p.neqv.q T F T T T F F F F T F T T T F T F T F T F F T F 表 6.3: 真理値表. ここで,実行文 A は1つの処理に限る.このときの 論理 if 文を使う例として次の例題を考える. PAD 図は図 6.1 で表される. 【例題 6.1 】1つの整数をキーボード より読み込み,その整数が 0 のときのみ'The 表示するプログラムを作れ. 【解答例】関係演算子として .eq. を用いることにする. value is zero' と program Example6_1 integer i : 変数 i を整数として定義 write(*,*) 'Input i' : 整数 i の入力を促す read(*,*) i : 整数 i を読み込む if (i.eq.0) write(*,*) 'The value is zero' : i が 0 ならメッセージ出力 stop end このプログラムをコンパイルして実行すると,キーボードから入力された整数が 0 のときには 'The value is zero' と画面に表示されるが,それ以外の整数のときはなにも表示されない. 次の例題も論理 if 文を用いる例である. 46 第6章 True p 図 6.1: 選択構造 (論理 選択構造 A if 文) の PAD 図 【例題 6.2 】4 桁の整数 n をキーボード より読み込み,それが 1000 の倍数なら 'multiple of 1000' と 出力し ,そうでなければなにも出力しないプログラムを作れ. 【解答例】この例題では整数 n が 1000 の倍数でないときはなにもしないようにすればいいので,n が 1000 の倍数のときだけ動作をするプログラムを作る. program Example 6_2 integer n : 変数 n を整数として定義 write(*,*) 'Input an integer' : 変数 n を読み込むためのプロンプト read(*,*) n : 変数 n を読み込む if(n/1000*1000.eq.n) write(*,*) n,' is a multiple of 1000' : 変数 n が 1000 の倍数ならば 'multiple of 1000' を出力 stop end この例題では,整数 n が 1000 の倍数であれば n/1000*1000 が n となり,そうでないときには n とは異なる値となることを用いている.このテクニックはよく使われる.n/1000*1000.eq.n は mod(n, 1000).eq.0 と同じ意味である. 6.3 ブロック if 文 ブロック if 文は end if や else あるいは else if 文などと共に用いてある指定した論理式が真 (.true) のときと偽 (.false.) のときに異なる処理を行う目的で使用する.その最も単純な形は if ( p ) then A else B end if のように表すことができる.ここで,処理 A と処理 B は論理 if 文とは異なり,複数個の処理 (複 文) であってもよい.このときの PAD 図は図 6.2 のように表される. 6.3. ブロック if 文 47 True A False B p 図 6.2: 選択構造 (最も簡単なブロック 次の例題は選択構造 (ブロック if 文) の PAD 図 if 文) を用いる例題である. 【例題 6.3 】キーボード から実数 x を入力し,x が正または 0 のときその平方根を出力し ,負のと き平方根が複素数になるというメッセージを出力するプログラムを作れ. program Example 6_3 : 変数 x を実数として定義 real x write(*,*) 'Input a real number:' : 実数 x の入力を促す read(*,*) x : 実数 x を読み込む if (x.ge.0.0) then write(*,*) sqrt(x) : x>=0 なら平方根を計算して出力 else write(*,*) 'square root of x is complex' : x<0 のとき複素数となる end if stop end ブロック if 文では2つの論理式を使って図 6.3 の PAD 図に示されるように3つの場合に分けて 処理をすることも可能である. 【 例題 6.4 】キーボード より整数 n を読み込み,その整数が正なら 'positive',0 なら 'zero',負な ら 'negative' と出力するプログラムを作れ. 【解答例】読み込んだ整数 n が正 (n > 0) でないときは,n = 0 の場合と n < 0 の場合があるので 選択構造 (if{then{else) を入れ子にする必要がある. program Example6_4 integer n : 変数 n を整数として定義 write(*,*) 'Input n' : 整数 n を読み込むためのプロンプト read(*,*) n : n を読み込む if (n.gt.0) then : n が正であるかど うか判定 write(*,*) n, ' is positive' : n が正であれば positive と出力 48 第6章 True A p False 図 6.3: 選択構造 True B False C q 選択構造 (3つの場合分け ) の PAD 図 else if (n.eq.0) then write(*,*) n, ' is zero' : n が 0 であれば zero と出力 else write(*,*) n, ' is negative' : n が正でも 0 でもなければ negative と出力 end if stop end この例のように,if および else if などが多重構造をとると,どの else がどの if に対応している のか明確にしておく必要がある.このとき,ある else が対応する if 文はその else にもっとも近い ところにあり,しかも他の else がまだ対応していないものである. コンピュータプログラミングではプログラミングの能力よりも,解決しようとする問題につい ての理解の方が重要であることが多い.次の例題も基礎的な数学の力がないとできない例である. 【例題 6.5 】方程式 ax = b を解くプログラムを作れ.ただし a, b はキーボード から入力するもの とする. 【解答例】この問題の解答は,a = 0 のとき x = b=a,a = b = 0 のとき x は任意の実数,a = 0 で b = 0 のとき解なしとなる. 6 6 program Example6_5 real a,b,x write(*,*) 'Input a and b' : 変数 a,b,x を実数として定義 : 実数 a, b を読み込むためのプロンプト 6.3. ブロック if 文 49 read(*,*) a,b : a,b を読み込む if (a.ne.0.0) then : a が 0 でないことを確認 write(*,*) 'x= ',b/a : a が 0 でないとき x=b/a else if (b.eq.0.0) then write(*,*) 'x is arbitrary' : a と b が 0 であればすべての実数が解 else write(*,*) 'no solution' : a が 0 で b が 0 でなければ解なし end if stop end ブロック if 文と else if 文を繰り返し用いることによって3つあるいはそれ以上の場合分けをす ることも可能である. 【例題 6.6 】キーボード より 1 ∼ 5 の整数を読み込み、その整数の英単語を出力するプログラムを 作成せよ.例えば 1 のときは `one' と出力すること. 【解答例】 program Example6_6 integer n : 変数 n を整数として定義 write(*,*) 'Input n' : 整数 n を読み込むためのプロンプト read(*,*) n : n を読み込む if (n.eq.1) then : n が 1 であるかど うか判定 write(*,*) n, ' is one' : n が 1 であれば 1 is one と出力 else if (n.eq.2) then write(*,*) n, ' is two' : n が 2 であれば two と出力 else if (n.eq.3) then write(*,*) n, ' is three' : n が 3 であれば three と出力 else if (n.eq.4) then write(*,*) n, ' is four' : n が 4 であれば four と出力 else if (n.eq.5) then write(*,*) n, ' is five' : n が 5 であれば five と出力 else write(*,*) n, ' is not in the range of 1, 5]' : n は 1 から 5 以外 end if stop end 50 第6章 選択構造 Fortran 90 では,このように多くの場合に分ける目的で select case 文が定義されている. ここでは説明しなかったが,Fortran 77 には算術 if 文がある.算術 if 文は次の節で説明する go to 文と同様にプログラムの流れをどこへでも飛ばすことができるので,極力使わないように努 めること. 6.4 ( ) go to 文 ラベル 文番号 と プログラムの流れを任意の場所に飛ばすことができる命令が go to 文である.この命令を使う とデバッグ (虫取り) が困難になることが多いので,なるべく使わないようにする必要がある.た だし ,非常に短いプログラムなどや,デバッグ中に誤りと思われる部分の処理を飛ばしてしまっ てそれ以外の部分のみを実行してチェックする目的には使うこともある.go to 文はラベル (文番 号) と併用する.ラベルには数字のみを使うことができ,プログラムの場所を特定するために用い る.たとえば,処理 A を無条件に繰り返すときは 100 continue : 文番号 100 のラベル A go to 100 : 文番号 100 へ飛ぶ のようになる.ここで,continue 文はなにもしない実行文である.文番号と共に用いてプ ログ ラムの場所を示す目的で用いられる (文番号を他の実行文につけてラベルとすることも可能だが, continue 文につけておくとわかりやすい.ただし ,なにもしない実行文があるとそれだけ冗長な プログラムになる).多くの場合,このプログラムを実行するといつまでもプログラムの処理が終 わらないので `^c' (コントロールキーを押しながら文字 c を押すコード ) をキーボードから入力し てプログラムを強制終了することになる.したがって,万一,go to 文を使う場合でも,対応する 文番号と go to 文の間にはプログラムの停止条件 (p) を挿入しておくことが望ましい. 100 continue if (p) go to 99 : 文番号 100 のラベル : もし p が真ならば文番号 99 へ飛ぶ A go to 100 99 continue たとえば,整数変数 i を初期に 0 としておき,これに 1 づつ加え続けると i は 1 づつ増加し続 けるはずなのに,減少することがある.このときの i の値を調べるプログラムは次のようになる. プログラム例 6.3 programgo_to integer*2i,j:i,jは2バイトの整数 6.4. ラベル (文番号) と go to 文 51 i=0 100continue j=i+1:j=i+1 if(j.lt.i)goto99 i=j:i=j write(*,*)i goto100 99stop end このプログラムを実行すると i = 32767 となった後にプログラムが終了する.この理由について 考えよ.ここでは,i と j を2バイトの整数としている.特に指定しなければ整数は4バイトにな り,このプログラムが終了するまでにかなりの長時間を要する. 52 第6章 選択構造 第6章 演習問題 問題 1. キーボード より整数 i を読み込み,その整数 i が偶数なら `even',奇数なら `odd' と出力 するプログラムを作れ. 問題 2. 3 つの実数 a, b, c をキーボードから読み込み,方程式 ax2 プログラムを作れ. 問題 3. キーボード より整数 n を読み込み,その整数を 3 で割って余りが 0 なら `divisible',1 な ら `the remainder = 1',2 なら `the remainder= 2' と出力するプログラムを作れ. 問題 4. キーボード より整数 i を読み込み,その整数が何桁の数であるかを出力するプログラムを 作れ.ただし,整数 i は 0 ≦ i ≦ 9999 であるとする.また,整数 i を正の数に限るときには if ∼ then ∼ else 文を使わずにプログラミングできる.そのプログラムの一例を示せ. 問題 5. 実数 x をキーボード より読み込み,その平方根を求めて出力し,さらにその平方根を求め て出力することを 10 回繰り返すプログラムを作れ. 問題 6. 画面いっぱいに 0 から 9 までの数字が出力され続けるプログラムを作れ.ただし数字の並 び方は任意とするが,途中や行末にスペースがあってはならない.go to 文を使うこと. + bx + c = 0 の解 x を求める 53 第 7 章 反復構造 反復構造はあるパラメータ (変数あるいはデータ) を変えながら同じ処理を繰り返し行うためのア ルゴ リズムである.繰り返し 処理はコンピュータがもっとも得意とする処理であり,プログラミ ングにおいてもこの構造を頻用する. p 図 7.1 反復構造と A 7.1: 反復構造の PAD 図 do 文 および continue 文 一般に,プ ログラミングにおいて反復構造は PAD 図では図 7.1 のように表され る.しかし , Fortran 77 では,反復構造を作る目的には繰り返し変数を使った do 文しか定義されていないの でコンピュータの状態や周辺機器およびネットワークの状態を調べて柔軟にプログラミングを行 うことが難しい.Fortran 77 において,反復構造は繰り返し変数を用いて次のように記述する. domi=i1,i2,i3] A mcontinue ここで,i,i1, i2, i3 は整数型あるいは実数型または倍精度実数型の変数である.i は繰り返し変 数,i1 は初期値,i2 は終値,i3 は増分である.i3 は省略することも可能 (省略することが多い) で, そのときは i3=1 と見なされる (デフォルト値).m は文番号をよばれるプログラムのある特定の 文を示すラベルである.do 文においては文番号 m がつけられている文までの処理を繰り返し行 う.このとき,しばしば continue 文 に文番号 m をつけることがある.前章の終わりに説明した ように continue 文は実行文ではあるが何もしない実行文であり,プログラムの実行を継続する目 的で用いられる (主にはラベルをつけるために用いる文であり,プログラムを停止する目的で用い られる stop 文と対比することも可能).i3> 0 のときは,i1 i2 ならば処理 A を行った後,i に 54 第7章 反復構造 増分 i3 を加え,それでも i i2 ならば処理 A を行うという操作を i > i2 となるまで繰り返し行 う.i3< 0 のときは,i1 i2 ならば処理 A を行った後,i に増分 i3 を加え (i3<0 なので実際は引 き算),それでも i i2 ならば処理 A を行うという操作を i < i2 となるまで繰り返し行う. たとえば,次のプログラム例では,i=1 から n=5 まで do loop に挟まれた部分の処理 (この例 では write(*,*) i ) を繰り返して行う. プログラム例 7.1 programdo_loop1 integeri,n n=5 do100i=1,n write(*,*)i 100continue stop end このプログラムの実行結果は次のようになる. 1 2 3 4 5 繰り返し変数 i の値を初期に 9 とおき,2 づつ減少しながら て do loop に挟まれた部分の処理を行う例は次のようになる. プログラム例 7.2 programdo_loop1 integeri,n n=9 do100i=9,0,-2 write(*,*)i 100continue stop end このプログラムの実行結果は次のようになる. 0 未満となるまでおきに繰り返し 7.1. 反復構造と do 文 および continue 文 55 9 7 5 3 1 繰り返し変数 i には整数以外の変数も用いることができるが,実数などは必ず計算誤差 (丸め誤 差など ) を含むため,プログラマが意図しないときに繰り返しの終了条件が終了する危険があるの で通常は必ず整数を用いるように努める. 反復構造を用いた簡単な例でしかも最も重要な例を考えよう.いくつかの数 (整数あるいは実数) の和を求める問題である.この問題では,和を求めるための変数 (次の例題では m ) を用意し,そ の変数を初期化し (0 とおき),その変数に必要なだけの数を加えていく. 【例題 7.1 】1 から 100 までの整数の和を求めるプログラムを作れ. 【解答例】和を表す変数 m を定義し ,初期に 0 としておく.この m に整数 1 から 100 までを加え ることにより,1 から 100 までの和を求める. program Example7_1 : 変数 i,m を整数として定義 integer i,m m=0 : m に 0 を代入 do 100 i=1, 100 : 整数 i を作業変数として 1 から 100 まで繰り返す m=m+i 100 : m に i を加えた数を新たに m とおく continue : 100 はラベル (文番号) write(*,*) 'the sum from 1 to 100 is ',m : 和 m を出力 stop end 計算の結果 m=5050 と得られる.これは 1 から n までの和 m の公式 m = n(n + 1)=2 による値と 一致している. 計算機内部では有限桁の精度で計算を行うため,計算機を用いてたくさんの数を加えるときに は数値誤差の蓄積に注意する必要がある.一般にいくつかの数を加えていくときには小さい数か ら順に加えていく方が,大きい数から加えるよりも精度が良い.そのような問題を考えてみよう. 1 【例題 7.2 】1000 k=1 k3 の値を求めるプログラムを作れ. 【解答例】和を表す変数 s を定義し ,初期に 0.0 としておく.この s に 1 加えることにより,1000 k=1 k3 を求める. program Example7_2 1/10003 から 1/13 までを 56 第7章 real s : 変数 s を実数として定義 integer k : 変数 k を整数として定義 s=0.0 : 和を s とする do 100 k=10000, 1, -1 : 変数 i を 10000 から 1 までとする 反復構造 s=s+1.0/real(k)**3 100 continue write(*,*) 'the sum from k=1 to 1000 of 1/k^3 is ',s stop end 計算の結果 s=1.202057 と得られる.しかし,do loop を 1 から 10000 とすれば,s=1.202051 と なる.正確な値は s= 1.202056898 なので,やはり小さい数から順に大きい数を加えていく方が精 度が良いことが確かめられる.と一致している. 変数 x の関数 f (x) の値をいくつかの変数値について繰り返し計算して,その最大値を見積もる 例を考える. ; 【 例題 7.3 】関数 f (x) = x exp( x) の値を x = 0 から 0:1 おきに x = 2 まで計算して,その関数 f (x) の最大値を与える x のおよその値とそのときの f (x) の値を求めよ. 【解答例】x = 0 から 0:1 きざみに x = 2 まで繰り返し f (x) の値を計算し ,出力する. program Example7_3 integer i : 変数 i を整数として定義 real x,dx,f : 変数 x,dx,f を実数として定義 dx=0.1 : きざみ幅 dx を 0.1 とする do 100 i=0,20 : i を作業変数として 1 から 100 まで繰り返す x=dx*float(i) : x の値を計算 f=x*exp(-x) : f の値を計算 write(*,*) x,f 100 : x,f の値を出力 continue stop end 計算の結果より,f (x) の最大値は x = 1 のときおよそ f (x) = 0:368 であることがわかる.しかし, せっかくコンピュータを使っているのだからもう少し自動的に最大値を探す方法を考えて,つぎ のようなプログラムを作る.この例題も重要で基本的なアルゴ リズムを含んでいる. program Example7_3a integer i : 変数 i を整数として定義 7.1. 反復構造と do 文 および continue 文 real x,dx,f,xm,fm 57 : 変数 x,dx,f を実数として定義 xm=0.0 : fm=xm*exp(-xm) : dx=0.1 : do 100 i=0,20 : x=dx*float(i) : f=x*exp(-x) : if (f>fm) then : xm=x : ただし ,xm は f が最大となる x fm はそのときの f の値 xm は x=0.0 としておく そのときの f きざみ幅 dx を 0.1 とする i を作業変数として 1 から 100 まで繰り返す x の値を計算 f の値を計算 f が fm より大きければ x を xm, f を fm とおく fm=f end if write(*,*) x,f 100 : x,f の値を出力 continue write(*,*) 'max. of f ',fm, 'at ',xm :最大値を出力 stop end この例では正確な最大点 x = 1 が求められたが,このような方法では最大点と最大値の近似的な 値しか求められないことに注意する必要がある.ただし,計算のきざみ幅 dx を小さくすればいく らでも正確な値に近づくことが可能である. Fortran 90 では,do 文で文番号を省略することができる.そのときには,end do までが繰り 返し処理の対象となる.また,do while 文も用意されている. 58 第7章 反復構造 第7章 演習問題 問題 1. 1 から n までの整数の逆数の和を求めるプログラムを作れ. 問題 2. do 文を用いて文字 `A' から `Z' までを出力するプログラムを作れ. 問題 3. n 個のデータ (実数) をキーボード から読み込み,それらの平均と2乗平均を求めるプロ グラムを作れ. 問題 4. 整数 n(n > 0) をキーボード から読み込み,n! を求めるプログラムを作れ.このとき,ど の程度大きな n の値まで n! を正しく計算できるか確かめよ. 問題 5. キーボード より整数 N を読み込み,近似式 e = グラムを作れ. 問題 6. 問題 7. 問題 8. N X 1 k=0 k! を用いて e の近似値を求めるプロ n 個の正負の混じったデータをキーボードから読み込み,非負のデータの数とその平均, 負のデータの数とその平均をそれぞれ求めるプログラムを作れ.ただし,データの数が 0 個 のときは平均の代わりに `****' と出力するように作ること. 漸化式 xn+1 p = 1 2 xn + x1n を満たす数列 xn は 2 を計算するプログラムを作れ. p 2 に収束する.この漸化式を用いて, 2つの整数 a と b を読み込み,その最大公約数 (GCM,Greatest Common Mesuare) と最 小公倍数 (LCM,lowest common multiple) を求めるプログラムを作れ.ヒント:Gを最大 公約数とすれば,a と b の2数の差もGで割り切れることを利用せよ. 59 第 8 章 関数 第4章では組み込み関数を学んだが,Fortran (Fortan 77 および Fortran 90) にはユーザーが必要 に応じて定義できる関数がある.ユーザーが定義する関数には文関数と関数副プログラムがある. このうち,関数プログラムと次章で説明するサブルーティン副プログラムはプログラムの構造化 を実現する手段での1つである.この章では文関数と関数副プログラムの定義の仕方と呼び出す 方法について学ぶ. 8.1 文関数 文関数は1つの式 (文) で表現できる関数であり,次のように定義される. f(x1, x2, ...) = g ここで,f は関数名であり,英文字から始まる6文字以内の英数字である.かっこ `( )' の中の 変数 x1, x2 などは引数と呼ばれる.文関数定義文の右辺には具体的な定義式を書く.文関数には 1つ以上の引数が必要である.Fortran を初めとするプログラミング言語ではこのように関数定義 に現れる引数を仮引数と呼び,その関数を呼び出して使うときの引数を実引数という.すなわち, 変数 x1, x2 などは仮引数である.関数呼び出し時に使う実変数の名前は必ずしも仮引数の名前と 同じでなくてもよい.ただし,変数の型と並び (順番) は一致している必要がある.特に,文関数 が2つ以上の仮引数をもつとき,その仮引数の名前は重要でなく,変数の並びが大切である.文 関数は非実行文であり,宣言文の後で最初の実行文の前に定義しなければならない. たとえば,区間 x = 0 1] を20等分して,それぞれの分割点 xi = i=20 における関数 f (x) = x exp( x) の値を表にするプログラムは次のようになる. ; プログラム例 8.1 programfunc1 realx,y,f integeri,n f(x)=x*exp(-x) n=20 do100i=0,n 60 第8章 関数 y=real(i)/real(n) write(*,*)'x=',y,'f(x)=',f(y) 100continue stop end このプログラム例のように何度も同じ計算を繰り返す必要があるときには,文関数としておくと 便利である. 文関数の具体例として2次式 f2 (x) = x2 + 2x + 3 を定義し ,いくつかの x の値についてその 関数値を計算するプログラムを作ってみよう. 【例題 8.1 】実数 x を引数とする関数 f2 (x) = x2 + 2x + 3 を定義して,x = 1 から 0:1 きざみに x = 1 まで計算するプログラムを作れ. ; 【解答例】文関数 f2 (x) を定義する. program Example8_1 100 integer i : 変数 i を整数として定義 real x,dx,f2 : 変数 x,dx と関数 f2 を実数として定義 f2(x)= x**2+2.0*x+3.0 : 文関数 f2 の定義 dx=0.1 : きざみ幅 dx を 0.1 とする do 100 i=-10,10 : i を作業変数として -10 から 10 まで繰り返す x=dx*float(i) : x の値を計算 write(*,*) x,f2(x) : x,f2(x) の値を出力 continue stop end 8.2 関数副プログラム プログラムが大きくなると誤りを見つけたり,保守したりするのが難しくなる.それに対する 解決策はプログラムの部品化である.共通の処理を部品化し,見通しをよくすることによって,効 率よくプログラムを作成できるようになる.その方法として Fortran では,関数とサブルーチン ( 関数副プログラム)を使ってプログラムの部品化を行う.その PAD 図は図 8.1 で表される. 関数副プログラムは次のように定義する. real function f (x1, x2, ...) real x1, x2, ... 8.2. 61 関数副プログラム S function f A 図 8.1: 関数副プログラムを表す PAD 図 A f=g return end 関数プログラムには冒頭に型名をつける.そのほかは文関数とほぼ同様に定義される.すなわち, f は関数名で,英文字から始まる6文字以内の英数字である.かっこ `( )' の中の変数 x1, x2 な どは仮引数である.文関数とは異なり,複数の処理を行うことができる.また,必ずしも仮引数 を必要としない.関数を呼び出す方法は主に式への代入である.関数を呼び出して使うときの実 引数には直接数字を代入することも可能であるが,変数を代入する習慣をつけておくようにしよ う.その理由の説明は少し難しくなるが,Fortran (Fortran 77 および Fortran 90) で関数を呼び 出すときには,引数をアドレスで渡すからである.引数の値だけを渡すのではなく,アドレスで 渡す場合にはその引数の値を変更することが可能となり,定数を変更すると矛盾が生じるので実 引き数には極力変数を用いる.逆に,引数をアドレスで渡すことを積極的に使って,関数を呼び 出すことによって1つ以上の値を受け取ることも可能となる.ここでも,関数呼び出し時の実変 数の名前は必ずしも仮引数の名前と同じでなくてもよい. たとえば,プログラム例 8.1 を関数副プログラムとして書き直してみると次のようになる. プログラム例 8.2 programfunc2 realx,f integeri,n n=20 do100i=0,n x=real(i)/real(n) write(*,*)'x=',x,'f(x)=',f(x) 62 第8章 関数 100continue stop end realfunctionf(x) realx f=x*exp(-x) return end 関数副プログラムは1つの値しか返すことができない.ただし,関数内で引数を変更すれば,関 数を呼び出した後にプログラムの流れが呼び出した側に戻ったとき,その引数の値は変わったま まなので,これを利用して2つ以上の値を計算することも可能である.そのようなプログラム例 を次に示す.ただし ,このような使い方は変則的なので2つ以上の値を求めたいときには次章で 説明するサブルーティンを使う. プログラム例 8.3 programfunc3 realx,y,z,f x=2.0 z=1.0 y=f(x,z) write(*,*)'x=',x,'y=',y,'z=',z 100continue end realfunctionf(x,z) realx,z f=x**2 z=x**3 return end このプログラム例は変数 x の2乗と3乗の値を求めるプログラムであるが,関数 f(x,z) は1つの 値しか返さないので,関数値として2乗を返し ,関数の引数 z に x の3乗を代入して返るよう に工夫している.関数 f(x,z) を呼ぶ前に z=1.0 としているのは,関数を呼ぶ前には引数が不定で あってはいけないので仮に適当な値を代入しているのである. 8.2. 63 関数副プログラム 次の簡単な例題を考えてみよう. 【例題 8.2 】実数 x を引数として,値 exp( x2 ) を返す関数 g(x) を定義して,x = 0 から 0:1 きざみ に x = 2 まで計算し ,x と g (x) の表を作成するプログラムを作れ. ; 【解答例】関数 g (x) を定義する. program Example8_2 100 integer i : 変数 i を整数として定義 real x,dx,f,g : 変数 x,dx,f と関数 g を実数として定義 dx=0.1 : きざみ幅 dx を 0.1 とする do 100 i=0,20 : i を作業変数として 1 から 20 まで繰り返す x=dx*real(i) : x の値を計算 f=g(x) : 関数 g(x) を呼び出す write(*,*) x,f : x,f の値を出力 continue stop end real function g(t) : 実数値関数を定義 real t g=exp(-t**2) : g の値を計算する return end 2つの整数の最大公約数と最小公倍数を求める問題は学校で数学の時間に習っても理解しにく い問題であるが,プログラミングにおいては基本的な問題であり,修得しておくべき課題である. 【 例題 8.3 】2つの整数を引数として,それら2つの整数の最大公約数を求める関数 gcm(m,n) を 定義して,この関数を用いてキーボード より入力した2つの整数の最大公約数と最小公倍数を求 めるプログラムを作れ. 【解答例】2つの整数を i, いことを用いる. j とすると,i と j の積は最大公約数 gcm と最小公倍数 lcm の積に等し program Example8_3 integer i,j,gcm : 変数 i,j と関数 gcm を整数として定義 integer m,n,ip : 最大公約数を m, 最小公倍数を n 64 第8章 関数 write(*,*) 'input two integers': i,j を読み込むためのプロンプト read(*,*) i,j : i,j を読み込む m=gcm(i,j) : 関数 gcm(i,j) を呼び出す n=i*j/m : i*j=m*n write(*,*) m,n : m,n を出力 stop end integer function gcm(m,n) : 整数値関数を定義 integer m,n,m1,n1,j,i m1=m : m の値を変えないように保存 n1=n 10 if(m1.lt.n1) then : m,n を比べて大きい方を新たに m とおく j=m1 m=n1 n1=j end if i=mod(m1,n1) : i は m1 を n1 で割った余り if (i.eq.0) then : m1 が n1 で割り切れれば n1 が最大公約数 gcm=n1 return else : 割り切れなければ n1->m1, m1-n1->n1 j=m1-n1 m1=n1 n1=j end if go to 10 return end 本来は,プログラミングで go to 文を使うべきではなく,このプ ログラム例でも do 文などでお き換えることも可能であるが,あえて go to 文を使った解答例を示した.Fortran 90 や C 言語で は while 文などがあり,無理なく go to 文を避けてプログラミングすることが可能である.この プログラムを実行して,たとえば 42 と 30 をキーボード より入力すると最大公約数として 6 ,最 小公倍数として 210 が得られる. 8.2. 65 関数副プログラム 第8章 演習問題 問題 1. 整数 1 から n までの2乗和 f (n) = 1 1 + 2 2 + : : : + n n を計算する関数を作り,こ れを用いてメインプログラム (main program) で2乗和 f (n) の値が 1000 を越えるときの n の値を求め,そのときの2乗和 f (n) を表示するプログラムを作れ. 問題 2. 2つのベクトル a = (a1 a2 a3 ) と b = (b1 b2 b3 ) を引数とし ,それらの内積を関数値と する関数を作り,これを用いることにより,2つのベクトルの各成分をキーボード より読み 込み,それらが直交しているかど うか調べるプログラムを作れ. 問題 3. 4 つの整数データを読み込み,それらの最大値を求めるプログラムを作れ.ただし データの最大値を計算する関数を作って用いること. 問題 4. じゃんけんのイニシャル `G', `T', `P' のうち2つの文字を入力し ,勝った方の文字を返す 関数を作り,キーボードからこれらの文字の2つを入力し,じゃんけんの勝ちを判定するプ ログラムを作れ.ただし ,`あいこ' のとき `D' を返すものとする. 問題 5. 3角形の3辺の長さ a,b,c を引数としての長さ a と b の2辺の間の角 を返す関数を作 り,これを用いて,キーボードから a,b,c を読み込みの長さ a と b の2辺の狭角 を表示 するプログラムを作れ. 2 つの 67 第 9 章 サブルーティン コンピュータ言語 Fortran を用いて構造化プログラミングを行うときには,多くの場合関数副プ ログラムよりもサブルーティン副プログラム (今後単にサブルーティンと呼ぶ) を用いる.この章 ではサブルーティンの記述法と呼び出し法について学ぶ. 9.1 サブルーティンの記述と呼び出し 仕事や研究で本格的な数値計算や数値シミュレーションを行うときは,そのプログラムは大き くなり,何千行や何万行にも及ぶことがあり,複雑な構造になる.複雑で長いプログラムには必ず バグ (プログラミングのミス) が入り込み,バグ取りを効率的に行う必要がある.そのために,プ ログラムを構造化する.サブルーティン (subroutine) は関数副プログラムと共に構造化プログラ ミングを実現するための重要な機能である.サブルーティンが関数副プログラムと異なる点は値 (返り値) をもたない点である.したがって,サブルーティンには型 (宣言) がない.サブルーティ ンはメインプログラム (main) または別のサブルーティンから呼び出されて,サブルーティン内で 記述された処理が終わると,処理の流れ (制御) をメインプログラムあるいは呼び出した側のサブ ルーティンに移す.サブルーティンの記述は次のように行う. subroutine S (x1, x2, ...) A return end ここで,S は英文字から始まる6文字以内の名前 (サブルーティン名) である.かっこの中には引数 を定義することができる.この引数は関数の場合と同様に仮引数と呼ばれる.また,引数は 0 個で あってもよい.このサブルーティンの内部で A (複数の文も可能) という処理を行った後,return 文により,プログラム処理の流れはこのサブルーティン S を呼び出したメインプログラムまたは 他のサブルーティンに移る. このサブルーティンを呼び出すときは call S (y1, y2, ...) と記述する.サブルーティンは値をもたず,関数のように変数に代入することができないので,call 文により呼び出しを行う.呼び出すときの引数は実引数と呼ばれる.実引数の変数名は仮引数の 変数名と同じである必要はない.ただし ,それらの並び順と型が一致している必要がある. call 68 第 9 章 サブルーティン 文によりサブルーティンを呼び出すと,実引数のアドレスがサブルーティンに引き渡される.し たがって,サブルーティン内で (仮) 引数の値を変更すると,制御が呼び出し側に移ったとき呼び 出した側の実引数の値も変更される.もし ,サブルーティンを呼び出すときに call S (1.0, 2.0, ...) などのように定数 (1.0 や 2.0) などを実引数として指定して,しかも,サブルーティンの内部でこ れらの値を変更してしまったらど うなるだろう.このとき,定数は変更することができないので, 重大なエラーとなる.したがって,サブルーティンの呼び出しにおいても関数副プログラムの場 合と同様に実引数には変数名を使う習慣をつけておくことが必要である1 .第5章でも説明したよ うに,サブルーティンの PAD 図は図 9.1 のように表される. S Subroutine S A 図 9.1: サブルーティンを表す PAD 図 たとえば,2つの実数 x,y の積と商を計算するサブルーティンを定義し,呼び出すプログラム を考える.サブルーティン名を calc とする.引数は4つ必要でそれらを x, y, prod, quot としよ う.プログラムは次のようになる. プログラム例 9.1 programsubcall reala,b,p,q:a,b,p,qを実数として定義 write(*,*)'Inputaandb' read(*,*)a,b callcalc(a,b,p,q):サブルーティン calcの呼び出し write(*,*)'a=',a,'b=',b,'a*b=',p,'a/b=',q stop end c subroutinecalc(x,y,prod,quot) 1 サブルーティン内で必要以上に引数の値を変更しないように心がけておかないと予期しない結果が生じることもあ る.特に,サブルーティン内だけで使う作業変数のとしては引数を使わず,作業変数は別に定義しておく必要がある. 9.1. 69 サブルーティンの記述と呼び出し realx,y,prod,quot prod=x*y quot=x/y return:制御をメインプログラムへ返す end 同様の例題として,正の整数 m を n で割ったときの商 ndev と余り nres を計算するサブルー ティンとそれを呼び出す簡単なプログラミングの例題を考えよう. 【例題 9.1 】2つの正の整数をキーボード より読み込み1つ目の整数を2つ目の整数で割ったとき の商と余りを出力するプログラムを作れ.ただし ,4つの整数 m,n, ndev, nres を引数として, 整数 m を n で割ったときの商 ndev と余り nres を計算するサブルーティン calcs (m, n, ndev, nres) を作り,このサブルーティンを用いてプログラミングを行うこと. 【解答例】整数 m を n で割ったときの商 n, ndev, nres) を定義する. ndev と余り nres を計算するサブルーティン calcs (m, program subcalls integer m,n,np,nq : m,n,np,nq を整数として定義 write(*,*) 'Input m and n' read(*,*) m, n call calcs (m, n, np, nq) : サブルーティン calcs の呼び出し write(*,*) 'm= ', m, 'n= ',n, 'm/n= ', np, 'mod(m,n)= ',nq stop end c subroutine calcs (m1, n1, np1, nq1) integer m1, n1, np1, nq1 np1=m1/n1 nq1=m1-m1/n1*n1 return : 制御をメインプログラムへ返す end このプログラムでも n として 0 を入力しないことを仮定している. 次に,今後よく使うことになるテクニック,すなわち2つの数の大小の判定するを繰り返すこと によって多数の (3つ以上の) 数の並べ替えを行う例題を考えてみよう. 【 例題 9.2 】2つの実数 x,y を引数として,それら2つの実数を比較し ,大きい数を x,小さい 数を y として返すサブルーティン swap(x, y) を作り,このサブルーティンを用いて3つの実数を キーボード より読み込みそれらを大きい順に出力するプログラムを作れ. 70 第 9 章 サブルーティン 【解答例】2つの実数を大きい順に並べ替えるサブルーティン swap(x, y) を定義する.a と b,a と c,b と c を比較して並べ替えることにより,a, b, c の順に大きい数となる. program Example9_2 : 変数 a,b,c を実数として定義 real a,b,c write(*,*) 'input three real numbers' : a,b,c を読み込むためのプロンプト read(*,*) a,b,c : a,b,c を読み込む call swap(a,b) : a,b を比較,大きい方を a, 小さい方を b とする call swap(a,c) : a,c を比較,大きい方を a, 小さい方を c とする : これで a が1番おおきくなった call swap(b,c) : b,c を比較,大きい方を b, 小さい方を c とする write(*,*) 'largest: ',a : 1番大きいのは a write(*,*) 'midle : 中くらいは b : ',b write(*,*) 'smallest: ',c : 1番小さいのは c stop end subroutine swap(x,y) : 大きい順に並べるサブルーティン real x,y,d if (y>x) then : yが x より大きいとき 2 つの数を入れ替える d=x x=y y=d end if return end 現在でも,Fortran でプログラミングを行うときに,グラフィック表示を初めとする図形出力に は労力を要する.そのため Fortran が作られた当初から手軽に簡単な表を描く方法がいくつか考 えられてきた.次の例題はそのような方法の1つである. 【 例題 9.3 】整数を引数として,その整数とその整数の数だけ `*' を出力するサブルーティンを作 り,このサブルーティンを使って,キーボード より入力した3つの整数とその数の `*' を出力する プログラムを作れ. 9.1. 71 サブルーティンの記述と呼び出し 【解答例】引数の整数で表される個数の `*' を出力するのに次のようにする.次章で説明する配列 を使えばもっと簡単にプログラミングをすることができる.ただし ,ここでは配列を使わずに次 のようにプログラミングを行う. program Example9_3 : 変数 i1,i2,i3 を整数として定義 integer i1,i2,i3 write(*,*) 'input three integers': 整数 i1,i2,i3 を読み込むためのプロンプト read(*,*) i1,i2,i3 : i1,i2,i3 を読み込む call stars(i1) : 整数 i1 と i1 個の '*' を出力 call stars(i2) : 整数 i2 と i2 個の '*' を出力 call stars(i3) : 整数 i3 と i3 個の '*' を出力 stop end subroutine stars(n) : n 個の'*' を出力するサブルーティンを定義 integer i,n write(*,100) n,':',('*',i=1,n) 100 : 整数 n と n 個の '*' を出力 format(1x,i2,70a1) return end このプログラムを実行して,たとえば ような出力結果が得られる. 10, 20, 30 の3つの整数をキーボード から入力すると次の 10:********** 20:******************** 30:****************************** サブルーティンを使用する例として,組み合わせの数 (Combination) m Cn を計算するプログラ ムを作ってみよう. 【例題 9.4 】2つの整数を引数として,組み合わせの数 m Cn を計算するサブルーティンを作り,こ のサブルーティンを使って,キーボード より2つの整数 m と n を入力し,m Cn を求めるプログ ラムを作れ. 【解答例】1つの整数 k を引数としてその階乗 k! を計算するサブルーティンを作り,それを3回 呼び出しても m Cn を計算できるが,階乗の計算では計算機で計算可能な整数の上限を越える危険 がある.したがって,次のようにプログラミングを行う. 72 第 9 章 サブルーティン program Example9_4 integer m,n,k : m,n,k は整数 write(*,*) 'input two integers' read(*,*) m,n if (m.lt.n) then : m<n ならば計算を行わない write(*,*) 'm must be larger than n' stop end if call combi(m,n,k) write(*,*) 'Combination of mCn = ', k stop end subroutine combi(m,n,k) integer m,n,n1,n2,k,i,id n1=n n2=m-n if(n1.lt.n2) then : n1>n2 となるように入れ換える id=n1 n1=n2 n2=id end if k=1 do 100 i=m,n1+1,-1 : mPn1 を計算 k=k*i 100 continue do 200 i=n2,2,-1 : mPn1/n2!を求める k=k/i 200 continue return end このプログラムを実行して,たとえば 10, 4 の2つの整数をキーボード から入力すると答えとし て 210 と求められるが,20, 10 を入力すると 117 となり,正しい答えが得られない.これはサブ ルーティンの内部で,変数 k の値が途中で最大値 2,147,483,647 を越えたからである.このプロ グラムでは計算途中で k の値が大きくならないように,n と m-n の大きい方を見つけて mPn ま たは mPm-n のいずれかを計算した後に,(m-n)! または n! で割っている. 9.1. 73 サブルーティンの記述と呼び出し 第9章 演習問題 問題 1. 2つの係数 a と b をキーボード より読み込み,1次代数方程式 ax + b = 0 の解を求める プログラムを作れ.ただし,1次代数方程式の解を求めるサブルーティンを作り,これを用 いること. 問題 2. 3つの係数 a,b,c をキーボード より読み込み,2次代数方程式 ax2 + bx + c = 0 の解を 求めるプログラムを作れ.ただし,a = 0 とし ,2次代数方程式の解を求めるサブルーティ ンを作り,これを用いること. 問題 3. 6 3角形の3辺の長さが与えたとき,その面積と3つの角を求めるサブルーティンを作り, これを用いて,キーボード より3辺の長さ a,b,c を読み込んで三角形の面積と3つの角を 表示するプログラムを作れ.ただし,3辺 a,b,c が3角形を作らないときは 'unable to form a triagonal' と表示すること. 問題 4. xy 平面上の2点 P1 (x1 y1 ) と P2 (x2 y2 ) をキーボード より読み込み,2点 P1 P2 を 2:1 に 内分する点 P0 の座標 (x0 y0 ) を求めるプログラムを作れ.ただし,2点を 2:1 に内分する サブルーティンを作り,これを用いること. 問題 5. 3角形の3つの頂点 P1 (x1 y1 ),P2 (x2 y2 ),P3 (x3 y3 ) をキーボード より読み込み,直線 x + y 1 = 0 に対してこの3角形と対称な3角形の3つの頂点を求めるプログラムを作れ. ただし,直線 x + y 1 = 0 に対してある点と対称な点を求めるサブルーティンを作り,こ れを用いること. ; ; 75 第 10 章 配列・文字列 配列を用いると1つの変数名で多くのデータを記憶したり,演算したりすることができる.同じ ような処理の繰り返しの多いプログラミングにおいては,配列を使うと非常に簡潔で効率的なプ ログラムを書くことができる.この章では配列と文字列の定義およびその使い方について学ぶ. 10.1 配列 数学におけるベクトルや行列のように,1つの変数名で多くのデータを取り扱うときには配列 (Fortran では dimension という) を用いる.配列は dimension 文を用いて次のように定義する. dimension a(d1, d2, … , dm) ここで,a(d1, d2, …, dm) は配列宣言子,a は配列名である.d1, d2, …, dm は寸法宣言子であ り,この例では m 個の寸法演算子をもつので m 次元配列であるという.Fortran 77 では7次元 配列まで定義できる.寸法演算子 d1 などは i1:i2 などのように表し ,i1 と i2 をそれぞれ寸法の 下限と上限という.下限が 1 のときはこれを省略することができる.このように定義した配列の 個々の成分は配列要素と呼び,配列名のあとにかっこをつけてその中に添え字を入れることにより 指定することができる.配列演算子も添え字も整数 (または整数型変数) でなければならない.変 数と同様に配列にも整数型,実数型,倍精度実数型,複素数型,論理型,文字型があり,それぞれ 配列型を宣言する必要がある.このとき,宣言 dimension を用いずに,型宣言分において配列を 定義することもできる. たとえば,実数 a が添え字 0 から 8 までをもち,9 個の実数型データを記憶する1次元配列で あれば real a dimension a(0:8) のように定義し,0 番目のデータを 列の寸法が 1 から始まるときには a(0) と表し , 8 番目のデータは a(8) のように引用する.配 real a dimension a(8) のように添え字の始まり real a(8) 1 を省略することができる.また,この例の場合では 76 第 10 章 配列・文字列 のように,dimension 文を省略することも可能である. 同様に, たとえば 0 から 10 までと 1 から 5 までの2つの添え字をもつ整数型配列 mは integer m(0:10, 5) のように定義する.変数 m は 配列要素 m(0, 1) から m(10, 5) までの合計 55 個のデータ (整数) を記憶することができる. たとえば,10 個の実数をキーボード より読み込み,それらを配列 x(10) に代入するプログラム は次のようになる. プログラム例 10.1 programdimsamp parameter(n=10):nは定数で値は 10 realx(n):xは要素数 10 の実数配列 integeri:iを実数として定義 do100i=1,n write(*,*)'Input',i,'-thnumber' read(*,*)x(i) 100continue do200i=1,n write(*,*)i,'-thnumber=',x(i) 200continue stop end このプログラムでは parameter 文を使って n=10 を宣言した.配列の大きさを自由に変えるよう にするには,このプログラム例のように parameter 文で定数を宣言しておく.なぜなら,配列の宣 言 dimension 文の前に定数や変数の値を代入する代入文が使えないからである.parameter 文で 宣言した n は定数なのでこの値をプログラム中で変更すると異常終了するので注意が必要である. 実数型配列を用いる次の簡単な例題を考えてみよう. 【例題 10.1 】キーボードから5人の学生の試験得点を読み込み,それらの平均点を求めるプログラ ムを作れ. 【解答例】配列寸法が 5 の整数型配列 iscore(5) を定義する. program Example10_1 integer iscore(5),isum,i : iscore を整数型配列として定義 real average : average (平均) を実数として定義 isum = 0 : 初めに isum を 0 としておく 10.1. 77 配列 do 10 i=1,5 write(*,*) 'Input the ',i,'-th score:' read(*,*) iscore(i) isum=isum+iscore(i) 10 continue average = real(isum)/5.0 write(*,*) 'Average score is ', average stop end この例題では,iscore を配列として定義しておく必要はなかったが,ここでは配列型変数を使用 する例としてあえて整数型配列を定義した. 配列の入出力には簡単な方法がある.たとえば,10個の要素をもつ実数配列 x(10) を読み込 むときには read(*,*) (x(i),i=1,10) と記述し,出力するには write(*,*) (x(i),i=1,10) のようにあたかも 例を考える. do 文を1行にしたような書き方ができる.この記述法を使って九九の表を作る プログラム例 10.1 programkuku integermn(0:9,0:9),i,j,n:0 行目はかけられる数 :0 列目はかける数 n=9 do100i=1,n mn(0,i)=i mn(i,0)=i 100continue do200i=1,n do200j=1,n mn(i,j)=i*j 200continue write(*,400)(mn(0,j),j=0,n) write(*,500):'-' を40個出力 do300i=1,n write(*,400)(mn(i,j),j=0,n) 78 第 10 章 配列・文字列 300continue 400format(1x,i3,'|',9(1x,i3)) 500format(1x,40('-')) stop end 出力結果が見にくくなるのをさけるために,この例のように書式付き出力を行う. 少し複雑だが,配列を使う頻度の高い次の例題を考えよう. 【例題 10.2 】10 個の整数をキーボードから読み込み,それらの平均,分散,最大値および最小値を 計算するプログラムを作成せよ. 【解答例】 program Example10_2 integer a,i,n : 変数 a,i,n を整数として定義 real sum,sum2,ave,var,amax,amin dimension a(10) : a を a(1) から a(10) までの配列として定義 : (integer a(10) でもよい) n=10 : キーボードから読み込む数は 10 個 do 100 i=1,n write(*,*) 'input ',i,'th integer': i 番目の整数を読み込むためのプロンプト read(*,*) a(i) 100 : i 番目の整数 a(i) を読み込む continue sum=a(1) : 最初に sum に a(1) を代入しておく amax=a(1) : 最初は最大値を a(1) としておく amin=a(1) : 最初は最小値を a(1) としておく do 200 i=2,n sum=sum+a(i) : sum に a(2) から a(n) までを加える if (a(i).gt.amax) then amax=a(i) : a(i) が amax より大きければおき換える end if if (a(i).lt.amax) then amin=a(i) : a(i) が amin より小さければおき換える end if 200 continue ave=sum/float(n) sum2=0.0 do 300 i=1,n sum2=sum2+(a(i)-ave)**2 300 continue : a(i) と平均 ave との差を2乗して足しあわせる 10.2. common 文 (共通記憶領域) 79 var=sum2/real(n) write(*,*) 'average = ',ave write(*,*) 'variance= ',var write(*,*) 'maximum = ',amax write(*,*) 'minimum = ',amin stop end 10.2 common 文 (共通記憶領域) Fortran では記憶領域を節約するために,メインプログラムとサブルーティンまたは2つ以上の サブルーティン間で配列を同じ記憶領域にとることができる.そのための宣言文が common 文で あり,次のように定義する. program main common /a1/a(d1, d2, …, dm) A stop end subroutine sub1 common /a1/a(d1, d2, …, dm) B return end ここで,/a1/は common 領域の名前であり,これを省略することもできる.ただし ,数多くの common 領域を設定するときはすべての common 文を書くのが面倒であり,しかも煩雑となるの で次のように名前をつけて必要最小限の common 文のみを書くことができる. program main common /a1/a(d1, d2, …, dm) common /b1/b(e1, e2, …, em) A stop end subroutine sub1 common /a1/a(d1, d2, …, dm) B return end 80 第 10 章 配列・文字列 subroutine sub2 common /b1/b(e1, e2, …, em) C return end common 文は Fortran の最大の特徴でもあるが,最大の欠陥でもある.なぜなら,common 文を 使うと配列変数の値をプログラム中のいろいろな場所で変更できてしまうからである.Fortran90 ではなるべく common 文を使わずに配列変数を引数としてサブルーティンに渡すように書くこと になっている.C 言語も同様である. common 文の使用例として,10 個の実数をキーボード より読み込み,それらを配列 x(10) に代 入し,サブルーティンでその値を2乗するプログラムは次のようになる. プログラム例 10.2 programcomsamp parameter(n=10):nは定数で値は 10 commonx(n):xは要素数 10 の共通配列 integeri:iを実数として定義 do100i=1,n write(*,*)'Input',i,'-thnumber' read(*,*)x(i) 100continue callsq2() do200i=1,n write(*,*)'squareof',i,'-thnumber=',x(i) 200continue stop end subroutinesq2() parameter(n=10):nは定数で値は 10 integeri commonx(n):xは要素数 10 の共通配列 do100i=1,n x(i)=x(i)**2 100continue return end 10.3. 文字列 81 この例からも common 文を使うとプ ログラムが分かりにくくなることが見て取れる.しかし , Fortran77 ができた当時はプログラムの可読性 (読み易さ) よりも記憶領域の節約に重きをおいて いたので,現在の価値基準から考えるのは誤りである. 10.3 文字列 文字型定数は2つのシングルクオテーション(アポストロフィーともいう) 「 '] でいくつかの文 字を挟むことにより定義する.たとえば,'mojiretsu' は文字定数である.また,文字型変数は character*n s または character s*n のように定義する.ここで,n は文字列の長さである.文字型変数の配列は character*n s(5) と定義する. 2つの文字列をつなぎ合わせて1つの文字列とするのには文字列連結演算子「 // 」を用いる.た とえば,'abc ' と 'def' をつなぎ 合わせると長さ 7 の文字列となるが,これを長さ 10 の文字列 変数である s に代入するには次のようにプログラミングを行う. プログラム例 10.3 programconnect character*10s s='abc'//'def' write(*,*)s stop end このプログラムをコンパイルした後,実行すると abc def と出力される. 1つの文字列のうちのいくつかをおき換えたり,引用したりするときには s(m:n) のように指定 する.たとえば,s='abcdefghijkl' のうち3文字目から5文字目までを 'z' でおき換えるには次の ように行う. プログラム例 10.4 programreplace character*12s 82 第 10 章 配列・文字列 s='abcdefghijkl' s(3:5)='zzz' write(*,*)s stop end このプログラムをコンパイルした後,実行すると abzzzfghijkl と出力される.ただし,文字列 変数を配列として定義した場合にはこのような引用をすることはできない. 名前 定義 引数 関数値 len(a) 文字列 a の文字長 character*n integer index(s,t) 文字列 s 中にある文字列 t の位置 character*m, character*n integer 表 10.1: 文字型変数に関する組み込み関数 文字列をキーボードから入力するとき,書式なし read 文を使う場合には文字列をシングルクオ テーションで囲っておく必要がある.書式付き read 文を使えば文字列をシングルクオテーション で囲う必要はない. 10.3.1 文字型変数に関する組み込み関数 文字型変数 (または文字型定数) に関する組み込み関数には,文字列の長さを調べる関数 len(s) と1つの文字列中に含まれる別の文字列の位置を調べる関数 index(s,t) がある (表 10.3). 関数 len(s) は次のように使う. プログラム例 10.5 programExample10_3 integern callchlen('abc',n) write(*,*)n stop end subroutinechlen(s,n) 10.4. 83 文字列の大小関係 integern character*(*)s n=len(s) return end このプログラム例では,サブルーティン chlen において文字列 s を character*(*) s のように宣言 している.ここで,(*) を引き渡し長さと呼び,呼び出し側で定義されている長さに合わせて整合 的に長さをとる.この例では,3文字の長さとなる.配列をサブルーティンで定義するときにも このような引き渡し長さを使うことができる. 文字列関数を例としてパスワード をチェックする次の例題を考える. 【例題 10.3 】10 文字以内の英数字をキーボードから読み込み,あらかじめ登録されているパスワー ド と一致するかど うか判定するプログラムを作成せよ. 【解答例】 program Example10_3 character s*10,z*10 : 変数 s,z を長さ 10 の文字列として定義 integer i 100 z='xpassword' : あらかじめ登録したパスワードは xpassword write(*,*) 'input your password' : s パスワード を読み込むためのプロンプト read(*,100) s : i 番目の整数 a(i) を読み込む format(a10) : 10文字を文字列として読み込む書式 i=index(s,z) : 読み込んだパスワード の中に登録した : パスワードがあれば i は 0 でない if(i.eq.0) then write(*,*) 'incorrect password' : パスワード は正しくない else write(*,*) 'correct!' : パスワード は正しくない end if stop end 10.4 文字列の大小関係 1つの英数文字の大小関係はアスキー表 (第4章の ASCII 表を参照) の順序によって判断する. アスキー表における前の文字は後ろの文字よりも小さいとする.いくつかの英数文字からなる文 84 第 10 章 配列・文字列 字列の大小関係は初めの文字から順に比較して大小関係を決める.異なる長さの文字列を比較す るとき,同じ長さの部分列が同じであれば短い文字列の方が小さいとする.Fortran77 では大小の 比較に .gt. や .eq. などの関係演算子を使用することになっているが,ここでは見やすさを優先 して Fortran90 の記号である > や == などを使って文字列の大小関係の例を示す. 'a'=='b' => F (.false.) 'a'<'b' => T (.true.) 'a'>'b' => F 'abc'>'ab'=> T 文字列の大小関係を用いて次の例題を考える. 【例題 10.4 】キーボード から 20 文字以内の4人分の名前をローマ字で入力 ('Ken' など ) して,そ れらの名前をアルファベット順に並べ替えるプログラムを作れ. 【解答例】キーボードから文字列を入力するときにはその前後を \"' (クォテーション ) で囲む必要 があることに注意せよ. program Example10_4 integer i,j,m : 変数 i,j,m を整数として定義 character*20 s(4) : 20 文字の文字列 s は s(1) から s(4) までの配列 m=4 : キーボードから読み込む数は 4 do 100 i=1,n do 100 i=1,m write(*,*) 'input a name' : i 番目の名前を読み込むためのプロンプト read(*,*) s(i) 100 : i 番目の名前を読み込む continue do 200 i=1,m-1 do 200 j=i+1,m call swap(s(i),s(j)) 200 : s(i) と s(j) の名前の順序を比較し必要なら入れ替える continue do 300 i=1,m write(*,*) s(i) 300 : s(i) を順番に出力 continue stop end subroutine swap(s1,s2) character*20 s1,s2,dmmy if (s1.gt.s2) then dmmy=s1 s1=s2 : 2つの文字列を入れ替えるにはダミー (dmmy) が必要 : 2つの文字列の比較 (s1>s2 ならば入れ替える) 10.4. 85 文字列の大小関係 s2=dmmy end if return end 例題 10.4 とほぼ同じだが、より実用的な次の例題を考える. 【例題 10.5 】 6 人分の名前 (ローマ字) と数学の点数をキーボード から読み込み,数学の点数が高い順に氏名 と数学の点数を並び換えよ. 【解答例】 program Example10_5 integer i,j,m,p(6) : 変数 i,j,m,p(6) を整数として定義 character*20 s(6) : 20 文字の文字列 s を s(1) から s(4) までの配列として定義 m=6 : キーボード から読み込む数は 6 do 100 i=1,m write(*,*) 'input ',i,'th name' : i 番目の名前を読み込むためのプロンプト read(*,*) s(i) : i 番目の名前を読み込む write(*,*) 'input ',i,'th mark' : i 番目の点数を読み込むためのプロンプト read(*,*) p(i) 100 : i 番目の点数を読み込む continue do 200 i=1,m-1 do 200 j=i+1,m call swap(p(i),p(j),s(i),s(j)) : p(i)<p(j) ならば入れ替える 200 continue do 300 i=1,m write(*,*) s(i),p(i) 300 continue stop end subroutine swap(p1,p2,s1,s2) integer p1,p2,pd : 2つの整数を入れ替えるにはダミー (pd) が必要 character*20 s1,s2,dmmy : 2つの文字列を入れ替えるにはダミー (dmmy) が必要 if (p1.lt.p2) then pd=p1 p1=p2 p2=pd dmmy=s1 : 2つの整数の比較 (p1<p2 ならば入れ替える) 86 第 10 章 s1=s2 s2=dmmy end if return end 配列・文字列 10.4. 87 文字列の大小関係 第10章 演習問題 問題 1. n 個の実数 x(i) (i=1, 2, : : : , n) をキーボード より読み込み,それらの平均と分散および 標準偏差を出力するプログラムを作れ.ただし,n 個の実数の平均と分散を求めるサブルー ティンを作り,これを用いること. 問題 2. n 個の名前 (name(i),アルファベット10文字以内, i=1, 2, : : : , n) と数学の点数 (m(i), 整数) (i=1, 2, : : : , n) をキーボード より読み込み,それらを数学の点数の高い順に出力する プログラムをプログラムを作れ.ただし,2個の整数と名前を引数とし,それらを整数の大 きい順に入れ換えて返すサブルーティンを作り,これを用いること. 問題 3. n 個の名前 (name(i),アルファベット10文字以内, i=1, 2, : : : , n) と数学の点数 (m(i), 整数) (i=1, 2, : : : , n) をキーボード より読み込み,それらを名前のアルファベット順に出力 するプログラムをプログラムを作れ.ただし,2個の整数と名前を引数とし,それらをアル ファベット順に入れ換えて返すサブルーティンを作り,これを用いること. 問題 4. 整数 (n < 60) を引数として,その数のカラムに `*' を出力するサブルーティンを作り,こ れを用いてキーボード より読み込んだ n 個の整数 m(i) (i=1, 2, : : : , n) をグラフに表すプロ グラムを作成せよ.たとえば,整数が2つで 3 と 6 の場合には 3:* 6:* となる. 問題 5. 20文字以内の文字列の中にある特定の文字 (たとえば `a' ) が含まれているかど うかを判 定する関数を作り,これを用いてキーボード より読み込んだ文字の中に小文字 a から 大文 字 Z までのそれぞれの文字の頻度分布を調べるプログラムを作れ. 89 第 11 章 ファイル入出力 ファイル入出力を用いると,キーボード から入力する代わりにハードデ ィスクなどの外部記憶装 置に保存されたデータファイルから入力したり,ディスプレ イに表示する代わりに外部記憶装置 のファイルに出力することができる.この章ではファイル入出力の基本について学ぶ. 11.1 外部ファイルと2つのファイル形式 数多くのデータをキーボードから入力するときや何度も同じデータを繰り返し入力するときな どはハードデ ィスクに代表される外部記憶装置にデータを保存しておき,必要に応じてプログラ ムの中でそれらのデータを読み込むようにしておくと便利である.逆に,プログラムを実行して 大量のデータを計算したり求めたときにはそれらを外部記憶装置に保存しておく必要がある.こ れらの目的に Fortran のファイル入出力機能を用いる. Fortran (他の言語でもほぼ同じだが ) のファイル読み込みと書き込みには2つの形式がある.そ の1つは逐次参照ファイル (sequential access le) であり,データを最初から順次読み込む形式で ある.このファイルは,たとえば10番目のデータを直接に読み込むことができないので,10 番目のデータを知りたいときには1番目のデータから10番目まで読み込む必要がある.多くの 場合,科学技術計算においてはこの逐次参照ファイルが用いられる.これに対して,直接参照ファ イル (direct access le) は何番目のデータでも直接に読み出すことができる.たくさんのデータの 中から必要とするデータを見つけだすためのデータベースなどでは直接参照ファイルにしておく 必要がある.これらの2つのファイル形式のうち,この章では逐次参照ファイルについてのみ説 明する. 11.2 ファイルのオープンと入出力 ファイルからデータを読もうとする場合,まずファイルを開きそのファイルからデータを読み 出す.ファイルを開くためには open 文を用い,次のように記述する. open(un, file='filename', status='OLD') ここで,un は機器番号であり,コンパイラーにも依存するが通常 1 から 16 あるいは 1 から 64 までの整数である (un は省略形,正式には unit=un).unit=5 はデフォルトでキーボード (標準 入力) に割り当てられているが,open 文で unit=5 と指定すればファイルに割り当てることも可 能である.lename はデータが書かれているファイルの名前を指定する.プログラムを実行する 90 第 11 章 ファイル入出力 デ ィレクトリと同じディレクトリにあるファイルの場合はファイル名のみを記述し ,他のディレ クトリにあるときにはそのパス (ルートからそのファイルのあるデ ィレクトリに至るまでのパス) の後にファイル名を書く.status='OLD' という記述はすでにハードディスク上に存在するファイ ルからデータを読むことを意味している.ここでは,ファイルからデータを読もうとしているの であるから,ファイルはすでに存在していなければならない.データを読み終えた後はファイル を閉じる.ファイルを閉じるには close(un) と記述する. たとえば,`comp.dat' というファイルに2つの整数が書かれていてそれらの整数を読み出すと きには,次のようにプログラミングを行う. プログラム例 11.1 programreadfile integeri1,i2 open(1,file='comp.dat',status='OLD') read(1,*)i1,i2 write(*,*)i1,i2 close(1) stop end ファイルにデータを書き込むときも同様にファイルを開き,そのファイルに書き込む.このと きには3つの場合がある.1つ目の場合はファイルが存在しない場合.2つ目はファイルがすで に存在していて,いくつかのデータがそのファイルに書かれているが,そのデータを消して新た にデータを書くとき.3つ目はファイルに書かれているデータの後ろに追加する場合である.こ の場合には書かれているデータを読み出してから (ファイルポインタを最後に移動して ),新たに データを追加する必要がある.ここでは,1つ目と2つ目の場合のみを考えて,ファイルが存在 しないか,存在しても一度そのファイルを消去して新たにファイルを作ることにする.そのとき, ファイルを開くための open 文は次のように書く. open(un, file='filename', status='UNKNOWN') データを読むときの open 文の記述とほぼ同じだが,status='UNKNOWN' という部分のみが異 なる.lename で指定されたファイルが存在しないと確実にわかっているときには status='NEW' とすることも可能である.ファイルにデータを書き終えたら close 文によりファイルを閉じる.デ フォルトでは unit=6 がディスプレ イ出力 (標準出力) に割り当てられている. たとえば ,`comp.dat' というファイルを新たに作り (既にあればそれを削除した後に新たに作 り),そのファイルに2つの実数を書き出すときには,次のようにプログラミングを行う. 11.2. 91 ファイルのオープンと入出力 プログラム例 11.2 programwritefile realx1,x2 open(2,file='comp.dat',status='UNKNOWN') read(*,*)x1,x2 write(2,*)x1,x2 close(2) stop end ファイルにデータを書き込む例として次の例題を考える. 【例題 11.1 】2つの整数をキーボードから読み込み,それらをファイル 'integers.dat' に書き込むプ ログラムを作れ . 【解答例】 program Example10_1 integer i,n : 変数 i,n を整数として定義 open(1,file='integers.dat',status='UNKNOWN') : ファイル 'integers.dat' を開く do 100 i=1,2 write(*,*) 'input ',i,'th integer' : プロンプト 100 read(*,*) n : i 番目の整数 n を読み込む write(1,*) n : i 番目の整数 n をファイルに書き込む continue close(1) : ファイルを閉じる stop end このプログラムをコンパイルの後に実行し,キーボードからたとえば というファイルには 10 と 20 を入力すると'integers.dat' 10 20 というデータが書かれる.このようにして書いたデータを読み出すのには次のようにする. 【例題 11.2 】ファイル 'integers.dat' には2つの整数が書かれている.これらを読み込みこんでディ スプレ イ (標準出力) に出力するプログラムを作れ . 【解答例】 92 第 11 章 ファイル入出力 program Example11_2 integer i,n : 変数 i,n を整数として定義 open(1,file='integers.dat',status='OLD') : ファイル 'integers.dat' を開く do 100 i=1,2 read(1,*,end=99) n : i 番目の整数をファイルから読み込む write(*,*) i,'th integer= ', n : i 番目の整数 n を画面に出力する 100 continue 99 close(1) : ファイルを閉じる stop end ここで,read(1,*,end=99) n というのは機器番号 1 から整数データを読み込んで n に代入するこ とを示しているが,このときファイルの中に読むべきデータがなくなったら文番号 99 へプログラ ムの流れを移す.このプログラムを実行すると,例題 11.1 で作ったファイル `integers.dat' に 10 20 のように正しくデータが2つ書かれている場合には2つ読み込むが,誤って 10 のように1つし か書かれていないときでも1つだけ読み込んでプ ログ ラムを終了する.もし , read(1,*) n としておくと,プログラムは異常終了する. 同じファイルでも読み込み方法によっては正常に読めたり,異常終了したりすることもある.次 のように書かれているファイル `reals.dat' を考える. 1.0 2.0 3.0 4.0 5.0 6.0 このファイルを次のプログラム例 11.3 のプログラムで読み出すと異常終了となる. プログラム例 11.3 programreadfile integeri realx(6) open(1,file='reals.dat',status='OLD') do100i=1,6 read(1,*)x(i) 11.2. 93 ファイルのオープンと入出力 write(*,*)i,'thnumber=',x(i) 100continue 99close(1) stop end しかし,読み込みの方法を少し変えて,次のプログラム例 11.4 のようにすれば正しく読み込める. プログラム例 11.4 programreadfile integeri realx(6) open(1,file='reals.dat',status='OLD') read(1,*)(x(i),i=1,6) do100i=1,6 write(*,*)i,'thnumber=',x(i) 100continue 99close(1) stop end 次の応用例を考える. 【例題 11.3 】6 人分の名前 (ローマ字,20文字以内) と数学の点数をキーボードから読み込み,ファ イル'names.dat' に書き込むプログラムを作れ. 【解答例】 program Example11_3 integer i,j,m,p(6) : 変数 i,j,m,p(6) を整数として定義 character*20 s(6) : 20 文字の文字列 s を s(1) から s(6) までの配列として定義 open(1,file='names.dat',status='UNKNOWN') : ファイル 'names.dat' を開く m=6 : キーボードから読み込む数は 6 do 100 i=1,m write(*,*) 'input ',i,'th name' : i 番目の名前を読み込むためのプロンプト 94 第 11 章 ファイル入出力 read(*,*) s(i) : i 番目の名前を読み込む write(*,*) 'input ',i,'th mark' : i 番目の点数を読み込むためのプロンプト read(*,*) p(i) : i 番目の点数を読み込む write(1,200) s(i),p(i) : 書式付きで名前と点数をファイルに出力 100 continue 200 format(a20,i6) close(1) : ファイルを閉じる stop end このプログラムでは名前を文字列としてキーボード から読み込むときに書式なし read 文を使っ ているので,名前をキーボード から入力する際には 'Jiro Mizushima' のようにシングルクオテー ション「 ' 」で囲んでおく必要がある.シングルクオテーションで囲まずに入力したいときには書 式付き read 文にする.このプログラムを実行して適当に入力をするとファイル 'names.dat' には 次のように書かれる. Jiro Mizushima 100 Taro Tanaka 90 Akira Yamada 80 Kenji Imada 70 Kouji Nakamura 95 Junya Yamanaka 98 このようにして作ったファイルを使って次の例題を考える. 【 例題 11.4 】ファイル'names.dat' に 6 人分の名前 (ローマ字) と数学の点数が書き込まれている. それらを読み出して,点数の高い順番に画面に出力するプログラムを作れ. 【解答例】 program Example11_4 integer i,j,m,p(6) : 変数 i,j,m,p(6) を整数として定義 character*20 s(6) : 20 文字の文字列 s を s(1) から s(6) までの配列として定義 open(1,file='names.dat',status='OLD') m=6 : ファイル 'names.dat' を開く : ファイルから読み込む数は 6 do 100 i=1,m read(1,200) s(i),p(i) 100 continue 200 format(a20,i6) : 書式付きで i 番目の名前と点数をファイルから読む 11.2. 95 ファイルのオープンと入出力 close(1) : ファイルを閉じる do 300 i=1,m-1 do 300 j=i+1,m call swap(p(i),p(j),s(i),s(j)) : p(i)<p(j) ならば入れ替える 300 continue do 400 i=1,m write(*,*) s(i),p(i) 400 continue stop end subroutine swap(p1,p2,s1,s2) integer p1,p2,pd : 2つの整数を入れ替えるにはダミー (pd) が必要 character*20 s1,s2,dmmy : 2つの文字列を入れ替えるにはダミー (dmmy) が必要 if (p1.lt.p2) then pd=p1 p1=p2 p2=pd dmmy=s1 s1=s2 s2=dmmy end if return end : 2つの整数の比較 (p1<p2 ならば入れ替える) 96 第 11 章 ファイル入出力 第11章 演習問題 問題 1. n 個の実数 x(i) (i=1, 2, : : : , n) をキーボード より読み込み,それらをファイル `reals.dat' に書き込むプログラムを作れ.ただし ,有効数字 8 桁までファイルに書くこと. 問題 2. ファイル `reals.dat' に何個か不明の実数が書かれている.これらの実数をファイルから読 み,その個数と平均値を求めるプログラムを作れ. 問題 3. n 個の名前 (name(i),アルファベット10文字以内, i=1, 2, : : : , n) と数学の点数 (ma(i), 整数) および英語の点数 (me(i),整数) (i=1, 2, : : : , n) をキーボード より読み込み,それら をファイル `marks.dat' に書き込むプログラムを作れ. 問題 4. ファイル `marks.dat' には何個か不明の名前 (name(i),アルファベット10文字以内, i=1, 2, : : : , n) と数学の点数 (ma(i),整数) および英語の点数 (me(i),整数) (i=1, 2, : : : , n) が 書かれている.これらを読み出して,各人の数学と英語の点数の平均点を計算して出力する プログラムを作成せよ. 97 第 12 章 ポスト スクリプト による描画法 プログラミング言語 Fortran を用いてグラフや図を描くためにはそれぞれのコンパイラに付属の グラフィックス・ルーティンや OpenGL と呼ばれる汎用サブルーティンを使うのが一般的である が,それにはかなりの熟練を要する.この章では,簡単にグラフや図を描くための道具 (ポストス クリプト言語を利用) を準備した.この道具を使うといくつかのサブルーティンを呼び出すだけ で簡単にきれいな図を描くことができる.これらを修得してコンピュータで図を描くことを楽し もう. 12.1 ポスト スクリプト による図の描き方 ポストスクリプト 1を用いて図を描くときにはポインタ (pointer) とパス (path) という考え方が 大切である.ポインタを移動することにより,パスという図形を描く.パスで描かれた図形はま だ何の出力ももたないが,パスで表された図形に墨入れ (stroke,または ll) することによってパ スで表された図形が出力される.簡単にパスを描く方法には plot というサブルーティンまたは関 数を用いる.詳しくはホームページ http://www1.doshisha.ac.jp/~jmizushi にあるポストスクリ プトの説明または水島研究室が発行しているポストスクリプトのマニュアルを参考にすること. 図形を描く際の座標系については次の節で詳しく説明する.簡単のため,ここでは出力機器 (機 器座標系) の左下は (0 0) であり,右上は (1 1) であるとしておく.最も簡単に,ポストスクリプ トを用いて図を描くためのプログラムを示す.このプログラムは4つの点 (0:2 0:2) (0:8 0:2) (0:8 0:8) (0:2 0:8) を順次つないで正方形を描くプログラムである. ; ; プログラム例 12.1 programsimplePS callinit callnewpath callplot(0.2,0.2,3) callplot(0.8,0.2,2) callplot(0.8,0.8,2) callplot(0.2,0.8,2) callplot(0.2,0.2,2) callstroke 1 ポストスクリプト (PostScript) はアドビ (Adobe) 社の登録商標です. ; 98 第 12 章 ポストスクリプトによる描画法 callfin stop end ポストスクリプトを用いて図を描くとき,図を描く命令を書く前に必ず 2 行目のように call init によりポストスクリプト描画の準備を行う.これによって,` temp1.ps' というファイルが作ら れてこのファイルに描画命令が書かれる.図を描き終わったら 10 行目のように call fin によ り描画終了する.これにより,図を出力するための手続きが完了する (ページを出力し,ファイル temp1.ps を閉じる). パスを作成するときは,まず call newpath によってこれから新しいパスを作るという宣言 を行う.最も簡単にパスを描くのには,plot というパスを描くためのサブルーティンを使う. plot(x, y, 3) は線を書かずにポインタを移動するだけでパスを作る.plot(x, y, 2) は線を書 きながらポインタを移動してパスを作る.プログラム例 12.1 では 4 行目から 8 行目でパスを作っ ている.4 行目は plot(0.2, 0.2, 3) なので,点 (0.2, 0.2) にポインタを移動している.はじ めにはどこにポインタがあるかわからないので,とりあえずこの点までポインタを移動しておく. このとき,線は書かない.つぎに,線を書きながらポインタを点 (0:2 0:2),(0:8 0:2),(0:8 0:8), (0:2 0:8),(0:2 0:2) まで順次移動する.これでパスが完成する.ここまではまだなにも出力され ていないので,9 行目のように墨入れ (stroke) を行うとパスに墨が入り,正方形が描画される (図 12.1). 図 12.1: (0:2 0:8) (0:8 0:8) (0:2 0:2) (0:8 0:2) 4つの点 (0:2 0:2)-(0:8 0:2)-(0:8 0:8)-(0:2 0:8) を順次つないで描いた正方形. 【例題 12.1 】ポストスクリプトにより,図心を (0.5, 0.5) にもつ一辺が 0.4 の正三角形を描くプロ グラムを 書け. 【 解答例】図心が (0.5, 0.5) であり一辺の長さが 0.4 である正三角形の3つの頂点の座標は (0.3, 0.385),(0.7, 0.385),(0.5, 0.7309) と計算できるので,これらの3点を頂点とする三角形をポス 12.1. ポストスクリプトによる図の描き方 99 トスクリプトにより描くプログラムとその実行結果は図 12.2 のようになる. c psbasic.obj をリンクまたは psbasic.f77 と一緒にコンパイルすること program Examp12_1 call init call newpath call plot(0.3, 0.3845, 3) call plot(0.7, 0.3845, 2) call plot(0.5, 0.7309, 2) call plot(0.3, 0.3845, 2) call stroke call fin stop end 図 12.2: 図心を (0.5, 0.5) にもつ一辺が 0.4 の正三角形. 【例題 12.2 】ポストスクリプトにより,中心を (0.5, 0.5) とする半径 0.2 の円を描くプログラムを 書け. 【解答例】ここでは,中心を (0.5, 0.5) とする動径 0.2 が 0 から 2 まで 2=n 刻みで回転するとき の軌跡として円を描く. c psbasic.for または psbasic.obj と一緒にコンパイルすること program Examp12_2 real x0,y0,x1,y1,r,t,tend integer i,n 100 第 12 章 ポストスクリプトによる描画法 x0=0.5 y0=0.5 tend=2.0*3.14159 n=40 r=0.2 call init call rect(0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 2) call newpath t=0.0 x1=x0+r*cos(t) y1=y0+r*sin(t) call plot(x1, y1, 3) do 100 i=1, n t=real(i)/real(n)*tend x1=x0+r*cos(t) y1=y0+r*sin(t) call plot(x1, y1, 2) 100 continue call stroke call fin stop end 図 12.3: 中心を (0.5, 0.5) とする半径 0.2 の円 12.2. 図形の描き方{2 次元グラフィックス 12.2 101 {2 次元グラフィックス 図形の描き方 この節では 2 次元図形処理の基本である 3 つの座標系,すなわち世界座標系・正規座標系・機 器座標系について説明し ,ポストスクリプトを使った便利な描画法について解説する. これから描こうとする図に座標系 (世界座標,図 12.4(a)) を導入する.このことは,現実の世界 または図形を矩形領域に切り出すことに対応している.この座標系は任意にとることができる.長 さの単位も任意であるが,領域をできるだけ正方形に近く切り出しを行うとできあがる図がきれ いに描ける.世界座標系に書かれた図をコンピュータの機器座標系 (出力座標,図 12.4(c)) に変換 してグラフィック出力する.すなわち,切り出した世界座標における矩形領域を機器座標に線形写 像するので元の世界座標における図形の縦横比が機器座標系での出力図形と同じ縦横比を保つた めには切り出す世界座標系における矩形領域の縦横比と機器座標の矩形領域の縦横比を同じにす る.世界座標で描かれた図形を機器座標に線形写像するために,まず世界座標を正規座標に変換す る.正規座標系で図形は左下が (0 0) であり,右上が (1 1) である正方形領域に写像される.世界 座標系における矩形領域の左下を (x1 y1 ) とし,右上を (x2 y2 ) とすれば,世界座標の1点 (x y ) から正規座標 ( ) への変換は = xx ;;xx1 = yy ;;yy1 2 1 2 (12.2.1) 1 で表される.また,機器座標系における矩形領域の左下を (X1 Y1 ) とし,右上を (X2 Y2 ) とすれ ば,正規座標 ( ) から機器座標 (X Y ) への変換は X = (X2 ; X1 ) + X1 Y = (Y2 ; Y1 ) + Y1 (12.2.2) で表される.出力機器は通常 A4 の紙 (21 cm 27 cm) を想定しているため,X1 = 0,X2 = 21, Y1 = 0,Y2 = 27 であるが,ここでは覚えやすくするために,X1 = 0,X2 = 1,Y1 = 0,Y2 = 1 としておく. プログラム例 12.1 で最も簡単なプログラムとして,世界座標で4つの点 (0:2 0:2) (0:8 0:2) (0:8 0:8) (0:2 0:8) を順次つないで正方形を描くプログラムを示した.そこでは世界座標の取り 方と機器座標の取り方については明確に示さなかったが,世界座標と機器座標を明確に指定する とプログラムは次のようになる. ; プログラム例 12.2 programsimplePS callinit callviewport(0.0,0.0,1.0,1.0) callxyworld(0.0,0.0,1.0,1.0) callnewpath callplot(0.2,0.2,3) callplot(0.8,0.2,2) callplot(0.8,0.8,2) ; ; 102 第 12 章 ポストスクリプトによる描画法 (a) 世界座標系 (x1 y2 ) (x2 y2 ) (x1 y1 ) (x2 y1 ) (b) 正規座標系 (0 1) (1 1) (0 0) (1 0) (c) 機器座標系 (X1 Y2 ) (X2 Y2 ) (X1 Y1 ) (X2 Y1 ) 図 12.4: 世界座標と正規座標と機器座標. callplot(0.2,0.8,2) callplot(0.2,0.2,2) callfin stop end このプログラムでは, call viewport(0.0,0.0,1.0,1.0) で機器座標の全体すなわち左下 (0, 0) から右上 (1, 1) までを使って描画することを宣言している.また,使用する世界座標も左下が (0, 0) であり,右上が (1, 1) であることを宣言している. 機器座標として左下を (X1, Y1),右上を (X2, Y2) と指定するには call viewport(X1,Y1,X2,Y2) と記述する.また,世界座標として左下を (x1, y1),右上を (x2, y2) と指定するには call world(x1,y1,x2,y2) と記述する. これらの命令を説明する前に,世界座標で 4 つの点 (0:2 0:2) (0:8 0:2) (0:8 0:8) (0:2 0:8) を順次つないで正方形を描くプログラムを改良しておく.はじめに指定したパスの出発点までポ インタを戻すときは, closepath という命令があるので,これを用いる. ; プログラム例 12.3 ; ; 12.2. 図形の描き方{2 次元グラフィックス 103 programsimplePS callinit callviewport(0.0,0.0,1.0,1.0) callxyworld(0.0,0.0,1.0,1.0) callnewpath callplot(0.2,0.2,3) callplot(0.8,0.2,2) callplot(0.8,0.8,2) callplot(0.2,0.8,2) callclosepath(0.2,0.2,2) callfin stop end もっと簡単に矩形を描く命令 rect(x1,y1,x2,y2,g,w,it) を使って次のようにすればもっと 短いプログラムとなる. プログラム例 12.4 programsimplePS callinit callviewport(0.0,0.0,1.0,1.0) callxyworld(0.0,0.0,1.0,1.0) callrect(0.2,0.2,0.8,0.8,0.0,1.0,1) callfin stop end 命令 rect(x1,y1,x2,y2,g,w,it) は左下を (x1, y1),右上を (x2, y2) とする矩形を明度 g (g=0 はもっとも黒く,g=1 は最も白い) で,線の幅を w (w/72 インチ=w 2.54/72 cm=w 0.353 mm) として実線 (it=1 のとき) で描く. 機器座標を4等分して左下を (0:0 0:0) とし,右上を (0:5 0:5) とする領域,左下を (0:5 0:0) とし, 右上を (0:5 1:0) とする領域,左下を (0:0 0:5) とし,右上を (0:5 1:0) とする領域,左下を (0:5 0:5) とし,右上を (0:5 1:0) とする領域の4つの部分に分けてそれらにそれぞれ世界座標で点 (0:2 0:2) を左下,(0:8 0:8) を右上とする正方形を描くプログラムは次のようになる. プログラム例 12.5 programsimple1 callinit callxyworld(0.0,0.0,1.0,1.0) 104 第 12 章 ポストスクリプトによる描画法 callviewport(0.0,0.0,0.5,0.5) callrect(0.2,0.2,0.8,0.8,0.0,1.0,1) callviewport(0.5,0.0,1.0,0.5) callrect(0.2,0.2,0.8,0.8,0.0,1.0,1) callviewport(0.0,0.5,0.5,1.0) callrect(0.2,0.2,0.8,0.8,0.0,1.0,1) callviewport(0.5,0.5,1.0,1.0) callrect(0.2,0.2,0.8,0.8,0.0,1.0,1) callfin stop end このプログラムの実行すると図 12.5 のような図形が得られる. 12.5: 機器座標を4等分して左下を (0:0 0:0) を右上を (0:5 0:5),左下を (0:5 0:0) を右上を (0:5 1:0),左下を (0:0 0:5) を右上を (0:5 1:0),左下を (0:5 0:5) を右上を (1:0 1:0) の4つの部分 に分けてそれらにそれぞれ世界座標で点 (0:2 0:2) を左下,(0:8 0:8) を右上とする正方形. 図 世界座標で点 (0:2 0:2) を左下,(0:8 0:8) を右上とする正方形を描きその中を灰色 (g=0.5) で塗 りつぶすプログラムは次のようになる. プログラム例 12.6 programsquarefill callinit callviewport(0.0,0.0,1.0,1.0) callxyworld(0.0,0.0,1.0,1.0) callrect(0.0,0.0,1.0,1.0,0.0,1.0,2) 12.2. 図形の描き方{2 次元グラフィックス 105 callnewpath callplot(0.2,0.2,3) callplot(0.8,0.2,2) callplot(0.8,0.8,2) callplot(0.2,0.8,2) callclosepath callsetgray(0.5) callfill callfin stop end このプログラムを実行すると図 12.6 のような図形が得られる. 図 12.6: 世界座標で点 (0:2 0:2) を左下,(0:8 0:8) と正方形の内部を塗りつぶした図形. 【例題 12.3 】ポストスクリプトにより,円とその円に内接する正六角形を描くプログラムを書け. 【解答例】ポストスクリプトにより,円とその円に内接する正六角形を描くプログラムは次のよう に書ける. program Examp12_3 real x0=0.0,y0=0.0,rr=30.0,pai=3.141593 real x(6),y(6) integer i,k do 100 i=1,6 x(i)=x0+rr*sin(2.0*pai*real(60*i)/real(360)) y(i)=y0+rr*cos(2.0*pai*real(60*i)/real(360)) 106 100 第 12 章 ポストスクリプトによる描画法 continue call init call viewport(0.2, 0.2, 0.8, 0.8) call xyworld(-50.0, -50.0, 50.0, 50.0) call circ1(x0,y0,rr) call newpath k=3 do 200 i=1,6 call plot(x(i),y(i),k) k=2 200 continue call closepath call fin stop end !--------------------------------------------------------------- このプログラムの実行結果は図 12.7 のようになる. 図 12.7: 円に内接する正六角形 ; 【例題 12.4 】ポストスクリプトにより,関数 sin x のグラフを x = 1 1] の範囲でグラフに描く プログラムを書け. 【解答例】ポストスクリプトにより,関数 sin x のグラフを x = 1 1] の範囲でグラフに描くプ ログラムは次のようになる. ; program Examp12_4 12.2. 図形の描き方{2 次元グラフィックス call init call viewport(0.2, 0.2, 0.8, 0.8) call xyworld(-1.2, -1.2, 1.2, 1.2) call frame call drawline call fin stop end subroutine frame integer i,n real x,y,dx,dy call arrow( -1.1, 0.0, 1.1, 0.0, 0.02, 0.0, 1.0) call arrow( 0.0, -1.1, 0.0, 1.1, 0.02, 0.0, 1.0) call text(1.1,-0.1,1,'x') call text(-1.1,-0.12,2,'-1') call text(0.98,-0.12,1,'1') call text(-0.1,1.1,1,'y') call text(-0.12,-1.02,2,'-1') call text(-0.12, 0.98,1,'1') call text(-0.12,-0.1,1,'0') n=10 do 100 i=1, n x=0.1*i if(i/5*5.eq.i) then dy=0.02 else dy=0.01 end if call line( -x, -dy, -x, dy, 0.0, 1.0, 1) call line( 100 x, -dy, x, dy, 0.0, 1.0, 1) continue do 200 i=1, n y=0.1*i if(i/5*5.eq.i) dx=0.02 else dx=0.01 then 107 108 第 12 章 ポストスクリプトによる描画法 end if call line( -dx, -y, dx, -y, 0.0, 1.0, 1) call line( -dx, 200 y, dx, y, 0.0, 1.0, 1) continue return end subroutine drawline integer i, nx real x,y,t nx= 100 x=-1.0 y=0.0 call linewidth(1.5) call plot( x, y, 3) do 100 i = -nx+1,nx t = float(i)/float(nx) x = t y = sin(t*3.14159) call 100 plot( x, y, 2) continue return end y 1 -1 0 1 x -1 図 12.8: x = ;1 1] における y = sin x のグラフ 12.2. 図形の描き方{2 次元グラフィックス 【例題 12.5 】 ポストスクリプトを用いて関数 y (x 軸と y 軸) も入れること. 【解答例】 = x2 を ;1 x 1 の範囲で描くプログラムを作れ . 座標軸 program Example12_5 integer i,n real x,y,xx,yy,dx,dy call init : ポストスクリプトを使う準備 call newpath call arrow(0.1, 0.5, 0.9, 0.5, 0.01, 0.0, 1.0) : x 座標 call arrow(0.5, 0.1, 0.5, 0.9, 0.01, 0.0, 1.0) : y 座標 call textx(0.9, 0.5,1,'x') call textx(0.2, 0.5, 2,'-1') call textx(0.8, 0.5, 1,'1') call textx(0.47, 0.5, 1,'0') call texty(0.47, 0.9,1,'y') call texty(0.47, 0.2,2,'-1') call texty(0.47, 0.8,1,'1') n=10 do 100 i=0, n : x 座標にチックを入れる x=0.06*i+0.2 if(i/5*5.eq.i) then dy=0.01 else dy=0.005 end if call line( x, -dy+0.5, x, dy+0.5, 0.0, 1.0, 1) 100 continue do 200 i=0, n : y 座標にチックを入れる y=0.06*i+0.2 if(i/5*5.eq.i) then dx=0.01 else dx=0.005 end if call line( -dx+0.5, y, dx+0.5, y, 0.0, 1.0, 1) 200 continue 109 110 第 12 章 ポストスクリプトによる描画法 call stroke call newpath : ここから2次曲線 x=-1.0 y=x**2 xx=x*0.3+0.5 yy=y*0.3+0.5 call plot(xx,yy,3) do 300 i=0,20 x=real(i)/20.0*2.0-1.0 y=x**2 xx=x*0.3+0.5 yy=y*0.3+0.5 call plot(xx,yy,2) 300 continue call stroke call fin : 図形を出力する stop end このプログラムを実行すると図 12.9 の図形が得られる. y 1 -1 0 1 x -1 図 12.9: x = ;1 1] における y = x2 のグラフ 12.2. 図形の描き方{2 次元グラフィックス 111 第12章 演習問題 問題 1. ポストスクリプトを用いて実線で正方形を描き,その中に正方形に接する円を描くプログ ラムを作れ. 問題 2. 同志社のシンボルマークを描き,その横に `Doshisha 名前をローマ字で書くプログラムを作れ. 問題 3. ポストスクリプトを用いて関数 y = 2x2 2x 3 のグラフを ログラムを作れ . 座標軸 (x 軸と y 軸) も入れること. 問題 4. ファイル `points.dat' に書かれている5点の (x y ) 座標の値 ( 1 x 1, 1 y 1) を 読み込み,ポストスクリプトを用いてそれらの点をなめらかに結ぶ曲線を描くプログラムを 作れ.座標軸 (x 軸と y 軸) も入れること. ; ; University',と書きその下に自分の ;1 x 1 の範囲で描くプ ; ;
© Copyright 2024 Paperzz