Julia
julia1.0

Julia 1.0の注意点

背景

Julia v1.0 が2018/8/8にリリースされました。
Julia v1.0は、前バージョンであるv0.6からいくつかの変更点があるのですが、
今のところ世の中にある(とくに日本語の)Julia関連のドキュメントはv0.6以前のものが多いため、v1.0で実行しても動かないことがあります。
そこで、Julia v1.0を使うための注意点など、気づいた点を書いてみたいと思います。
実際には、とくにユーザーとして普通に使うだけならv0.6とv1.0の違いは大したことはないので、たいてい、ほんのちょっと変えれば動くのですが、その「ほんのちょっと」が分からないせいでJulianが減ってしまうのは悲しいので。

Julia v0.7のすすめ

すでに、@bicycle1885さんが書かれていますが、Julia v0.7から非推奨(deprecated)の警告メッセージを削除したものがv1.0になります。したがって、機能的には、ほぼ v0.7≒v1.0と考えてよいです。
上で書いたように、Julia v0.7(≒v1.0)は、前バージョンであるv0.6からいくつかの変更点があるわけですが、Julia v0.7では古い(v0.6時代の)書き方でコードを書くと、「この書き方は非推奨(deprecated)になりました。代わりにこう書いて下さい」という警告メッセージを出します。警告メッセージにしたがって新しい書き方に直せばよいわけです。
それに対して、v1.0では、古い書き方をすると、どう直せばいいのかという警告メッセージを出すことなく、単にエラーになってしまいます。

というわけで、とくにv0.6 時代のドキュメントを参考にしてJuliaのコードを書こうという方は、v1.0よりもv0.7を使うほうがよいかもしれません。

Packageのインストール方法

Julia v1.0が出たばかりの頃は、主要なPackageがv1.0では軒並み動作しないということで、Julia公式のフォーラム阿鼻叫喚状態になっていたのですが、1か月たって、主要なパッケージはほぼv1.0対応してきているので、普通に使う分にはv1.0で大丈夫になってきています。
v1.0では新しいPackageマネージャーが導入されて、Packageのインストールが劇的に速くなりました。Packageのインストールは、REPL上で]を押して、Pkgモードに入ってadd Package名とします。

ちなみに、v0.6時代には、Pkg.add("Package名")としていたわけですが、v1.0でやるとERROR: UndefVarError: Pkg not definedとエラーになってしまいます。これは、下に書きますが、多くの標準ライブラリがBaseの外に出されたのにともなってPkgも外出しされたためでして、最初に、Using Pkgとしておけば、Pkg.add("Package名")も動作します。

多くの標準ライブラリが明示的にusingが必要になった

v1.0では、多くの標準ライブラリが、Baseから外に出されました。BaseというのはJuliaを起動したときに自動的に読み込まれるパッケージなわけですが、ここから外に出された標準ライブラリを使うには、using ライブラリ名と明示的に記述する必要があります。

例えば、

  • @printfを使うには using Printfする
  • 線形代数関係の関数など(invdetDiagonalSymmetriceigvals)を使うには、using LinearAlegebra
  • 日付関連の関数(nowDateなど)を使うには、using Date

など、といった具合です。
標準ライブラリの一覧は、公式ドキュメントのStandard Libraryの項にあります。
とにかく、v0.6時代のコードを、v1.0動かしてERROR: UndefVarErrorと怒られたら、using 〇〇が必要な可能性が非常に高いです。(Julia v0.7を使っていれば「この機能を使うには、using 〇〇をしてね」という警告メッセージがでます。)

Scopeルールの変更

v0.6 → v1.0の変更点のうちでハマる可能性が高い罠に、変数スコープの変更があります。この変更はそれ自体は理にかなったものなのですが、REPL(普通にJuliaを起動してJulia>というプロンプトがでている画面)で、インタラクティブにJuliaを使う場合には注意が必要です。

たとえば、REPL上で、1から10までの数字の和を計算しようとして、Forループを使うと

julia> a = 0
       for i in 1:10
         a += i
       end
ERROR: UndefVarError: a not defined

となります。
正しくは、

julia> a = 0
       for i in 1:10
         global a += i
       end
julia> a
55

とする必要があります。

Julia v1.0の変数スコープについての説明がここにありますが、かいつまんで言うと

  1. Juliaには、グローバルスコープとローカルスコープの2種類のスコープがある
  2. for、while、let、関数定義などは新しいローカルスコープを導入する
  3. ローカルスコープはいくらでもネストできる。
  4. ネストされた内側のローカルスコープの中で、外側のローカルスコープで定義された変数を読み書きできる。
  5. ローカルスコープの中では、グローバルスコープで定義された変数を読むことはできるが、(デフォルトでは)書き変えることはできない
  6. ローカルスコープの中で、グローバルスコープの変数を書き換えたい場合には、global 変数名とグローバルスコープの変数を参照するということを明示的に記述する必要がある。

となります。とくに注意が必要なのはルール5でして、REPLでの実行は、つまりグローバルスコープでの実行なので、グローバルスコープで定義された変数aを、for文で導入されたローカルスコープ内で書き換えることができないためにエラーになったわけです。
正しく動作させるには、ルール6にしたがってglobal a += 1と変数aがグローバルスコープで定義された変数であることを明示する必要があります。

別の解決策としては、

julia> let
         a = 0
         for i in 1:10
           a += i
         end
         a
       end
55

のように、ルール3,4にしたがって、letでローカルスコープを導入して、その中でforを使うでもよいです。ネストされたローカルスコープの内側から、外側のローカルスコープ(letで導入されたローカルスコープ)で定義された変数aを読み書きできます。関数定義でもローカルスコープが導入されるので、letで囲む代わりに処理を関数にしてしまうでもよいです。Juliaでは、グローバルスコープで計算しないで可能な限り関数にするというのが鉄則なので(関数化しないとJuliaの強力な最適化機構が効きません)、関数化するほうが真っ当な解決策でしょう。

この新しい変数スコープの仕様は、とくにREPL上では非常に使いにくいということで、issueがあがっているので、おそらく、近いうち(Julia v1.1?)には、REPL上で実行するときだけは特別扱いされて、globalが必要なくなると思われます。
ちなみに、Jupyter上ではJuliaを実行する場合には、すでに、特別扱いがされているので、globalをつけなくても動きます。