プログラム開発の流れ 始め:課題設定 プログラミング: 問題を解く手順をプログラミング言語で記述 エディタ(emacs, vi など)でソースファイルを作成・編集 コンパイル( a.out を作成): プログラミング言語で書かれたプログラムを機械語に翻訳 コンパイル成功? No Yes 実行( a.out を実行) No 期待する結果が得られたか? Yes 次の作業へ 1 プログラミング言語 FORTRAN:数値計算には有用.旧バージョン(FORTRAN77)と新バージョン(FORTRAN90/95) がある > ifort prog.f (Intel compiler, F90/95 用) > g77 prog.f (GNU compiler, F77 用) > gfortran prog.f (GNU compiler, F90/95 用) C :unix 開発用に作られた.ANSI(American National Standard Institute)規格準 拠が標準 > icc prog.c -lm (Intel compiler) > gcc prog.c -lm (GNU compiler) 数学関数がある場合は 「-lm」 (数学ライブラリの結合)が必要 その他 C++ :C にオブジェクト指向の拡張を加えたもの コンパイラとして,icpc(Intel compiler)と g++(GNU compiler)が利用可能 Java :オブジェクト指向,グラフィックスも強化 Pascal :構造化された(初期の)言語 Perl :スクリプト言語 (1) プログラムの基本構成 FORTRAN program example 宣言文 ...... 実行文(入出力文,代入文,制御文など) ...... end C #include <stdio.h> main() { 型宣言; ...... 実行文; ...... } 2 (2) プログラムの書式 FORTRAN 固定形式 ・1 行に 1 つの命令 ・命令は 7-72 桁に書く ・1-5 桁は文番号用. ・1つの命令が 1 行に書ききれない場合は,継続行(6 桁目に「数字の 0 と空白以外のシンボル」 を入れると,前の行から継続していることを表わす) . ・1 桁目に「c」または「*」または「!」が書かれていると,コメント行.また何も書かれてい ない空行は無視される. ( 「!」を推奨.特に「!」は 1 カラム目でなくても可.その行の「!」以下の記述は無視される. ) ・変数などの名前は,アルファベット,数字および下線「 _ 」の組み合わせで,アルファベッ トで始まる 31 文字以内の文字列(古い FORTRAN では 6 文字以内の制限) .アルファベットの大 文字/小文字は区別されない. FORTRAN 自由形式 ・1 行は 132 桁で,セミコロン(;)で区切ればいくつの命令を書いてもよい. ・命令を書く桁に制限はない(どこに書いても良い) . ・1つの命令が 1 行に書ききれない場合は,継続行(文の末尾に「&」を付けると,次の行に継 続していることを表わす) . ・コメント行を表わすシンボルは「!」のみ.何も書かれていない空行は無視される. ・変数などの名前は,アルファベット,数字および下線「 _ 」の組み合わせで,アルファベッ トで始まる 31 文字以内の文字列(古い FORTRAN では 6 文字以内の制限) .アルファベットの大 文字/小文字は区別されない. C ・命令の末尾には必ずセミコロン(;).複数行にまたがる命令もセミコロンまでが 1 つの命令 と見なされる. ・1行にいくつ命令を書いてもよい. ・コメントは「/*」と「*/」で囲む. ・何も書かれていない空行は無視される. ・変数などの名前は,アルファベット,数字および下線「 _ 」の組み合わせで,アルファベッ トまたは下線「 _ 」で始まる 31 文字以内の文字列.アルファベットの大文字/小文字は区別 される. 3 (3) データの型と宣言文 データの型 ・整数(4 バイト) :-231~231-1 (231=2147483648) ・実数(4 バイト=単精度,8 バイト=倍精度):有効数字は 6-7 桁(単精度)または 14-15 桁(倍 精度) ・複素数(8 バイト=単精度,16 バイト=倍精度) ・文字(列)(1文字=1 バイト) ・論理型:真/偽 FORTRAN ・暗黙の型宣言:i,j,k,l,m,n で始まる名前の変数は整数,それ以外は単精度実数 ・暗黙の型宣言の変更 implicit real*8(a-h,o-z) ! a-h,o-z で始まる変数を倍精度実数とする ・暗黙の型宣言を無効にする(使用する変数名は全て,データの型を指定して宣言しなければな らない.推奨) implicit none ・配列(ベクトル,行列型の変数,dimension 文) :添字は特に指定がなければ 1,2,3.. dimension a(10) ! a(1),a(2),..,a(10)の変数が確保 dimension a(0:9) ! a(0),a(1),..,a(9)の変数が確保 ・定数を与える(parameter 文) ・初期値を与える(data 文) 例(F77) implicit none integer i,j,kdim parameter (kdim=4) real*4 x,y(kdim),pi ! 定数 kdim を使って配列の大きさを与える parameter (pi=3.141592654) data y/1.0,2.0,3.0,4.0/ ! 配列の初期値設定(data 文) real*8 dx,dy(kdim,kdim) complex ca,cunit data cunit/(0.0,1.0)/ ! 複素数の初期値設定 character*8 title logical init 例(F90/95) implicit none integer::i,j integer, parameter::kdim=4 real(4)::x,y(kdim)=(/1.0,2.0,3.0,4.0/) real(4), parameter::pi=3.141592654 real(8)::dx,dy(kdim,kdim) complex::ca,cunit=(0.0,1.0) character(8)::title logical::init=.false. ! 論理型変数に初期値.false.(偽)設定 4 C ・使用する全ての変数に対して,型宣言が必要 ・配列の成分を表わす添字は,0,1,2,... ・定数は#define 文で main 関数の前に定義,または「const」を宣言 ・C に特有の型宣言(unsigined, long=4バイト整数, short=2バイト整数) 例 #include <complex.h> /* 複素数型変数の利用に必要 */ #define KDIM 4 /* 定数には大文字を使うのが慣例 */ main(){ int i,j; float x=1.0,y[KDIM]; /* /* /* /* /* double dx,dy[KDIM][KDIM]; complex ca; complex const cunit=I; char line[100]; unsigned char name; ... x の初期値を与える */ 配列 y として y[0],..,y[3]が確保 */ 2 次元配列の定義 */ 複素数 */ 虚数単位 I は定数(再定義は不可)*/ /* 1バイト符号なし整数(0-255)*/ } 5 (4) 入出力文 ・標準入力(キーボード) FORTRAN read(*,*) i read *, i ! 書式指定なし C(変数名の前に「&」を付ける) scanf(”%d”,&i); ・標準出力(コンソール) FORTRAN write(*,*) i print *, i write(*, ’(i5)’) i print ’(i5)’, i write(*,100) i print 100, i 100 format(i5) ! 書式指定なし ! 書式付き ! 書式付き(format 文) C(書式の最後に「¥n」を付けないと改行されない. 「¥」はバックスラッシュ) printf(”%d¥n”,i); ・標準エラー出力(コンソール) :エラーメッセージを出力 FORTRAN:標準では使われていない write(0,*) ‘something wrong?’ C fprintf(stderr,”something wrong?¥n”); ・ファイル入出力 FORTRAN open(unit=10,file=’input.dat’) ! input.dat というファイルを開き read(10,*) x,y,z ! そこからデータを読込む close(unit=10) ! ファイルを閉じる open(unit=12,file=’output.dat’) ! output.dat というファイルを開き write(12,100) x,y,z ! そこにデータを書く close(unit=12) ! ファイルを閉じる 100 format(3f12.6) 注)「unit=」の後の数値は装置番号.装置番号=5,6 は標準入力と標準出力 6 C FILE *fp; char inbuffer[80]; .... fp=fopen(”input.dat”,”r”); /* ファイル input.dat を読込み用として開く*/ fgets(inbuffer,sizeof(inbuffer),fp); /* 1 行分(80 桁)を読込んで */ sscanf(inbuffer,”%f%f%f”,&x,&y,&z); /* 数値をデータとして格納 */ fclose(fp); /* ファイルを閉じる */ fp=fopen(”output.dat”,”w”); /* ファイル output.dat を書き込み用として開く*/ fprintf(fp,”%f %f %f¥n”,x,y,z); fclose(fp); /* ファイルを閉じる */ 「fgets; sscanf;」の 2 行を「fscanf(fp,”%f%f%f”,&x,&y,&z);」とまとめても可 ・書式の変換:主に出力用 FORTRAN(format 文の括弧内に記述) f12.6 :実数型データ(小数点以下 6 桁,全体で 12 桁) e15.7 :実数型データ(小数点以下 7 桁:-0.1234567e-10) d23.15 :実数型データ(小数点以下 15 桁:-0.123456789012345d-10) i5 :整数型データ(5桁右寄せ) 5x :空白5文字 / :改行 ’aaa’ :文字列(aaa) 例) write(*,100) x,y,z 100 format(’ (x,y,z) =’,3f12.6) write(*,120) (i,r(i),i=1,20) 120 format(i5,’ value = ’,e15.7/) ! do 型並びの例 C(printf, scanf 関数の第一引数(ひきすう)として ” ” で囲んで記述) %12.6f :実数型データ(小数点以下 6 桁,全体で 12 桁) %15.7e : %5d :整数型データ(5桁右寄せ) ¥n :改行 %x :16 進表示 例) printf(”(x,y,z) = %12.6f %12.6f %12.6f ¥n”,x,y,z); scanf(”%d %d %f”,&i, &j, &a); 7 ・入出力のエラー処理(特にファイルからの入力の時) FORTRAN read(10,*,err=100) x ! ファイルの読込みエラーがあれば文番号 100 へ read(10,*,end=200) x ! ファイルの終わりまで来たら文番号 200 へ C if((fp=fopen(”input.dat”,”r”))==NULL) { /* ファイルが開けなければ */ fprintf(stderr,”can’t open file¥n”); /* 標準エラー出力に警告を出力 */ exit(0); /* 終了 */ } while(fgets(inbuffer,sizeof(inbuffer),fp)!=NULL) { /* 1 行が読み込めれば */ sscanf(inbuffer,”%f%f%f”,&x,&y,&z); /* データとして格納 */ } 8 (5) 演算子,組み込み関数,代入文 ・算術演算子 加減乗除 ベキ乗(ax) 剰余 :+, -, *, /(FORTRAN, C 共通,乗除は加減より優先) :a**x (FORTRAN),pow(a,x) (C) :mod(i,j)(FORTRAN) ,i%j (C) ・関係演算子 大きい 大きいか等しい 小さい 小さいか等しい 等しい 等しくない :.gt. :.ge. :.lt. :.le. :.eq. :.ne. ・論理演算子 否定 論理積 論理和 :.not. (FORTRAN)または ! (C) :.and. (FORTRAN)または && (C) :.or. (FORTRAN)または || (C) ・ビット操作 ビット毎の AND ビット毎の OR ビット毎の排他的 OR ビット毎の NOT 注)ビット演算 i (FORTRAN)または (FORTRAN)または (FORTRAN)または (FORTRAN)または (FORTRAN)または (FORTRAN)または > (F90/95, C) >= (F90/95, C) < (F90/95, C) <= (F90/95, C) == (F90/95, C) /= (F90/95) または !=(C) :iand(i,j) (FORTRAN)または i & j (C) :ior (i,j) (FORTRAN)または i | j (C) :ieor (i,j) (FORTRAN)または i ^ j (C) :not (i) (FORTRAN)または ~i (C) j AND OR XOR 0 0 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 1 1 0 ・組み込み関数 sqrt, sin, cos, tan, acos, asin, atan, exp, log, log10 等(FORTRAN, C) ただし,C では「#include <math.h>」が必要(ベキ乗を表わす pow 関数も) 注)man コマンドで必要なインクルードファイルを確認 >man 3 pow 9 ・代入文 「変数 x の値に変数 y の値を足して,変数 x として保存」 ,「変数 x の値として数値1を代入」 など(FORTRAN, C 共通) x=x+y x=1 インクレメント/デクレメント演算子(C) i++; /* i の値を 1 増やす */ i--; /* i の値を 1 減らす */ 数値定数の書き方(C) a=12; a=012; a=0x12; /* 10 進数 */ /* 8 進数 */ /* 16 進数 */ 10 (6) 繰り返し計算と条件分岐 ・単純な繰り返し計算 FORTRAN (i) do ~ end do do i=0,4,1 ..... end do ! i=0-4 の間,i を 1 ずつ増やして ! 「end do」までの間の命令を繰り返し実行 (i-1) ラベルを使って(F90/95) loop1: do i=0,4,1 ..... end do loop1 注)自由形式のみ (i-2) 文番号を付けて(F77) do 5 i=0,4,1 ..... 5 continue (ii) do while ~ end do i=0 do while(i<5) ..... i=i+1 end do C (i) for for (i=0; i<5 ; i++) { ...... } (ii) while i=5; while(i<5) { ..... i++; } (iii) do while i=5; do { ..... i++ } while(i<5); (ii)と(iii)の違いは,繰り返しの条件をチェックする場所 ・条件分岐 (1) FORTRAN(論理 if 文) if(x < 0.0) goto 10 ..... 10 ..... C if(x < 0.0) goto neg; ..... neg: ..... 注)F90/95 ならばラベルを指定してジャンプしても良いが,goto 文の多用はプログラムが わかりにくくなるので推奨されない 11 ・条件分岐(2): if ~ else FORTRAN(ブロック if 文) if(x > 10.0) then ..... else ..... end if ・条件分岐(3): case FORTRAN select case(i) case (1) ..... case (2:3) ..... case default ..... end select C switch (i) { case 1: ..... break; case 2: case 3: ..... break; default: ..... break; } C if(x > 10.0) { ..... } else { ..... } ! i=1 ! i=2,3 ! それ以外 /* i=1 */ /* i=2,3 */ /* それ以外 */ ・繰り返しループからの脱出 FORTRAN(F77) do 10 i=1,10 if(x>10.0) go to 10 if(x>20.0) go to 20 ..... 10 continue 20 continue ! 次の i へ ! 繰り返し計算をやめる FORTRAN(F90/95) do i=1,10 if(x>10.0) cycle ! 次の i へ 12 ! 繰り返し計算をやめる if(x>20.0) exit ..... end do C for(i=1,i<11,i++) { if(x>10.0) continue; if(x>20.0) break; ..... } /* 次の i へ */ /* 繰り返し計算をやめる */ ・エラー検出によるプログラムの終了 FORTRAN if(error) stop C if(error) exit(0); 注)多重ループ ループの入れ子は許される do i=1,10 —————─—┐ do j=1,10 ─——┐ │ ....... │ │ end do ──—─┘ │ end do ──────┘ 交差したループは禁止 do 10 i=1,10 ─────┐ do 20 j=1,10 ───┐ │ ....... │ │ 10 continue ───—─┼─┘ 20 continue ──—──┘ 注)古い形式の FORTRAN で使われている条件分岐(使用は推奨されない):算術 if 文 if(x) 10, 20, 30 ! x<0,x=0,x>0 にしたがって文番号 10,20,30 へ 13 (7) 副プログラムと変数の受け渡し ・まとまった作業を独立した副プログラムとしてまとめる >基本となる作業を繰り返し行なうときに,コードの重複が避けられる. ・全体の作業を基本的な作業の組み合わせとして設計し,基本的な作業を請け負う副プログ ラムをパッケージ化 >個々の副プログラムの開発を分担=開発の効率化 >障害の原因の発見が容易になる=保守の効率化 >1つの副プログラムをいくつもの作業に利用=プログラムの資産化 ・引数=プログラム間でデータを受け渡しするインターフェース FORTRAN(型宣言の形式は F90/95) 例) program main implicit none real::x,y real::func ! 関数(値)の型宣言(必須) ..... call task(x,y) ! サブルーチンプログラムの呼び出し(x,y=「(実)引数」) x=func(y) ! 関数副プログラムの呼び出し(y=「(実)引数」) ..... end subroutine task(a,b) implicit none real::a,b real::local_x local_x=b b=0.0 if(a<0.0) return a=local_x**2 end ! サブルーチン文(a,b=「(仮)引数」) function func(z) implicit none real::z,func func=z**2 end ! ファンクション文(z=「(仮)引数」) ! 引数の型宣言 ! ローカル変数(副プログラム中だけで有効)の宣言 ! 途中で主プログラムに戻る ! 引き数,関数値の型宣言が必要 ・主プログラム(program 文で始まる全体の作業を記述するプログラム)中で引数に値を代 入し,副プログラムに渡し,副プログラムではその値を使って必要な作業を行ない,結果 を引数に代入し,メインプログラムに戻す. ・副プログラム中だけで使用する作業用変数(ローカル変数)の値は,保存されない(再度, 副プログラムが呼び出されたときに,前の値は残っていない).この値が保存されるように するためには 14 real, save:: local_x ! SAVE 属性をもった実数型データ(F90/95) real local_x save local_x ! 実数型の宣言(F77) ! local_x を save(保存)することを宣言(F77) または ・引数が入力用/出力用を明示(副プログラム中で) :INTENT 属性(F90/95) real, intent(in)::y ! 入力用(値を変更しようとするとエラー) real, intent(out)::x ! 出力用 real, intent(inout)::x ! 入出力用 ・引数を用いないでデータを受け渡しする方法 (i) common を使う(F77,F90/95) program main implicit none real::x,y common/dataset/x,y ! dataset という名前の common 領域を確保 ...... end subroutine task implicit none real::x,y common/dataset/x,y ...... end ! dataset という名前の common 領域を共有 (ii) module/use を使う(F90/95): module は use より前に置く module dataset ! dataset という名前の module を定義 real::x,y end module program main use dataset ...... end subroutine task use dataset ...... end ! dataset という名前の module を使用 ! dataset という名前の module を使用 15 C 例) #include <stdio.h> #define NLIMIT 100 float global_x; /* グローバル変数の宣言 */ float task (float x); { float local_x; local_x = x; return local_x*global_x; } /* 関数 task の定義と引数の型宣言 */ /* 戻り値(関数値)の宣言(実数)も*/ /* ローカル変数の宣言 */ /* 戻り値を与える */ /* main に戻る */ main() { float x,y; y=task(x); ..... } /* 関数 task の呼び出し */ ・副プログラムの形式は全て主プログラムと同じ「関数」 ・ユーザ定義の関数は main 関数で呼び出す前に記述 ・main 関数やその他の関数でデータとして共有できる変数(グローバル変数)の宣言は全て の関数の定義の外側に書く ・ローカル変数の値は保存されない.保存するためには static を宣言 static float local_x; ・関数の中で引数の値を再定義しても,呼び出し側ではその変更は反映されない(FORTRAN との大きな相違点) 16 (8) 文関数と内部手続き(FORTRAN) 一つの文で書けるような簡単な関数は「文関数(statement function)」で定義出来る (F77,F90/95). program example implicit none real::x,sfun ! 文関数と変数の型宣言 sfun(x)=sin(x)/x ! 文関数の定義は全ての実行文の前に ...... end ただし,文関数は通常の代入文と見分けがつきにくいことなどから推奨されない. F90/95 では文関数のかわりに「内部手続き」が利用出来る.内部手続きは主プログラムの 全ての実行文の後ろに「contains 文」の後に通常の副プログラムと同様の様式で書かれる. program example implicit none ...... contains ! この後ろに内部手続き(サブルーチン/関数)が function sfun(x) ! 書かれる real::x,sfun sfun=sin(x)/x end function sfun end program ! 「program」と対になる「end」 内部手続きは「module」の中に書いて,複数のプログラムで使うことも可能. 17 (9) 配列の扱い(FORTRAN) 数値計算では配列型のデータが有用である.特に F90/95 では配列型データの扱いが強化さ れた. ・配列型のデータはメモリ上で連続した領域に格納される.2 次元以上の配列でデータが収 められる順番は,a(3,3)の配列の場合 a(1,1), a(2,1), a(3,1), a(1,2), a(2,2), a(3,2), a(1,3), a(2,3), a(3,3) となる(前に置かれた引数(添字)が先に動く) .したがって do j=1,n do i=1,n a(i,j)=b(i,j) end do end do のように前の引数に関する do ループが内側に来る方が効率的.また副プログラムとの間でデ ータをやり取りする際は program main integer,parameter::Ndim=8 real::a(Ndim,Ndim) ..... call sub(a, Ndim) ..... end subroutine sub(b,N) integer::N real::b(N,*) ... のように,1 番目の配列の大きさ(行の数)を合わせておかないと行列(2 次元配列)の構造 が保たれないので注意(列の数はいくつあってもよい).ただしこの点は F90/95 では interface 構文で解決出来る. program main interface subroutine sub(a) real, intent(in)::a(:,:) end subroutine end interface integer,parameter::Ndim=8 real::a(Ndim,Ndim) ..... call sub(a) ..... end 18 subroutine sub(b) real, intent(in)::b(:,:) ... ・配列代入と配列演算(F90/95) :3 つの配列 a(1:10), b(1:10), c(1:10)に対して a=b ! 成分毎に代入(a(i)=b(i) (i=1,10)) a(1:5)=b(6:10) ! a の第 1-5 成分に b の第 6-10 成分を代入 a(6:10)=c(1:10:2) ! a の第 6-10 成分に c の 1,3,5,7,9 成分を代入 a=sqrt(abs(a)) ! a の各成分にその sqrt(絶対値)を代入 a=b*c ! 成分毎の積(a(i)=b(i)*c(i) (i=1,10)) ・where 文と配列関数(F90/95) where(a>100) a=100 x=dot_product(a,b) a=matmul(b,c) a=transpose(b) x=count(a>0) x=size(a) x=sum(z) ..... ! ! ! ! ! ! ! a の各成分で 100 を越えるものは 100 にする 1 次元配列(ベクトル)a,b の内積 2 次元配列(行列)b,c の積 2 次元配列(行列)b の転置 配列 a の正の要素の数 配列 a の要素の数 配列 a の要素の和 19 (10) 構造型(F90/95)/構造体(C) 整数型,実数型,複素数型,文字列等の複数のデータをひとまとめにした複合体のデータ 型を定義することができる. FORTRAN type vec2d real::x,y end type vec2d ! 構造型 vec2d の定義 type(vec2d)::a(32) ! vec2d 型の配列 a を定義 dist=sqrt(a(1)%x**2+a(1)%y**2) ! vec2d 型データの成分の参照 構造型の定義は「module」の中に書いて,複数のプログラムで使うことも可能. C /* 構造体 vec2d の定義 */ struct vec2d { float x; float y; } a[32]; /* 構造体定義とともに変数も定義 */ struct vec2d pt; /* 変数 pt の定義 */ dist=sqrt(a[0].x*a[0].x+a[0].y*a[0].y); /* 成分の参照 補)C に特有な構造型 (i) 共用体 union ulist { char name[8]; int ind; }; */ /* 共用体 ulist の定義 */ union ulist my; /* 共用体 ulist の型の変数を定義 */ strcpy(my.name,”abcde”); printf(%d¥n”,my.ind); /* 文字列の代入 */ /* 整数型データとしてプリント */ 変数 my は,文字データ(my.name)としても,整数型データ(my.ind)としても利用可 (ii) 列挙型 enum eRIKOU {math,phys,civil,mech,elec,chem,sys,info}; /* 列挙型 eRIKOU を定義 */ 20 enum eRIKOU dept; /* 列挙型 RIKOU の変数を定義 */ dept = phys; printf(”%d¥n”,dept); /* dept=math,phys,civil...に */ /* 整数値 0,1,2,...が対応 */ switch (dept) { /* dept を利用した条件分岐 */ case math: printf(”数学科¥n”); break; ..... } 21 (11) ポインタ(F90/95, C) プログラムの中で使われる変数はメモリ上の決まった場所に記憶される.この記憶場所の 位置をメモリ上のアドレスと呼ぶ.アドレスは 1 バイトを単位として先頭の 0 番地から順に 番号を付けて表わすことにして,通常のコンピュータではこの番地を 4 バイト(32 ビット) の整数(0~232-1 )で表現する(32 ビットマシンと呼ばれるのはこのため).プログラムで 使われている変数がメモリ上のどの番地にあるかを示す変数を「ポインタ」と呼ぶ.このポ インタを上手く使えば変数や配列を効率よく参照することができる. FORTRAN real,pointer::ptr real,target::x ..... ptr=>x x=ptr ptr=1.0 ! 実数型データを指し示すポインタ ptr を定義 ! ポインタで指し示される実数型変数を定義 int *ptr; int val1,val2; ..... ptr = &val1; val2 = *ptr; /* 整数型データを指し示すポインタ ptr を定義 */ ! 変数 x の番地をポインタ変数 ptr に代入 ! ptr 番地に書かれたデータを変数 x に代入 ! ptr 番地のデータとして 1.0 を格納 C /* 変数 val1 のアドレスを ptr に代入 */ /* ptr 番地に書かれたデータを val2 に代入 */ 22
© Copyright 2024 Paperzz