FortranとJuliaの文法比較

演算子

FortranとJuliaの演算子その他について比較してみましょう。ほとんど同じですが、一部だけ異なります。

定義FortranJulia
$a+b$a+ba+b
$a-b$a-ba-b
$a \times b$a*ba*b
$a \div b$a/ba/b(実数)、div(a,b)(商(整数))、a÷b(divと同じ。入力は\divで。)
$a \div b$の余りmod(a,b)a % b
$a^b$a**ba^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-46e-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

と書きます。条件比較に使う比較演算子の違いをテーブルにまとめると

FORTRAN77FortranJulia
a .lt. ba < ba < b
a .le. ba <= ba <= b
a .eq. ba == ba == b
a .ne. ba /= ba != b
a .gt. ba > ba > b
a .ge. ba >= ba >= b

となります。複数の条件を使う場合の違いは

定義FortranJulia
aかつba .and. ba && b
aまたはba .or. ba || b

です。

型の対応

Fortranでは変数の型を定義しなければコンパイルエラーが起きますが、Juliaの場合は型を意識せずにコーディングすることができます。一方、内部ではJuliaはやはり型を扱っていますから、Fortran使用者はFortranとJuliaの型の対応を知っておくと理解しやすいでしょう。

種類FortranJulia
倍精度実数real*8real(8)Float64
単精度実数realreal(4)Float32
32ビット符号付き整数integer(機種依存及びコンパイルオプション依存)やinteger(4)Int32
64ビット符号付き整数integer(機種依存及びコンパイルオプション依存)やinteger(8)Int64
倍精度複素数complex*16complex(8)ComplexF64
単精度複素数complexcomplex(4)ComplexF32
論理値logicalBool
文字データcharacterString

配列の型と定義

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もモジュールという機能があります。