Julia入門 配列

引き続き、Julia入門。
本日は配列について。型の話が避けられなかったので、ちょっとだけその話もする。

配列の宣言

julia> a = [1, 2, 3]
3-element Array{Int64,1}:
 1
 2
 3

julia> arr = ["this", "is", "an", "array"]
4-element Array{ASCIIString,1}:
 "this" 
 "is"   
 "an"   
 "array"

julia> arr[4]
"array"

julia> arr[1]
"this"

上記のように、[ ] で作る。
非常に重要なポイントとして、配列の最初のインデックスは0ではなくて1。

REPLからの出力に出ているが、、初期化の際の内容によって、何の配列なのかが決まる。
1番目の例では整数であるInt64、2番目の例では文字列のASCIIString型になっている。

Juliaでの文字列はASCII文字列のASCIIStringと他バイト文字のUTF8Stringで型が異なるため、上記のarrに日本語を追加するとエラーになる。

julia> push!(arr, "ゆにこーど")
ERROR: invalid ASCII sequence
 in convert at /opt/homebrew-cask/Caskroom/julia/0.3.8/Julia-0.3.8.app/Contents/Resources/julia/lib/julia/sys.dylib
 in push! at array.jl:460

日本語を追加する予定があるのであれば、宣言のときにUTF8String[]という形式で、型を指定してあげればよい。

julia> utf8_array = UTF8String["new", "utf8string", "array"]
3-element Array{UTF8String,1}:
 "new"       
 "utf8string"
 "array"     

julia> push!(utf8_array, "日本語でおk")
4-element Array{UTF8String,1}:
 "new"       
 "utf8string"
 "array"     
 "日本語でおk"    

どんな型でも入る配列が希望であれば、Any型というものがある。

julia> any_array = Any[123, "数字", 1.54]
3-element Array{Any,1}:
 123    
    "数字"
   1.54 

julia> typeof(any_array)
Array{Any,1}

julia> for val = any_array
           println(val, " ----> ", typeof(val))
       end
123 ----> Int64
数字 ----> UTF8String
1.54 ----> Float64

上記のようにtypeof関数を使えば、対象の変数の型を調べることができる。

基本的なメソッド

julia> arr = UTF8String["a", "b", "c"]
3-element Array{UTF8String,1}:
 "a"
 "b"
 "c"

julia> length(arr)     # 長さ
3

julia> push!(arr, "d")    # エレメントの追加
4-element Array{UTF8String,1}:
 "a"
 "b"
 "c"
 "d"

julia> push!(arr, "e", "f")     # 複数のエレメント追加
6-element Array{UTF8String,1}:
 "a"
 "b"
 "c"
 "d"
 "e"
 "f"

julia> new_arr = ["x", "y", "z"]
3-element Array{ASCIIString,1}:
 "x"
 "y"
 "z"

julia> append!(arr, new_arr)    # 配列の結合
9-element Array{UTF8String,1}:
 "a"
 "b"
 "c"
 "d"
 "e"
 "f"
 "x"
 "y"
 "z"

julia> insert!(arr, 7, "G")    # 指定したインデックスにエレメントを追加
10-element Array{ASCIIString,1}:
 "a"
 "b"
 "c"
 "d"
 "e"
 "f"
 "G"
 "x"
 "y"
 "z"

そのほかのメソッドこちらを参照のこと。
なお、上記のpop!, insert!, append! など、ビックリマークがついているとおりどれも破壊的な関数で、指定した配列自体が変更される。

ちょっと関数型っぽくないが、Julia全体として、あまりイミュータビリティにはこだわりがないというか。
上記の関数に非破壊的なバージョンもないし。なんとなくそういうポリシーだと思っておくと良いと思う。

2次元配列

これまたちょっとトリッキーなところです。

julia> array = [[1, 2], [3, 4], [5, 6]]
6-element Array{Int64,1}:
 1
 2
 3
 4
 5
 6

直感的に上記のようにかくと、すべて連結されてただの1次元の配列になる。
シンプルな2次元配列の宣言と初期化は以下の通り。

julia> array = [1 2; 3 4; 5 6]
3x2 Array{Int64,2}:
 1  2
 3  4
 5  6

julia> array[2,2]
4

Juliaでは、「2次元配列」と「配列の配列」は全く異なるものであることを覚えておこう。
配列の配列を宣言したければ、以下の通り。

