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}: (略)
Julia入門 条件分岐・複文・ループ
前回の記事に引き続き、Juliaの入門というか、一通りの機能を触りながら記事にしていきたいと思う。
ちなみにJuliaは型(Type)システムがコンセプトのなかで非常に重要なのだけど、そこそこハードな内容になりそうなので、あとで記事にまとめるまではあまり使用せずに説明していく。*1
ちなみに対象としているバージョンは
$ 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を参考にした。
- Scope of Variables — Julia Language 0.3.9-pre documentation
- Scope of Variables — Julia Language 0.4.0-dev documentation
続きはまた明日・・・。
Juliaことはじめ
Juliaについて
ここ最近Alternative RもしくなNext Pythonってな感じで、統計・機械学習などの科学計算界隈で特に見かけることが多くなった言語 、Julia。
メインストリームのプログラミング言語と比べると特徴・違和感がを感じるポイントが触れば触るほど出てくるのだけど、メリットとしては
- LLVMベースのJITコンパイル→場合によってはC / Java並みに速い
- Matlab / R 風の行列計算のネイティブサポート
- ノードをまたいだ並列計算の言語レベルサポート
- 型チェック機構
- C, Fortran, Pythonのライブラリを直接呼べる
といったところが挙げられると思われる。
上記のサイトによれば、Juliaの作者は以下のような目標を掲げて開発を始めたようです。
ゆるいライセンスのオープンソースで、Cの速度とRubyの動的さが欲しい。Lispのような真のマクロが使える同図象性のある言語で、Matlabのように分かりやすい数学の記述をしたい。Pythonのように汎用的に使いたいし、Rの統計処理、Perlの文字列処理、Matlabの線形代数計算も要る。シェルのように簡単にいくつかのパーツをつなぎ合わせたい。チョー簡単に習えて、超上級ハッカーも満足する言語。インタラクティブに使えて、かつコンパイルできる言語が欲しい。
(そういえば、C言語の実行速度が必要だってのは言ったっけ?)
こんなにもワガママを言った上だけど、Hadoopみたいな大規模分散コンピューティングもやりたい。もちろん、JavaとXMLで何キロバイトも常套句を書きたくないし、数千台のマシンに分散した何ギガバイトものログファイルを読んでデバッグするなんて論外だ。幾層にも重なった複雑さを押しつけられるようなことなく、純粋なパワーが欲しい。単純なスカラーのループを書いたら、一台のCPUのレジスターだけをブン回す機械語のコードが生成されて欲しい。A*Bと書くだけで千の計算をそれぞれ千のマシンに分散して実行して、巨大な行列の積をポンと計算してもらいたい。
型だって必要ないなら指定したくない。もしポリモーフィックな関数が必要な時には、ジェネリックプログラミングを使ってアルゴリズムを一度だけ書いて、あとは全ての型に使いたい。引数の型とかから自動的にメソッドを選択してくれる多重ディスパッチがあって、共通の機能がまったく違った型にも提供できるようにして欲しい。これだけのパワーがありながらも、言語としてシンプルでクリーンなものがいい。
これが本当なら最強だろ。ということで触り始めてみた。
表面をさらっと触る分にはごく普通の言語っぽいのだが、少し深堀りを始めると、メインストリームのPython / JS / Rubyみたいな言語を普段扱っている身からすると色々と「え!?」となる部分が出てくる出てくる。
というわけで、少しずつ解説していきたい。
インストール / 実験環境
Julia自体はHomebrewで入れる。
brew cask install Caskroom/cask/julia
インストールできたら
julia
でインタラクティブシェルが起動する。
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を入れましょう。
以下の公式サイトの手順だけではうまくいかなかった。
まずは依存ライブラリを入れる必要がある。
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を選べば良い。
とりあえず以上。
とりあえず
たったら書く