ずっとFortran学習してますが、なかなか飽きません。飽きるまで続く気がしています。そして新たな発見。where構文。こんな強力な構文があるとは知りませんでした。
今回はwhereを使っていきます。
whereその1
まずはwhereを使わない例です。doで回して配列aの要素が5より大きかったら、配列bに代入するときに、2倍にするプログラムです。
takk@deb9:~$ cat do.f90 program main integer a(10),b(10) data a /1,2,3,4,5,6,7,8,9,10/ do i=1,10 if (a(i) > 5) then b(i) = a(i) * 2 else b(i) = a(i) end if end do do i=1,10 write(*,*) b(i) end do stop end takk@deb9:~$ gfortran do.f90 takk@deb9:~$ ./a.out 1 2 3 4 5 12 14 16 18 20 takk@deb9:~$
6行目から2倍になってますね。
上のプログラムをwhereを使って作り直すとこうなります。
takk@deb9:~$ cat where.f90 program main integer a(10),b(10) data a /1,2,3,4,5,6,7,8,9,10/ where (a > 5) b = a * 2 else where b = a end where do i=1,10 write(*,*) b(i) end do stop end takk@deb9:~$
いやあスゴイ。なんて簡潔に書けるのでしょうか。
実行するともちろん同じ結果になります。
takk@deb9:~$ gfortran where.f90 takk@deb9:~$ ./a.out 1 2 3 4 5 12 14 16 18 20 takk@deb9:~$
使い方は、whereとend whereの間に配列代入文を挿むだけ。
where 条件 配列代入文 end where
もしくはこのパターン。
where 条件 配列代入文1 else where 配列代入文2 end where
配列代入文以外はだめなのでしょうか。配列代入の代わりにwriteを入れてみます。
takk@deb9:~$ cat where2.f90 program main integer a(10),b(10) data a /1,2,3,4,5,6,7,8,9,10/ where (a > 5) write(*,*) 'HELLO' end where stop end takk@deb9:~$
ビルドエラーとなります。
takk@deb9:~$ gfortran where2.f90 where2.f90:6:27: write(*,*) 'HELLO' 1 Error: Unexpected WRITE statement in WHERE block at (1) takk@deb9:~$
もちろんwhere構文内なので配列代入しかできないのであって、if文の中なら問題ないです。
takk@deb9:~$ gfortran if.f90 takk@deb9:~$ ./a.out HELLO takk@deb9:~$ cat if.f90 program main integer a(10) data a /1,2,3,4,5,6,7,8,9,10/ if (a(6) > 5) then write(*,*) 'HELLO' end if stop end takk@deb9:~$ gfortran if.f90 takk@deb9:~$ ./a.out HELLO takk@deb9:~$
普通の代入はどうでしょうか。
takk@deb9:~$ cat where3.f90 program main integer a(10),b(10) data a /1,2,3,4,5,6,7,8,9,10/ where (a > 5) i = 1 end where stop end takk@deb9:~$ gfortran where3.f90 where3.f90:6:9: i = 1 1 Error: WHERE assignment target at (1) has inconsistent shape takk@deb9:~$
whereの中では、普通の代入もエラーとなりました。
やはり配列代入のみしか書くことはできないようです。
whereその2
whereを使って配列を初期化する方法を考えてみます。
配列を同じ値で初期化する場合は、たとえば1なら、こんな風に書けると思います。
where (a .ne. 1) a = 1 end where
whereのあとの a .ne. 1は常に真でもよかったかもしれませんが、書き方が分からないので、1以外なら1で初期化するようにしています。
実行してみます。
takk@deb9:~$ cat where4.f90 program main integer a(10) where (a .ne. 1) a = 1 end where do i = 1,10 write(*,*) a(i) end do stop end takk@deb9:~$ gfortran where4.f90 takk@deb9:~$ ./a.out 1 1 1 1 1 1 1 1 1 1 takk@deb9:~$
1で初期化できました。
data文で初期化するのとどちらがプログラムサイズが少なくて済むでしょうか。配列数を10000ぐらいにして確認してみます。
まずはdata文を使って静的に初期化した場合。
takk@deb9:~$ cat data5.f90 program main integer a(10000) data a /10000*1/ write(*,*) a(5000) stop end takk@deb9:~$ gfortran data5.f90 takk@deb9:~$ ./a.out 1 takk@deb9:~$ ls -l a.out -rwxr-xr-x 1 takk takk 49136 12月 20 20:34 a.out takk@deb9:~$
簡潔に書けますね。サイズは49136。大きいです。
where文で動的に初期化した場合。
takk@deb9:~$ cat where5.f90 program main integer a(10000) where (a .ne. 1) a = 1 end where write(*,*) a(5000) stop end takk@deb9:~$ gfortran where5.f90 takk@deb9:~$ ./a.out 1 takk@deb9:~$ ls -l a.out -rwxr-xr-x 1 takk takk 9104 12月 20 20:33 a.out takk@deb9:~$
行数は2行増えましたが、こちらも簡潔に書けてる気がします。プログラムサイズは9104。5分の1になりました。
whereその3
前回whereを使って、配列に初期値を格納したのですが、whereを使う意味はまったくありませんでした。
配列代入の意味を勘違いしていたのですが、まずは前回作った初期化のプログラムを見てみます。
takk@deb9:~$ cat where4.f90 program main integer a(10) where (a .ne. 1) a = 1 end where do i = 1,10 write(*,*) a(i) end do stop end takk@deb9:~$
10個の要素すべてが1に初期化されていたので正しく動いてはいたのですが、実はwhereを使う必要はありませんでした。こうすれば良いです。
takk@deb9:~$ cat where6.f90 program main integer a(10) a = 1 do i=1,10 write(*,*) a(i) end do stop end takk@deb9:~$
そうです。a = 1とするだけ。10個の要素すべてが1になります。
実行してみます。
takk@deb9:~$ gfortran where6.f90 takk@deb9:~$ ./a.out 1 1 1 1 1 1 1 1 1 1 takk@deb9:~$
whereはすごい! と糠喜びしてましたが、こうなると、whereって何に使うのだろう? と疑問符。
要素の総和とかできるでしょうか。1~10までの足し算を試みます。
takk@deb9:~$ cat where7.f90 program main integer a(10),s data a /1,2,3,4,5,6,7,8,9,10/ s = 0 where(a > 0) s = s + a end where do i=1,10 write(*,*) a(i) end do stop end takk@deb9:~$
こんなんでいけるでしょうか。ビルドしてみます。
takk@deb9:~$ gfortran where7.f90 where7.f90:8:10: s = s + a 1 Error: Incompatible ranks 0 and 1 in assignment at (1) where7.f90:8:10: s = s + a 1 Error: WHERE assignment target at (1) has inconsistent shape takk@deb9:~$
エラーになりました。スカラー変数への代入はwhere構文内ではできないことを忘れてました。
配列代入しかできないということは、総和もできないってことですね。
コメント