julia> array_of_array = Array{Int64}[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
3-element Array{Array{Int64,N},1}:
 [1,2,3]
 [4,5,6]
 [7,8,9]

julia> array_of_array[3][2]
8

3次元以上の配列

3次元以上の配列はリテラルでは宣言と同時に初期化はできない。
Array(T, d1, d2, d3) という形式で宣言し、部分ごとに割り当てる。

julia> three_d = Array(UTF8String, 2, 2, 3)
2x2x3 Array{UTF8String,3}:
[:, :, 1] =
 #undef  #undef
 #undef  #undef

[:, :, 2] =
 #undef  #undef
 #undef  #undef

[:, :, 3] =
 #undef  #undef
 #undef  #undef

jjulia> three_d[:, :, 1] = UTF8String["the" "first"; "2x2" "array"]
2x2 Array{UTF8String,2}:
 "the"  "first"
 "2x2"  "array"

julia> three_d[:, :, 2] = UTF8String["the" "second"; "2x2" "array"]
2x2 Array{UTF8String,2}:
 "the"  "second"
 "2x2"  "array" 

julia> three_d[:, :, 3] = UTF8String["the" "third"; "2x2" "array"]
2x2 Array{UTF8String,2}:
 "the"  "third"
 "2x2"  "array"

julia> three_d
2x2x3 Array{UTF8String,3}:
[:, :, 1] =
 "the"  "first"
 "2x2"  "array"

[:, :, 2] =
 "the"  "second"
 "2x2"  "array" 

[:, :, 3] =
 "the"  "third"
 "2x2"  "array"

julia> three_d[1, 2, :]
1x1x3 Array{UTF8String,3}:
[:, :, 1] =
 "first"

[:, :, 2] =
 "second"

[:, :, 3] =
 "third"

上記のように:(コロン)は対象の次元のデータすべて、という意味になる。

数字の場合には、上記の形式で宣言すると初期化されていない状態(変な数字が入る)になってしまうので、zeros関数で宣言するのが良いと思う。

下の例は各次元が2ずつの4次元配列を宣言する例。

num_array = zeros(Float64, 2, 2, 2, 2)
2x2x2x2 Array{Float64,4}:
[:, :, 1, 1] =
 0.0  0.0
 0.0  0.0

[:, :, 2, 1] =
 0.0  0.0
 0.0  0.0

[:, :, 1, 2] =
 0.0  0.0
 0.0  0.0

[:, :, 2, 2] =
 0.0  0.0
 0.0  0.0

例えば640x480xRGBA各8bitの画像データを配列で表現した時に一番上の列を全部255にしたかったら以下のような形。
ちなみにrandでzerosやArrayと同じ形式で、ランダムな数字の入った配列を宣言できる。
2行目のfillで、指定した形を1番目の引数で埋めた配列を作れる。
(pythonで[255] * 16 とやっていたのを、juliaではfill(255, 16) と書く)

julia> vga_rgba_data = rand(UInt8, 640, 480, 4)
(略)
julia> vga_rgba_data[:, 1, :] = fill(255, 640, 1, 4)
640x1x4 Array{Int64,3}:
(略)

そのほか

内包表記

pythonと同様に書ける

julia> [i for i in 1:16]
16-element Array{Int64,1}:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16

Vector, Matrix

Vector, Matrixという二つの型があるのだけど、それぞれ1次元配列、2次元配列の別名である。

julia> Vector{Any} == Array{Any, 1}
true

julia> Matrix{Any} == Array{Any, 2}
true

いじょう。

Julia入門 条件分岐・複文・ループ

前回の記事に引き続き、Juliaの入門というか、一通りの機能を触りながら記事にしていきたいと思う。

ちなみにJuliaは型(Type)システムがコンセプトのなかで非常に重要なのだけど、そこそこハードな内容になりそうなので、あとで記事にまとめるまではあまり使用せずに説明していく。*1

nullbyte.hatenablog.com

ちなみに対象としているバージョンは

$ julia --version
julia version 0.3.8

です。

if文

if i % 15 == 0
    println("fizzbuzz")
elseif i % 5 == 0
    println("buzz")
elseif i % 3 == 0
    println("fizz")
else
    println(i)
end

if文は前回紹介した通り、上記のような形になる。

ちなみに、&& (AND演算)や || (OR 演算)を用いて、以下のような書き方もできる。
条件分岐のよりJuliaらしい書き方としてよく使われるようである。
この書き方の方がより簡潔になる場合は、採用するとよいと思う。

julia> a = 1
1

julia> a == a && print("OK")
OK
julia> a != a || print("NG")
NG

FizzBuzzをこの方法で書き直すと、以下のようにcontinueをつけないといけない。
若干イマイチ。素直にif ~ elseifでつなげた方がベターかと思う。

for i in 1:15
    i % 15 == 0 && (println("fizzbuzz"); continue)
    i % 5 == 0 && (println("buzz"); continue)
    i % 3 == 0 && (println("fizz"); continue)
    println(i)
end

ちなみに&&の右辺が複文になる場合、カッコがないとセミコロンから先が左辺の結果とは無関係に評価されるので注意。

複文(Compound Expression)

上記のcondition && expr .....のようなコードがより複雑になってくると、一行で書くのが厳しくなってくると思う。
そんな時は以下のbegin ~ end構文を利用するとよい。

true && begin
            do_something()
            do_otherthing()
            do_yetotherthing()
        end

以下のように=でつなげると、beginブロックで最後に評価された式の結果が、1行目のtotalに代入される。

julia> total = begin
                   price = 2980
                   tax = 1.08
                   price * tax
               end
3218.4

ちなみにbeginはスコープを作らない。つまり、beginの内側で定義した変数は外側でも使える(外側に影響をあたえる)ので注意。*2

ブロック内に変数の影響範囲を留めたい場合は、localつきで宣言すれば良い。

julia> begin
           val = "dirty data"
           local localval = val
       end
"dirty data"

julia> val
"dirty data"

julia> localval
ERROR: localval not defined

ループ

ループにはforとwhileの2種類がある 。

julia> for i in 1:5
           print(i, ", ")
       end
1, 2, 3, 4, 5, 

julia> i = 1     # MUST!
1

julia> while i <= 5
           print(i, "->")
           i += 1
       end
1->2->3->4->5->

上のコードでi = 1とwhile文の前で定義している事に注意。そうしないと、while文でnot definedのエラーが発生する。

また、for文についてはC言語チックな"for (int i = 0; i < 10; i++) { }" という構文はない。
あくまでも上記のようにIteratableなオブジェクト(配列など)の中身を走査しながら変数に代入していく形になる。
*3

また、以下のように、二つの変数を同時にfor文に指定することができる。
この場合、同時に進むのではなく、全部の組み合わせが実行される。
ここは他の言語であまりみない動きだと思うので、注意。

julia> for i = 1:3, j = 1:3
         print(i, " - ", j,  "\n")
       end
1 - 1
1 - 2
1 - 3
2 - 1
2 - 2
2 - 3
3 - 1
3 - 2
3 - 3

また、for i in 1:5の代わりにfor i = 1:5 というふうに=で書けて、同じ意味だということもおさえておこう。

2つの配列を同時に走査したければ、以下のようにzip関数を使えばできる。

julia> for (i, j) in zip(1:3, 6:9)
         print(i, " - ", j,  "\n")
       end
1 - 6
2 - 7
3 - 8

参考リンク

以下のオフィシャルのDocumentationを参考にした。

続きはまた明日・・・。

*1: ちょっとしたプログラムなら、面倒な型宣言を気にせずコーディングできるのは、Juliaのとっつきやすさでもある

*2:function, while, for, try, catch, finally, let, type はレキシカルスコープを作る

*3:例えば上記の"1:5"の部分は、Rangeオブジェクトという、1から5までを表す配列状のオブジェクトになっている。

Juliaことはじめ

Juliaについて

ここ最近Alternative RもしくなNext Pythonってな感じで、統計・機械学習などの科学計算界隈で特に見かけることが多くなった言語 、Julia

メインストリームのプログラミング言語と比べると特徴・違和感がを感じるポイントが触れば触るほど出てくるのだけど、メリットとしては

  • LLVMベースのJITコンパイル→場合によってはC / Java並みに速い
  • Matlab / R 風の行列計算のネイティブサポート
  • ノードをまたいだ並列計算の言語レベルサポート
  • 型チェック機構
  • C, Fortran, Pythonのライブラリを直接呼べる

といったところが挙げられると思われる。

marui.hatenablog.com

上記のサイトによれば、Juliaの作者は以下のような目標を掲げて開発を始めたようです。

ゆるいライセンスのオープンソースで、Cの速度とRubyの動的さが欲しい。Lispのような真のマクロが使える同図象性のある言語で、Matlabのように分かりやすい数学の記述をしたい。Pythonのように汎用的に使いたいし、Rの統計処理、Perlの文字列処理、Matlab線形代数計算も要る。シェルのように簡単にいくつかのパーツをつなぎ合わせたい。チョー簡単に習えて、超上級ハッカーも満足する言語。インタラクティブに使えて、かつコンパイルできる言語が欲しい。
(そういえば、C言語の実行速度が必要だってのは言ったっけ?)
こんなにもワガママを言った上だけど、Hadoopみたいな大規模分散コンピューティングもやりたい。もちろん、JavaXMLで何キロバイトも常套句を書きたくないし、数千台のマシンに分散した何ギガバイトものログファイルを読んでデバッグするなんて論外だ。幾層にも重なった複雑さを押しつけられるようなことなく、純粋なパワーが欲しい。単純なスカラーのループを書いたら、一台のCPUのレジスターだけをブン回す機械語のコードが生成されて欲しい。A*Bと書くだけで千の計算をそれぞれ千のマシンに分散して実行して、巨大な行列の積をポンと計算してもらいたい。
型だって必要ないなら指定したくない。もしポリモーフィックな関数が必要な時には、ジェネリックプログラミングを使ってアルゴリズムを一度だけ書いて、あとは全ての型に使いたい。引数の型とかから自動的にメソッドを選択してくれる多重ディスパッチがあって、共通の機能がまったく違った型にも提供できるようにして欲しい。これだけのパワーがありながらも、言語としてシンプルでクリーンなものがいい。

これが本当なら最強だろ。ということで触り始めてみた。

表面をさらっと触る分にはごく普通の言語っぽいのだが、少し深堀りを始めると、メインストリームのPython / JS / Rubyみたいな言語を普段扱っている身からすると色々と「え!?」となる部分が出てくる出てくる。

というわけで、少しずつ解説していきたい。

インストール / 実験環境

Julia自体はHomebrewで入れる。

brew cask install Caskroom/cask/julia

インストールできたら

julia

インタラクティブシェルが起動する。

f:id:nullbyte:20150527005940p:plain

Hello, Julia

println("Hello, Julia!")

特に悩むことはなくて、上記の通り

FizzBuzz

julia> function fizzbuzz(n)
    for i in 1:n
        if i % 15 == 0
            println("fizzbuzz")
        elseif i % 5 == 0
            println("buzz")
        elseif i % 3 == 0
            println("fizz")
        else
            println(i)
        end
    end
end 

fizzbuzz (generic function with 1 method)

julia> fizzbuzz(20)
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
16
17
fizz
19
buzz

関数定義はfunction。if分は if / elseif / else。
ブロックはendで締める必要があり、インデントは強制されない。

julia> print("foo"); print("bar");
foobar

コロンで区切れば1行に複数行かける。

julia> is_odd(n) = if n % 2 == 1 true else false end
is_odd (generic function with 1 method)

julia> is_odd(3)
true

上記の通り、関数を1行で定義する方法もあり。
もっと簡単な例では

add(x, y) = x + y

よりJuliaっぽい部分

次回以降書く。

iPython notebook + R (IRKernel) をセットアップする

iPython notebook + IRKernelでiPythonでrを動かす手順。
そこそこ面倒だし、若干不安定(ggplot2はエラーメッセージが出るみたい)だけど
使えるは使えるのでこんな感じでRのサンプルコードをgithubに公開したいってときには非常に便利かと。

XCode commandline tools をインストール

Terminalから以下のとおり。

 xcode-select --install

インストーラのウィンドウが開くので、手順にそってインストール。

Homebrew インストール

まだ入ってなかったら
まずはHomebrew をインストールする。

Homebrewは、mac用のパッケージマネージャ。
コマンドラインからpythonやらemacsやらgitやら必要なものを一通り入れられるので
macで開発するには必須(だと思う)。

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Press RETURN to continue or any other key to abort
==> /usr/bin/sudo /bin/chmod g+rwx /usr/local/sbin /usr/local/share/man/man8
Password:

と聞かれたら、macのログインパスワードを入力してしばし待つ。
無事完了した場合は以下のとおり。

==> Installation successful!
==> Next steps
Run `brew help` to get started

これでインストールは完了。

今後homebrewでいれたコマンドなどをmacのデフォルトより優先して使うためにパスを通す。
Yosemiteでは(前からだっけ)~/.bash_profileや~/.bashrcがデフォルトで存在なくなっているので、注意。
まあ以下のとおり実行すればOK。なければ.bash_profileを新規作成します。

echo "" >> ~/.bash_profile
echo "# set PATH for Homebrew packages" >> ~/.bash_profile
echo "export PATH=/usr/local/bin:\$PATH" >> ~/.bash_profile

# 読み込み
source ~/.bash_profile

以上、Homebrew。

iPython インストール / アップデート

インストールは以下の記事に沿って、Anacondaをとりあえず入れるのが良いと思う。qiita.com

iPythonでpython以外の言語(のkernelのGUIでの切り替え)を使うために
iPythonとiPython notebookとついでにipython-qtconsoleを3以降にアップデートしておく。

conda update ipython ipython-notebook ipython-qtconsole
The following packages will be UPDATED:

(中略)

Proceed ([y]/n)? 

などと出るので、yを打ち忘れないように。

完了したら
ipython --versionを実行して

3.1.0

と出れば良い。

Rインストール

R本体

Homebrewで入れられる。gfortranを直接入れる手順は古いので注意。
2015/5/22現在、rのパッケージがgcc49に依存しているので、以下のとおりインストール。
コンパイルが走るので、激しく時間がかかる(そのうち普通のgccで良くなると思う)。

もしかするとオフィシャルサイトから落としたほうが早いかも。

brew update
brew tap homebrew/science
brew install gcc49 --with-fortran
brew install r    

Rstudio.app

Rstudioを入れたかったらbrew caskを入れて、インストール

brew install caskroom/cask/brew-cask
brew cask install rstudio

# Applicationsフォルダの中ににシンボリックリンクを貼る
ln -s ~/Applications/RStudio.app /Applications/

初期設定

.Rprofile作成・CRANミラー設定

Rの初期設定ファイルは~/.Rprofile。
R用ライブラリのレポジトリCRANのミラーのアドレスをとりあえず設定しておく

echo 'options(repos="http://cran.md.tsukuba.ac.jp")' >> ~/.Rprofile
ホームディレクトリにLibrary用のディレクトリを作る。

Cavertsでも触れられているが、homebrew でアップデートしたときに
デフォルトの設定だとR上で追加したRのライブラリが使えなくなる模様。

mkdir -p ~/Library/R/3.2/library

また、こうしておくとRにてlibrary()関数でインストール済みライブラリを確認した時に

> library()
  パッケージ (ライブラリ ‘/Users/user/Library/R/3.2/library’ 中): 

base64enc               Tools for base64 encoding
 
 パッケージ (ライブラリ ‘/usr/local/Cellar/r/3.2.0/R.framework/Versions/3.2/Resources/library’ 中): 

base                    The R Base Package
boot                    Bootstrap Functions (Originally by Angelo Canty
                        for S)
・・・

というような感じで、自分で入れたライブラリが明確にみえるので良い。

もしくは、自分の好きなディレクトリを使いたいときは
.Rprofileに以下の設定を入れる。

.libPaths(c("/opt/RLibrary", .libPaths()))

上の例では/opt/RLibraryがLibraryの読み込み元に追加される。

IRKernel (ipython notebookでrを使うためのライブラリ)

やっと本題のIRKernelを入れましょう。
以下の公式サイトの手順だけではうまくいかなかった。
まずは依存ライブラリを入れる必要がある。

github.com

Rのコンソールから

install.packages(c('base64enc', 'evaluate', 'jsonlite', 'uuid', 'digest'))

Homebrewから入れているせいかも。
あとは公式サイト通りの手順。

install.packages(c('rzmq','repr','IRkernel','IRdisplay'), repos = 'http://irkernel.github.io/', type = 'source')
IRkernel::installspec()

インストールはこれで完了。マジおつか。

試してみる

ターミナルから

ipython notebook

でipython notebookを起動して、「New」からRを選べば良い。

f:id:nullbyte:20150523054643p:plain

とりあえず以上。