FortranとJuliaの文法比較
演算子
FortranとJuliaの演算子その他について比較してみましょう。ほとんど同じですが、一部だけ異なります。
| 定義 | Fortran | Julia |
|---|---|---|
| $a+b$ | a+b | a+b |
| $a-b$ | a-b | a-b |
| $a \times b$ | a*b | a*b |
| $a \div b$ | a/b | a/b(実数)、div(a,b)(商(整数))、a÷b(divと同じ。入力は\divで。) |
| $a \div b$の余り | mod(a,b) | a % b |
| $a^b$ | a**b | a^b |
| $\sin a $, $\cos b$ | sin(a),cos(b) | sin(a),cos(b) |
| $|a|$ | abs(a) | abs(a) |
| $\sqrt{a}$ | sqrt(a) | sqrt(a) |
| $e^a$ | exp(a) | exp(a) |
| $\log a$ | log(a) | log(a) |
| $6 \times 10^{-4}$ (倍精度) | 6d-4 | 6e-4 |
| $z$の複素共役 | conjg(z) | conj(z) |
| ${\rm Re}z$ | dble(z) | real(z) |
| ${\rm Im}z$ | dimag(z) | imag(z) |
| ${\rm Max}(a,b)$ | max(a,b) | max(a,b) |
繰り返しループ
Fortranでは
do i=1,5
end doというdoループが用いられますが、対応するJuliaのループはforを使って、
for i=1:5
endと書きます。
ループの途中で抜けたい場合、Fortranではexitを、Juliaではbreakを使います。
ループの次の反復ステップに行きたい場合には、Fortranではcycleを、Juliaではcontinueを使います。
If文
条件分岐にはどちらもIf文を使います。Fortranでは
if (a > b) then
else if(a < b) then
else
endifのような形で書きますが、Juliaでは
if a > b
elseif a < b
else
endと書きます。条件比較に使う比較演算子の違いをテーブルにまとめると
| FORTRAN77 | Fortran | Julia |
|---|---|---|
a .lt. b | a < b | a < b |
a .le. b | a <= b | a <= b |
a .eq. b | a == b | a == b |
a .ne. b | a /= b | a != b |
a .gt. b | a > b | a > b |
a .ge. b | a >= b | a >= b |
となります。複数の条件を使う場合の違いは
| 定義 | Fortran | Julia |
|---|---|---|
| aかつb | a .and. b | a && b |
| aまたはb | a .or. b | a || b |
です。
型の対応
Fortranでは変数の型を定義しなければコンパイルエラーが起きますが、Juliaの場合は型を意識せずにコーディングすることができます。一方、内部ではJuliaはやはり型を扱っていますから、Fortran使用者はFortranとJuliaの型の対応を知っておくと理解しやすいでしょう。
| 種類 | Fortran | Julia |
|---|---|---|
| 倍精度実数 | real*8やreal(8) | Float64 |
| 単精度実数 | realやreal(4) | Float32 |
| 32ビット符号付き整数 | integer(機種依存及びコンパイルオプション依存)やinteger(4) | Int32 |
| 64ビット符号付き整数 | integer(機種依存及びコンパイルオプション依存)やinteger(8) | Int64 |
| 倍精度複素数 | complex*16やcomplex(8) | ComplexF64 |
| 単精度複素数 | complexやcomplex(4) | ComplexF32 |
| 論理値 | logical | Bool |
| 文字データ | character | String |
配列の型と定義
Fortranは科学技術計算用のプログラミング言語ですから、行列やベクトルを表現する配列の操作が(他の同じ歴史の長いプログラミング言語等よりも)容易です。一方、Juliaも科学技術計算用として作られていますから、当然、配列の操作も容易です。
それでは、まず配列の定義方法について比較してきましょう。Fortranでは配列の定義は
real(8)::d(1:3,1:4)のような形で書きますね(他の書き方は省略)。この場合はdは倍精度実数が要素の2次元配列です。この配列の中身は決まっていませんので、通常は
d = 0d0などと初期化します。Juliaの場合、0で初期化した2次元配列が必要な場合
d = zeros(Float64,3,4)と書きます。もし、Fortranと同じように初期化されていない配列が欲しい場合には、
d = Array{Float64,2}(undef,3,4)と書きます。Array{Float64,2}の2は配列の次元を示し、(undef,3,4)の3,4はそれぞれの次元の要素数を示します。
倍精度複素数の3次元配列が欲しい場合は、Fortranであれば
complex(8)::f(1:2,1:3,1:5)で定義できます。Juliaであれば
f = zeros(ComplexF64,2,3,5)となります。
Fortranではallocatableを使えば
real(8),allocatable::d(:,:)
allocate(d(1:3,1:4))というような形で、実行時に動的に配列の形状を決めることができていました。Juliaのzeros(Float64,3,4)やArray{Float64,2}(undef,3,4)などはこれと同じように動的に配列の形状を決めていると考えても構いません。両者はよく似ています。
Fortranの場合、配列の始まりと終わりを好きなように決めることができました。例えば、
real(8)::d(1:2,3:9)というdを定義すると、1次元目の添字の範囲は1から2、2次元目の添字の範囲が3から9の配列を定義することができます。Juliaではデフォルトではこのような添字の範囲を設定することはできません。しかし、JuliaのパッケージにはOffsetArraysというパッケージがありまして、これを使うことで同じことが可能となります。例えば、上のような配列の場合には
using OffsetArrays
c = Array{Float64,2}(undef,2,7)
d = OffsetArray(c, 1:2, 3:9)とすればdの添字の範囲はFortranと同じになります。なお、OffsetArraysを使って通常の配列cからdを作成する時にメモリのコピーは発生しておりませんので、追加のコストはほとんどかかりません。
出力
Fortranで画面に書き出したい場合には
write(*,*) "a = ",aのように書きます。Juliaでは
println("a = ",a)あるいは
println("a = $a")と書きます。$aと書くと変数を文字列に変えることができます。また、
write(*,*) "a = ",2*aであれば、
println("a = $(2*a)")と書けます。
ファイルに出力する場合、Fortranでは
open(10,file="test.txt")
write(10,*) "a = ",2*a
close(10)のように装置番号を指定してファイルを開きますが、Juliaでは
fp = open("test.txt","w")
println(fp,"a = $(2*a)")
close(fp)と書きます。
入力
テキストファイルtest.txtが
1 3.0000000000000000
2 6.0000000000000000
3 9.0000000000000000
4 12.000000000000000 というデータだとしましょう。このファイルからデータを読み込む場合、Fortranでは、
open(12,file="test.txt")
do i=1,4
read(12,*) j,x
write(*,*) j,x
end do
close(12)とすることで読み込むことができます。Juliaの場合には、
data = readlines("test.txt")
for i=1:4
u = split(data[i])
j = parse(Int64,u[1])
x = parse(Float64,u[2])
println("$j $x")
endで読み込めます。Juliaではこのreadlineを使うやり方の他にも読み込みの方法がありますが、ここでは使い勝手の良いこの方法のみを紹介することとします。
配列へのアクセス
Fortranでは配列aのi,j成分にアクセスするには
a(i,j)と書きますが、Juliaでは
a[i,j]と書きます。 配列のメモリへの格納順はFortranとJuliaでは同じになっており、行列で言うところの列の単位で格納されています(一番左の添字が一番内側)。
配列の一部分へのアクセスもよく似ており、Fortranでは
a(1:4,1:5)となり、Juliaでは
a[1:4,1:5]となります。
関数
Fortranでは返り値が一つある場合にはfunctionを、返り値がない場合にはsubroutineを使っていました。Juliaでは両方ともfunctionです。定義の仕方はいくつか方法があります。例えば、$c=a+b$という計算は
function test(a,b)
c = a+b
return c
end
function test(a,b)
c = a+b
end
test(a,b) = a+bが可能です。return文がない場合には、最後の行がreturnとして返されることになっています。返り値が複数の場合は
function test2(a,b)
c = a+b
d = a*b
return c,d
endのように書きます。
Fortranにおいて、functionを使った場合、左辺に右辺の値が代入する形になります(c = test(a,b))。これはJuliaも同様です。しかし、配列を扱うときは右辺に値を生成するとコピーが発生してしまうので、これを避けるためにはFortranではsubroutineを使っていました。同様のことはJuliaでも可能です。例えば、Fortranで
module m_test
contains
subroutine testsub(c,b)
implicit none
real(8),intent(out)::c(:)
real(8),intent(in)::b
c(1) = b
end subroutine
end module
program main
use m_test
implicit none
real(8)::val(1:4)
val = 0d0
call testsub(val,3d0)
write(*,*) val
end programというコードを考えます。サブルーチンtestsub(c,b)は引数のcの値を変更します。 これをJuliaで書きますと、
function testsub!(c,b)
c[1] = b
end
function main()
val = zeros(Float64,4)
testsub!(val,3)
println(val)
end
main()となります(ここではあえてモジュールを使いませんでした。モジュールについては後述します)。Juliaでは引数の値を変更するときは慣例的に関数名の最後に!をつけます。
モジュール
FortranもJuliaもモジュールという機能があります。