Juliaという言語がある。Amazon.co.jpでぐぐると18禁ページにとばされそうになってびっくりする。
JuliaboxというサイトでJupyter ノートブックで使える。言語としてはRubyとかPythonとかのようなスクリプト言語のように見えるが実はなかなかハードコアで、動的に特定の型に特化されたバージョンがJITでコンパイルされて動作するのでかなり速い、らしい。
結構特徴的な言語仕様なので、面白いところを紹介する。
1オリジン
Juliaはコンピュータサイエンティスト向けというよりは数学者向けの言語で、その証拠に、配列の添字が1で始まる。配列の添字が1で始まる言語はRとかその前身のSとか、古のFortranとか、APLとか、数学者向けなのだ。コンピュータ屋にとっては配列のインデックスはベースアドレスに足す数なので、1オリジンとか気が狂ってるように見えるのだけど、数学者にとっては「0番目の要素」とか言われるとやっぱり気が狂っているように見えるのだろう。
> a = [1,2,3]
> a[1]
1
これがPythonだと当然下のようになる。なれるまでは時間かかりそう。
> a = [1, 2, 3]
> a[1]
2
ans
REPLの中ではansという変数が特殊な変数扱いになっていて、直前の入力の評価結果になる。
julia> 1+1
2
julia> ans
2
スクリプトとして使うときには当然使えない。
係数の書き方
数学だと2x
と書くと2かけるx
という意味になり、係数が省略できるが、一般のプログラミング言語では、2 * x
のように掛け算であることを明示する必要がある。ところがJuliaでは2x
のように直接書くことができる。スペースを開けるとエラーになる。数字で始まる変数名は許されないのでパーズできるのだろう。変数名がe
とかだと指数表記との区別が結構面倒なコーナーケースがありそうだけど問題ないのだろうか。
試してみたら案の定、結構面倒。
> x = 2
> 2x+1
5 # 2 * 2 + 1
だが、
> e = 2
> 2e+1
20 # 2 * 10 ^ 1
となる。。。数値の後にe+
もしくはe-
がくるとパースを変えるようだ。割にアドホック。
変数名としてeを使ってはいけない、ということだな。
変数名
任意のユニコード文字が変数として使える。それは今どきの言語としては別に珍しくもないが、JuliaのREPLにはlatexライクなギリシャ文字変換機能がついているので、かな漢字変換の無い環境のユーザにも使いやすくなっている。例えばπであれば\pi
とうってからtab でπ
に変換される。
ちなみにπ
には円周率(の近似値)がはじめから入っている。便利だ。
julia> π
π = 3.1415926535897...
julia> sin(0.5π)
1.0
変数名は些細な話のようにも思えるけど、数式を引き写すときにはかなり便利っぽい。μ
とかσ
とかをそのまま使えるのはよい。。
REPLで変換できる文字のリストがここにある。恐ろしい数が定義されているけど、区別がつかないものも多数。。
有理数
分子と分母の2つの整数で表す有理数もサポートされている。
python の fractions と同じような機能だが、言語で直接サポートされているので、ずっと書きやすい。//
で区切って書く。
julia> 1//3 * 3
1//1
有理数と浮動小数点を足すと浮動小数点になる。
Julia> 1//3 * 3.0
1.0
有理数と整数の演算は、有理数のまま。
julia> 1//3 * 3
1//1
分子部分を取り出すにはnumerator
、分母部分を取り出すにはdenominator
を用いる。
julia> numerator(2//3)
2
julia> denominator(2//3)
3
複素数
Juliaは複素数をネイティブでサポートしている。算術演算系の演算子も複素数をサポートしている。すごい。リテラルとしては虚数単位を予約変数im
で書く。
julia> typeof(1 + 1im)
Complex{Int64}
julia> typeof(1 + 1.0im)
Complex{Float64}
算術演算子が複素数をサポートしているので、例えばオイラーの公式の左辺をこんなふうに書くことができる。 ℯ
はe
ではなくて、自然対数の底を表すいわゆるネイピア数で、π
同様もともと定義されている。REPLからだと\euler + tab で出すことができる。
julia> f(θ) = ℯ^(im * θ)
f (generic function with 1 method)
julia> f(π)
-1.0 + 1.2246467991473532e-16im
うーん素晴らしい。
ちょっと面白いのはsqrt
の挙動。sqrt(-1)
は実数の範囲には解がないので、エラーが出る。でも、複素数なら解があるのでちゃんと動く
julia> sqrt(-1.0)
ERROR: DomainError with -1.0:
sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
Stacktrace:
[1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31
[2] sqrt(::Float64) at ./math.jl:492
[3] top-level scope at none:0
julia> sqrt(-1.0 + 0.0im)
0.0 + 1.0im
リテラルとしてではなく複素数を作るにはcomplex
関数を用いる。実部、虚部を取り出すにはそれぞれreal
、imag
を、共役数を得るにはconj
を用いる。angle
は実軸からの回転角を返す。
julia> c = complex(1.0 + 1.0im)
1.0 + 1.0im
julia> real(c)
1.0
julia> imag(c)
1.0
julia> conj(c)
1.0 - 1.0im
julia> angle(c)
0.7853981633974483
割り算系演算子
ちなみに、Python3では//
は商の整数の部分を表す。整数同士の割り算が浮動小数点を返すという仕様のせい。
>>> 10 / 3
3.3333333333333335
>>> 10 // 3
3
実はJuliaも同じように、整数の割り算結果が浮動小数点で返される。では//
に当たる演算子はどうなっているのかというと、なんと÷
を使う。これは\div + tab
で出せる。
julia> 10 / 3
3.3333333333333335
julia> 10 ÷ 3
3
julia> 10 % 3
1
このように普通のキーボードで直接打てない演算子は他にもいくつかあるようだ。昔あったAPLはおかしな記号だらけだったけど、それに近いものを感じる。。しかし、REPLではlatex記法で打てるにしても、エディタでプログラムを書く場合にはどうやって出すんだろうか。われわれ日本人はIMEでなんとかなるが。
さらに!\
という演算子も定義されている。a \ b
は b / a
と等価。これいるかなあ。。
julia> 10 \ 3
0.3