{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "このチュートリアルはJuliaの機能を最速で学ぶためのものです。\n", "Juliaは科学技術計算を得意とするプログラミング言語で、スクリプト言語のように手軽に書くことができるのにC言語やFortranにも負けない実行速度を誇ります。\n", "文法や機能はPythonなど他のスクリプト言語とかなり共通しているので、別のプログラミング言語の知識があれば基本的な機能はすぐに習得できるでしょう。\n", "\n", "他のスクリプト言語と比較した、Juliaの特異な点をいくつか挙げてみると、\n", "\n", "* コンパイル言語に比肩する実行速度\n", "* シンプルな言語機能と豊富な標準ライブラリ\n", "* 引数の型により実行される関数が決まる動的ディスパッチ\n", "* Lispのような強力なマクロ機能\n", "\n", "というようなものが挙げられます。\n", "それゆえ、今までのようにパフォーマンスが必要な部分をC言語など他の言語で書く必要はなく、すべてJuliaで書けます。\n", "実際、文字列を含むJuliaの標準ライブラリはほとんどJuliaで書かれており、十分なパフォーマンスを持ちます。\n", "\n", "\n", "ここで使用するJuliaのバージョンは2018年9月の最新版であるv1.0(もしくはv0.7)です。\n", "それでは、早速Juliaを学んでいきましょう!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Juliaの文法" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "まずはJuliaのコードをざっと見てみましょう。配列をソートするクイックソートのコードです。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "partition (generic function with 1 method)" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "quicksort(xs) = quicksort!(copy(xs))\n", "quicksort!(xs) = quicksort!(xs, 1, length(xs))\n", "\n", "function quicksort!(xs, lo, hi)\n", " if lo < hi\n", " p = partition(xs, lo, hi)\n", " quicksort!(xs, lo, p - 1)\n", " quicksort!(xs, p + 1, hi)\n", " end\n", " return xs\n", "end\n", "\n", "function partition(xs, lo, hi)\n", " pivot = div(lo + hi, 2)\n", " pvalue = xs[pivot]\n", " xs[pivot], xs[hi] = xs[hi], xs[pivot]\n", " j = lo\n", " @inbounds for i in lo:hi-1\n", " if xs[i] <= pvalue\n", " xs[i], xs[j] = xs[j], xs[i]\n", " j += 1\n", " end\n", " end\n", " xs[j], xs[hi] = xs[hi], xs[j]\n", " return j\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "どうでしょう。PythonやRubyをやったことがる人なら初見でも大体の意味が分かるのではないでしょうか。\n", "まるで擬似コードのような、スッキリした文法です。\n", "\n", "関数定義・分岐・反復などの構文はそれぞれ`function ... end`, `if ... end`, `for ... end`, `while ... end`のようにキーワードで始まり`end`で終わります。 ちょうどRubyと同じような感じです。 インデントはPythonのように必要ではありませんが、4スペースでひとつのインデントを表すのが慣習です。\n", "\n", "また、6行目の`p = partition(xs, lo, hi)`で分かるように変数の宣言や型の指定は通常必要ありません。\n", "\n", "18行目の`@inbounds`というのが気になるかもしれません。\n", "`@`マークで始まるこの表記は**マクロ呼出し**と言われ、コードを書き換えたりJuliaのコンパイラに最適化のヒントを与えることができます。\n", "ここで使われている`@inbounds`は添字アクセス(`xs[i]`など)のチェックを省き、少し計算を高速化できますが、配列の範囲外にアクセスした時はセグメンテーション違反などを起こし停止する可能性があります。\n", "\n", "こうしたJuliaで書かれたコードはJuliaのLLVMベースのJITコンパイラによりコンパイルされ、C言語などで書いたコードとそれほど変わらない速度で実行できます。\n", "\n", "試しに整数の小さい配列をソートしてみると、うまく行っています。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6-element Array{Int64,1}:\n", " 1\n", " 2\n", " 3\n", " 4\n", " 5\n", " 6" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "quicksort([3, 6, 2, 4, 5, 1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "浮動小数点でも動きます。" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6-element Array{Float64,1}:\n", " -2.1\n", " -1.2\n", " 0.1\n", " 3.1\n", " 3.4\n", " 5.0" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "quicksort([-2.1, 3.4, 5.0, -1.2, 3.1, 0.1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1千万要素もあるような浮動小数点数の配列のソートも一瞬です。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 1.211873 seconds (6 allocations: 76.294 MiB, 4.84% gc time)\n" ] }, { "data": { "text/plain": [ "10000000-element Array{Float64,1}:\n", " -5.201821821719352\n", " -5.050624909871284\n", " -4.983261442674312\n", " -4.974985552832985\n", " -4.893857076120602\n", " -4.864517550048943\n", " -4.85004091215009 \n", " -4.811551905035468\n", " -4.785936931795382\n", " -4.758123564483724\n", " -4.751953182949212\n", " -4.751136270866197\n", " -4.700641597803617\n", " ⋮ \n", " 4.643556492102544\n", " 4.664239936011457\n", " 4.682333119886756\n", " 4.685774543279274\n", " 4.695170058903736\n", " 4.710676182931005\n", " 4.824126039132785\n", " 4.889103328580121\n", " 4.920689915842902\n", " 4.976832282177101\n", " 5.16998796753511 \n", " 5.293639671892019" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using Random: seed!\n", "seed!(0xdeadbeef)\n", "xs = randn(10_000_000)\n", "@time quicksort(xs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ここでひとつ注目すべきことは、`quicksort`関数の定義時に引数の型を指定していなかったにも関わらず、整数にも浮動小数点数にも適用できるということです。\n", "実は、関数の最初の実行時にそれぞれの型に応じて高速なコードを生成しています。\n", "この機能のおかげで、**Juliaでは関数の型を一切指定しなくても十分なパフォーマンスが得られます**。\n", "\n", "`quicksort`は、数値にかぎらず以下の様な文字や文字列でも適用できます。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Char,1}:\n", " 'A'\n", " 'B'\n", " 'C'\n", " 'D'\n", " 'E'" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "quicksort(['B', 'A', 'D', 'E', 'C'])" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{String,1}:\n", " \"Alice\" \n", " \"Bob\" \n", " \"Charlie\"\n", " \"Dave\" \n", " \"Eve\" " ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "quicksort([\"Bob\", \"Alice\", \"Dave\", \"Eve\", \"Charlie\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 変数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Juliaの変数名はとても自由です。英字やアンダースコア(`_`)から始まる英数字/アンダースコアの他に、UTF8の多様な文字が使えます。\n", "\n", "使えるもの:\n", "* `xyz`\n", "* `_foo3_`\n", "* `π`\n", "* `f′`\n", "\n", "使えないもの:\n", "* `33xyz`などの数値から始まるもの\n", "* `for`, `function`, `end`など予約語\n", "* `x.y`, `x:y`などの予約されている記号を使ったもの" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`x`などが使えるのはもちろんですが、" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "100" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = 100" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`η`のようなギリシャ文字や漢字も使えます。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.01" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "η = 0.01" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.01" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "漢字変数 = η" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ここまで見てきたように、変数は特別に宣言せずとも初期化と同時に使用できます。\n", "\n", "変数には、その影響するソースコードの範囲である**スコープ**という概念があります。\n", "`function`や`for`などで始まり、`end`で終わるほとんどのブロックは新たな変数のスコープを作ります。`for ... end`がスコープを作るのはPythonなどと動作が異なりますので注意が必要です。\n", "\n", "以下の例では、変数`xx`は`for ... end`の内側のみのスコープを持つため、その外で`xx`にアクセスすると変数未定義の例外が発生しています。" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "ename": "UndefVarError", "evalue": "UndefVarError: xx not defined", "output_type": "error", "traceback": [ "UndefVarError: xx not defined", "", "Stacktrace:", " [1] top-level scope at In[10]:4" ] } ], "source": [ "for i in 1:10\n", " xx = i\n", "end\n", "xx" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "例外的にスコープを作らないのは`if ... end`と`begin ... end`です。すなわち、`if ... end`や`begin ... end`の内側で定義した変数にはその外側でもアクセスできます。" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "if true\n", " yy = 10\n", "end\n", "yy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 数値型" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Juliaの値は型を持ちます。Juliaでは動的に型がつき、様々な機能と密接に関わってきます。\n", "\n", "整数型は符号有無と8bit, 16bit, 32bit, 64bit, 128bitの組み合わせの10種類、それに加えて`Bool`型と`BigInt`型で合計12種類あり、それぞれ符号付き64bitは`Int64`や符号なし(unsigned)32bitは`UInt32`など一貫性のある型名がつけられています。\n", "\n", "浮動小数点数の型も16bit, 32bit, 64bitと`BigFloat`型で合計4種類があります。型名は`Float64`などのように精度の数値が最後についています。\n", "\n", "`BigInt`型と`BigFloat`型はそれぞれ任意精度の整数と浮動小数点数です。" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Int64" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Int64" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Float64" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Float64" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "他には複素数の`Complex{T}`型があります。\n", "`T`というのは**型パラメータ(type parameter)**で、実部と虚部の数値の型を指定します。ちょうどC++のテンプレートやHaskellの型変数(type variable)のようなものです。" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Complex{Float64}" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Complex{Float64}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "特殊な例として、円周率のような定数は`Irrational`型として定義されています。" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "π = 3.1415926535897..." ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "π" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isa(π, Irrational) # isa(x, typ)は値xが型typの値であるかどうかを返す関数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "科学計算のために作られているJuliaは、このように豊富な数値の型を持つ点が魅力のひとつです。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### リテラル" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "大抵の場合、何らかの値を作るリテラルは他の言語と同じです。\n", "\n", "* 数値\n", " * 整数 `Int`型: `1`, `42`, `-4`\n", " * 浮動小数点数 `Float64`型: `3.14`, `-2.1`, `6.0221413e+23`\n", " * 複素数 `Complex{T}`型: `3 + 2im`, `1.1 - 9.1im`\n", " * 有理数 `Rational{T}`型: `3 // 2`, `355 // 113`\n", "* 文字(列)\n", " * 文字 `Char`型: `'a'`, `'樹'`\n", " * 文字列 `String`型: `\"deadbeef\"`, `\"漢字\"`, `\"\"\"Triple Quote String\"\"\"`\n", "* その他\n", " * 真偽値 `Bool`型: `true`, `false`\n", " * シングルトン `Nothing`型: `nothing`\n", " \n", "実際のソースコードでは`Int`型を目にすることが多いですが、これは環境によって`Int32`または`Int64`のエイリアスになっています。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "型は以下のように`typeof`で確認できます。" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Int64" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(42)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Float64" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(42.0)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Rational{Int64}" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(3 // 2)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Complex{Int64}" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(2 + 3im)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Char" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof('A')" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Char" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof('漢')" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "String" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(\"deadbeef\")" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "String" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(\"漢字\")" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Bool" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(true)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Nothing" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(nothing)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "また,ある値がある型であるかは`isa`関数で確認できます。" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isa(1, Int)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1 isa Int # 中置もできる" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 配列 / タプル" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Juliaでは1次元配列を**ベクトル(`Vector`)**、2次元配列を**行列(`Matrix`)**とよびます。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ベクトル(1次元配列)は`[x,y,...]`で表現します。" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Int64,1}:\n", " 1\n", " 2\n", " 3" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[1,2,3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "添字のアクセスはカギ括弧(`[]`)を使います。**添字は`1`から始まり**、配列の長さ分で終わります。" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Int64,1}:\n", " 1\n", " 2\n", " 3\n", " 4\n", " 5" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 5要素のベクトル\n", "xs = [1,2,3,4,5]" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xs[1]" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xs[5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "値の更新も標準的な構文で可能です。" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "200" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xs[2] = 200" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Int64,1}:\n", " 1\n", " 200\n", " 3\n", " 4\n", " 5" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "配列を末尾からアクセスするときは`end`が使えます。" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xs[end]" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xs[end] == xs[5]" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xs[end-1] == xs[4]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ある範囲を切り出すには`n:m`というような書き方ができます。" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element Array{Int64,1}:\n", " 200\n", " 3" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xs[2:3]" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Int64,1}:\n", " 3\n", " 4\n", " 5" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xs[end-2:end]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "型もちょっと見てみましょう。`[x,y,...]`で作られるのは`Array`型の値です。\n", "以下の`{Int64,1}`の意味は後の型システムのところで説明します。" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Array{Int64,1}" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof([1,2,3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "また、`[1,2,3]`は`Vector`型の値でもあります。これは`isa`関数でチェックできます。" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isa([1,2,3], Vector)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "行列は`[a b c; d e f]`のように書けます。" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×3 Array{Int64,2}:\n", " 1 2 3\n", " 4 5 6" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[1 2 3; 4 5 6]" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×3 Array{Int64,2}:\n", " 1 2 3\n", " 4 5 6" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = [1 2 3;\n", " 4 5 6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "添字でのアクセスも見ておきましょう。" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x[1,2]" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x[2,end]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Vector`の時と同様に`Matrix`型であることを確認しておきます。" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isa([1 2 3; 4 5 6], Matrix)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "配列の要素数は`length`で取得します。" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "length([1,2,3])" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "length([1 2 3; 4 5 6])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "タプル(組)は`(x,y,...)`です。" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1, 2, 3)" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(1,2,3)" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Tuple{Int64,Int64,Int64}" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof((1,2,3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "タプルもベクトル同様、添字でのアクセスが出来ます" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(1,2,3)[2]" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(1,2,3)[end]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "タプルの括弧は、曖昧性がなければ省略できます。" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1, 2, 3)" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1,2,3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "配列の大きさは`size`関数で得られますが、タプルとして返されます。" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(3,)" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "size([1,2,3])" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(2, 3)" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "size([1 2 3; 4 5 6])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "タプルとベクトルはよく似ていますが、内部の構造や動作は大きく異なります。\n", "\n", "まず、タプルは不変(immutable)ですが、ベクトルや配列は可変(mutable)です。\n", "したがって、一度作ったタプルはそれ以降変更できませんが、配列では可能です。\n", "\n", "また、タプルはメモリーの割当が起きないことがあるため、オブジェクトの生成コストが極めて小さいです。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 範囲" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Juliaには値の範囲を表す範囲型も用意されています。`start:stop`のように書くことで、`start`から`stop`まで、両端を含む範囲を表現します。" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1:10" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1:10" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'a':1:'z'" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'a':'z'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "これは`for`ループを書くときや、配列や文字列から一部分を切り出す際に用いられます。" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n", "4\n", "5\n", "6\n" ] } ], "source": [ "for i in 3:6\n", " println(i)\n", "end" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6-element Array{Int64,1}:\n", " 1\n", " 2\n", " 3\n", " 4\n", " 5\n", " 6" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = [1,2,3,4,5,6]" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4-element Array{Int64,1}:\n", " 3\n", " 4\n", " 5\n", " 6" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x[3:6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`start:step:stop`のように書くことで、ステップ幅を指定することもできます。" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "10\n", "20\n", "30\n", "40\n", "50\n", "60\n", "70\n", "80\n", "90\n" ] } ], "source": [ "for i in 0:10:90\n", " println(i)\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ステップ幅に`-1`を指定すれば、逆順の範囲も作れます。" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5\n", "4\n", "3\n", "2\n", "1\n" ] } ], "source": [ "for i in 5:-1:1\n", " println(i)\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 辞書" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Juliaでは辞書ももちろん用意されています。" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Dict{String,Int64} with 2 entries:\n", " \"bar\" => 2\n", " \"foo\" => 1" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = Dict(\"foo\" => 1, \"bar\" => 2)" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x[\"foo\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 型システム" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Juliaにおいて、型は非常に重要な意味を持ちます。\n", "PythonやRubyのようにJuliaは動的型付け言語ですが、オブジェクト指向のプログラミング言語での型の使い方とはかなり異なっています。\n", "ここでは、Juliaの型システムを理解し、その後に出てくる型の定義や動的ディスパッチの前提知識を固めましょう。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 型の階層構造" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Juliaの値はひとつの **具体的な** 型を持ちます。一部例外を除いて、型が自動的に別の型にキャストされることはありません\n", "(一部例外とは、`1 + 1.5`などの数値計算とコンストラクタです )。\n", "\n", "ここで、 **具体的な (concrete)** とわざわざ述べたのは、Juliaにはこれと対極をなす **抽象的な (abstract)** 型があるためです。\n", "適切な訳語がない、ここでは原文通り具体的な型を「具体型」、抽象的な型を「抽象型」と表記します。\n", "\n", "最も大きな違いは、**具体型はインスタンス化可能**な一方で**抽象型はインスタンス化ができない**点です。\n", "したがって、任意の`x`に対して、`typeof(x)`の結果は必ず具体型になります。\n", "抽象型は具体型や他の抽象型のsupertypeとして機能し、型間のsubtype/supertypeの関係性は木構造のグラフをとります。\n", "さらに、具体型は他の型のsupertypeにはなれませんので、必然的にグラフの葉が具体型に、内部ノードが抽象型となります。\n", "このグラフの根にあるのが`Any`という抽象型です。\n", "Juliaでユーザーが型を定義するとデフォルトでこの`Any`型のsubtypeになります。\n", "\n", "具体例で確認しましょう。`Int64`と`Int32`は共に具体型で、`Integer`という抽象型のsubtypeになっています。\n", "具体型は`isconcretetype`、subtype/supertypeの関係は`<:`という関数で確認できます。" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isconcretetype(Int64)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isconcretetype(Int32)" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "false" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isconcretetype(Integer)" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Int64 <: Integer" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Int32 <: Integer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "他には、`Bool`型なども`Integer`型のsubtypeです。" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Bool <: Integer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "全ての型はsupertypeを一つだけ持ち、抽象型は複数のsubtypeを持つことができます。\n", "それぞれ`supertype`と`subtypes`という関数があるので、少しみてみましょう。" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Integer" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "supertype(Bool)" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Any,1}:\n", " Bool \n", " Signed \n", " Unsigned" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "subtypes(Integer)" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6-element Array{Any,1}:\n", " BigInt\n", " Int128\n", " Int16 \n", " Int32 \n", " Int64 \n", " Int8 " ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "subtypes(Signed)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "supertypeを辿って行くと、最終的には`Any`型にたどり着きます。\n", "次のような`tracetype`関数で検証してみましょう。" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tracetype (generic function with 1 method)" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function tracetype(t)\n", " print(t)\n", " while t != supertype(t)\n", " print(\" <: \")\n", " t = supertype(t)\n", " print(t)\n", " end\n", " println()\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "どこから抽象型が共通になってるかなどにも注目して眺めてみて下さい。" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Int64 <: Signed <: Integer <: Real <: Number <: Any\n" ] } ], "source": [ "tracetype(Int64)" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Bool <: Integer <: Real <: Number <: Any\n" ] } ], "source": [ "tracetype(Bool)" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "BigInt <: Signed <: Integer <: Real <: Number <: Any\n" ] } ], "source": [ "tracetype(BigInt)" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Float64 <: AbstractFloat <: Real <: Number <: Any\n" ] } ], "source": [ "tracetype(Float64)" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "String <: AbstractString <: Any\n" ] } ], "source": [ "tracetype(String)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "数値の型に関しては、以下の図のようになっています。\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 型パラメータ" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`[1,2,3]`は以下の様な型です。" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Array{Int64,1}" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof([1,2,3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "これは、`[1,2,3]`が`Int64`型を要素とする`1`次元の配列(ベクトル)という意味になります。一般化すると、`Array{T,N}`型は`T`型を要素とする`N`次元の配列です。\n", "このように、Juliaでは**型が別の型や値をパラメータとして持つ**ことができます。\n", "\n", "以下の例で、型パラメータに注目して確認してください。" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 1.0\n", " 2.0\n", " 3.0" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[1.0, 2.0, 3.0]" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Array{Float64,1}" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof([1.0, 2.0, 3.0])" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1×3 Array{Int64,2}:\n", " 1 2 3" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[1 2 3]" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Array{Int64,2}" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof([1 2 3])" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×3 Array{Int64,2}:\n", " 1 2 3\n", " 4 5 6" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[1 2 3; 4 5 6]" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Array{Int64,2}" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof([1 2 3; 4 5 6])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 型定義" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "自分で型を作るのも簡単です。\n", "以下のように`struct`または`mutable struct`に続けて型名を書き、フィールドを定義します。" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "collapsed": true }, "outputs": [], "source": [ "mutable struct Person\n", " name::String\n", " age::Int\n", "end\n", "\n", "struct Location\n", " x::Float64\n", " y::Float64\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "デフォルトコンストラクタがありますので、即座にインスタンス化できます。\n", "インスタンスのフィールドへはドット(`.`)でアクセスできます。" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Person(\"ヤマダ田中\", 34)" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "person = Person(\"ヤマダ田中\", 34)" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(\"ヤマダ田中\", 34)" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "person.name, person.age" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`mutable struct`で作られた型のフィールド値は更新できます。" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "35" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "person.age += 1" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Person(\"ヤマダ田中\", 35)" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "person" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Location(1.0, 2.0)" ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "loc = Location(1.0, 2.0)" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1.0, 2.0)" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "loc.x, loc.y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`struct`で作られた型のフィールド値は更新できません。" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "ename": "ErrorException", "evalue": "type Location is immutable", "output_type": "error", "traceback": [ "type Location is immutable", "", "Stacktrace:", " [1] setproperty!(::Location, ::Symbol, ::Float64) at ./sysimg.jl:19", " [2] top-level scope at In[94]:1" ] } ], "source": [ "loc.x += 3.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 関数とメソッド" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 関数の定義" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "既に何度か出てきますが、関数の定義は\n", "\n", "```julia\n", "function <関数名>(<引数>, ...)\n", " <関数本体>\n", "end\n", "```\n", "\n", "を使います。\n", "返り値は`return`を使いますが、最後に評価された式が自動的に返り値になるので省略可能です。" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dist (generic function with 1 method)" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 2次元空間でのpとq間のユークリッド距離\n", "function dist(p, q)\n", " dx = p[1] - q[1]\n", " dy = p[2] - q[2]\n", " sqrt((dx)^2 + (dy)^2)\n", "end" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5.0" ] }, "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dist((0, 0), (3, 4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "もう一つ、別の方法として以下の「代入」のような形も使えます。一行で済むような\n", "簡単な関数を定義するときに便利です。" ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dist (generic function with 1 method)" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dist(p, q) = sqrt((p[1] - q[1])^2 + (p[2] - q[2])^2)" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5.0" ] }, "execution_count": 98, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dist((0, 0), (3, 4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "オプション引数やキーワード引数、可変長引数もJuliaでは使えます。" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ktop (generic function with 2 methods)" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# オプション引数: '='\n", "ktop(xs, k=3) = sort(xs)[1:k]" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Int64,1}:\n", " 1\n", " 2\n", " 3" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ktop([1,5,3,2,6])" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element Array{Int64,1}:\n", " 1\n", " 2" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ktop([1,5,3,2,6], 2)" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ktop (generic function with 2 methods)" ] }, "execution_count": 102, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# キーワード引数: ';' '='\n", "function ktop(xs; k=3, rev=false)\n", " sort(xs, rev=rev)[1:k]\n", "end" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element Array{Int64,1}:\n", " 1\n", " 2" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ktop([1,5,3,2,6], k=2)" ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4-element Array{Int64,1}:\n", " 6\n", " 5\n", " 3\n", " 2" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ktop([1,5,3,2,6], k=4, rev=true)" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "pathlength (generic function with 1 method)" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function pathlength(p, q, rs...)\n", " len = dist(p, q)\n", " for r in rs\n", " len += dist(q, r)\n", " q = r\n", " end\n", " return len\n", "end" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.4142135623730951" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pathlength((0, 0), (1, 1))" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.414213562373095" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pathlength((0, 0), (1, 1), (1, 2))" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4.650281539872885" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pathlength((0, 0), (1, 1), (1, 2), (0, 0))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "それぞれに特徴的な記号をまとめておきます。\n", "\n", "\n", " \n", " \n", " \n", "
オプション引数`=``xs, k=3`
キーワード引数`;` `=``xs; k=3`
可変長引数`...``x, xs...`
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ここで、Juliaでの関数名の慣習を確認しておきます。\n", "ドキュメントや標準ライブラリの関数から推奨される関数の命名法は以下の様なものになります\n", "(括弧内は標準ライブラリにある関数の具体例です)。\n", "\n", "* すべて小文字 (`get`, `readline`)\n", "* アンダースコア(`_`)は、なるべく使わない (`readdlm`, `findmin`)\n", "* 行う操作を表す英語の動詞を使う (`open`, `serialize`)\n", "* `Bool`を返す関数は`is...`や`has...`という名前を使う (`isempty`, `haskey`)\n", "* 引数のデータを変えてしまう関数は最後に`!`をつける (`push!`, `sort!`)\n", "\n", "一番最初の例を振り返ってみると、この形の関数定義を使っていました。\n", "\n", "```julia\n", "quicksort(xs) = quicksort!(copy(xs))\n", "quicksort!(xs) = quicksort!(xs, 1, length(xs))\n", "```\n", "\n", "`quicksort!`のように、関数名の最後に`!`をつけるのは引数を変更するときの慣習的な方法です。\n", "関数の引数は基本的に参照渡しのようになるため、`Array`などmutableな型の値は関数内で変更できます。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 多重ディスパッチ" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "実はJuliaでは、関数がとる引数の型を`<引数>::<型>`のように指定することもできます。これは他のスクリプト言語にはあまりみられない特徴です。" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "add (generic function with 1 method)" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function add(x::Int, y::Int)\n", " return x + y\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "型が合えば関数を呼び出すことができます。" ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add(1, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "しかし型が合わないと、呼び出すこともできません。" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [ { "ename": "MethodError", "evalue": "MethodError: no method matching add(::Float64, ::Float64)", "output_type": "error", "traceback": [ "MethodError: no method matching add(::Float64, ::Float64)", "", "Stacktrace:", " [1] top-level scope at In[111]:1" ] } ], "source": [ "add(1.0, 2.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ここで、次の関数を定義してみましょう" ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "add (generic function with 2 methods)" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function add(x::Float64, y::Float64)\n", " return x + y\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "すると、先ほど呼び出せなかった方の組み合わせで呼び出せるようになります" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.0" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add(1.0, 2.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "しかも、これは元の関数の上書きではありません。元の`Int`, `Int`の組み合わせでも呼び出せます。" ] }, { "cell_type": "code", "execution_count": 114, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 114, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add(1, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "このように、引数の型に合わせて呼び出すメソッドを変える仕組みを**多重ディスパッチ**と呼びます。\n", "以下の例で、これがどのように動くのかを確認して下さい。" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "foo (generic function with 4 methods)" ] }, "execution_count": 115, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function foo(x::Int)\n", " println(\"foo Int: $x\")\n", "end\n", "\n", "function foo(x::Int, y::Int)\n", " println(\"foo Int Int: $x $y\")\n", "end\n", "\n", "function foo(x::Float64, y::Float64)\n", " println(\"foo Float64 Float64: $x $y\")\n", "end\n", "\n", "function foo(x::Int, y::Float64)\n", " println(\"foo Int Float64: $x $y\")\n", "end " ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "foo Int: 1\n" ] } ], "source": [ "foo(1)" ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "foo Int Int: 1 2\n" ] } ], "source": [ "foo(1, 2)" ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "foo Float64 Float64: 1.0 3.14\n" ] } ], "source": [ "foo(1.0, 3.14)" ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "foo Int Float64: 1 3.14\n" ] } ], "source": [ "foo(1, 3.14)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "型の指定は`Int64`や`Float64`のような具体型に限りません。\n", "より高位の抽象型を使うこともできます。" ] }, { "cell_type": "code", "execution_count": 120, "metadata": {}, "outputs": [ { "ename": "MethodError", "evalue": "MethodError: no method matching add(::BigInt, ::BigFloat)", "output_type": "error", "traceback": [ "MethodError: no method matching add(::BigInt, ::BigFloat)", "", "Stacktrace:", " [1] top-level scope at In[120]:1" ] } ], "source": [ "add(BigInt(1), BigFloat(1)) # これにマッチするメソッドはまだない" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`x isa Number`かつ`y isa Number`になれば、以下のメソッドを呼び出せますので、具体型を使っていたときに比べ適用範囲がぐっと広がります。" ] }, { "cell_type": "code", "execution_count": 121, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "add (generic function with 3 methods)" ] }, "execution_count": 121, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function add(x::Number, y::Number)\n", " return x + y\n", "end" ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.0" ] }, "execution_count": 122, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add(BigInt(1), BigFloat(1)) # 今度は呼び出せる" ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(4.141592653589793, 4.141592653589793, 1)" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add(1, π), add(π, 1.0), add(true, false) # 様々な型の組み合わせが使える " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### コンストラクタ" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Juliaのコンストラクタは通常の関数と同様に定義でき、多重ディスパッチも利用できます。\n", "コンストラクタは以下の2種類に分けられます。\n", "\n", "* 内部コンストラクタ (inner constructor)\n", "* 外部コンストラクタ (outer constructor)\n", "\n", "外部コンストラクタは、他の外部コンストラクタや内部コンストラクタを呼び出すことでオブジェクトを作ることができます。\n", "最終的に、全てのオブジェクトは内部コンストラクタを経由して作られるため、**内部コンストラクタで最終的な値のチェック**などを実現できます。\n", "\n", "それでは、具体例を見てみましょう。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "以下の`RGB`型はRed-Green-Blueの3原色を指定して色を表現する型です。\n", "それぞれの色は8bitの符号なし整数でエンコーディングしています。\n", "しかし、色を作る際は8bitで表現できない値が与えられる可能性があるため、不正な値が与えられれば例外を投げるようにしたいです。\n", "これを実現するため、内部コンストラクタで与えられた値のチェックをしています。\n", "内部コンストラクタは、構文的には`(mutable) struct ... end`の内部で定義された関数です。\n", "内部コンストラクタの特別な点は、`new`関数を呼び出すことで、その型のオブジェクトを作ることができるところにあります。" ] }, { "cell_type": "code", "execution_count": 124, "metadata": {}, "outputs": [], "source": [ "struct RGB\n", " r::UInt8\n", " g::UInt8\n", " b::UInt8\n", "\n", " function RGB(r, g, b)\n", " @assert 0 <= r <= 255\n", " @assert 0 <= g <= 255\n", " @assert 0 <= b <= 255\n", " return new(r, g, b)\n", " end\n", "end" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "RGB(0x01, 0x02, 0x03)" ] }, "execution_count": 125, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RGB(1, 2, 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "300など8bitで表現できない色が与えられたら、`@assert`マクロが例外を投げます。" ] }, { "cell_type": "code", "execution_count": 126, "metadata": {}, "outputs": [ { "ename": "AssertionError", "evalue": "AssertionError: 0 <= g <= 255", "output_type": "error", "traceback": [ "AssertionError: 0 <= g <= 255", "", "Stacktrace:", " [1] RGB(::Int64, ::Int64, ::Int64) at ./In[124]:8", " [2] top-level scope at In[126]:1" ] } ], "source": [ "RGB(2, 300, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "デフォルトコンストラクタは`new`関数を呼び出すだけの内部コンストラクタで、プログラマが明示的に内部コンストラクタを定義するとデフォルトコンストラクタは作られません。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ある1つの値を与えたら、R,G,Bすべてに同じ値を設定する簡便なコンストラクタが欲しいかもしれません。\n", "その場合は以下のように外部コンストラクタを使うと便利です。" ] }, { "cell_type": "code", "execution_count": 127, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "RGB" ] }, "execution_count": 127, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RGB(x) = RGB(x, x, x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "外部コンストラクタは構文的には`(mutable) struct ... end`の外部で定義された関数です。ここでは`new`関数は使えず、内部コンストラクタや他の外部コンストラクタを呼び出すことでオブジェクトを作ります。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "これは、他の外部コンストラクタを呼び出す外部コンストラクタの例です。" ] }, { "cell_type": "code", "execution_count": 128, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "RGB" ] }, "execution_count": 128, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RGB() = RGB(0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "多重ディスパッチのお陰で、以下の3つのコンストラクタはすべて使うことができます。" ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(RGB(0x00, 0x00, 0x00), RGB(0x0a, 0x0a, 0x0a), RGB(0x0a, 0x14, 0x1e))" ] }, "execution_count": 129, "metadata": {}, "output_type": "execute_result" } ], "source": [ "RGB(), RGB(10), RGB(10, 20, 30)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 構文糖衣" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ここではJuliaで見られる特徴的な構文をざっと見て行きます。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 係数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "変数や関数の前に数値を置くことで、その値との積を表現できます。" ] }, { "cell_type": "code", "execution_count": 130, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.1" ] }, "execution_count": 130, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = 2.1" ] }, { "cell_type": "code", "execution_count": 131, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4.2" ] }, "execution_count": 131, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2x" ] }, { "cell_type": "code", "execution_count": 132, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "21.94" ] }, "execution_count": 132, "metadata": {}, "output_type": "execute_result" } ], "source": [ "4x^2 + 3x - 2" ] }, { "cell_type": "code", "execution_count": 133, "metadata": {}, "outputs": [], "source": [ "x = range(0, stop=1, length=50);" ] }, { "cell_type": "code", "execution_count": 134, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "50-element Array{Float64,1}:\n", " -3.0 \n", " -2.8267897373141215 \n", " -2.6339373233305805 \n", " -2.4219133558638326 \n", " -2.1913188324640127 \n", " -1.9428824987789626 \n", " -1.677457339318942 \n", " -1.3960162317568687 \n", " -1.0996467914829426 \n", " -0.7895454385442837 \n", " -0.4670107243016668 \n", " -0.13343596009188374\n", " 0.20969880513734385\n", " ⋮ \n", " 6.996871635711436 \n", " 6.996458834488029 \n", " 6.969421660503003 \n", " 6.9159064004967 \n", " 6.836202476105568 \n", " 6.730740627863947 \n", " 6.60009021313547 \n", " 6.444955635692603 \n", " 6.266171930354854 \n", " 6.064699531632904 \n", " 5.841618260670547 \n", " 5.598120569893563 " ] }, "execution_count": 134, "metadata": {}, "output_type": "execute_result" } ], "source": [ "4sin.(2x) - 3cos.(4x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 内包表記" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`[]`内に`for`ループを書き、ベクトルや行列などの配列を作ることができます。" ] }, { "cell_type": "code", "execution_count": 135, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4-element Array{Int64,1}:\n", " 1\n", " 2\n", " 3\n", " 4" ] }, "execution_count": 135, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x for x in 1:4]" ] }, { "cell_type": "code", "execution_count": 136, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4×5 Array{Int64,2}:\n", " 1 2 3 4 5\n", " 2 4 6 8 10\n", " 3 6 9 12 15\n", " 4 8 12 16 20" ] }, "execution_count": 136, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x * y for x in 1:4, y in 1:5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "最後に`if`をつけることでフィルターをかけることもできます。" ] }, { "cell_type": "code", "execution_count": 137, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Int64,1}:\n", " 1\n", " 3\n", " 5\n", " 7\n", " 9" ] }, "execution_count": 137, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x for x in 1:10 if isodd(x)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "辞書も可能です。" ] }, { "cell_type": "code", "execution_count": 138, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Dict{Char,Int64} with 26 entries:\n", " 'n' => 14\n", " 'f' => 6\n", " 'w' => 23\n", " 'd' => 4\n", " 'e' => 5\n", " 'o' => 15\n", " 'h' => 8\n", " 'j' => 10\n", " 'i' => 9\n", " 'k' => 11\n", " 'r' => 18\n", " 's' => 19\n", " 't' => 20\n", " 'q' => 17\n", " 'y' => 25\n", " 'a' => 1\n", " 'c' => 3\n", " 'p' => 16\n", " 'm' => 13\n", " 'z' => 26\n", " 'g' => 7\n", " 'v' => 22\n", " 'l' => 12\n", " 'u' => 21\n", " 'x' => 24\n", " 'b' => 2" ] }, "execution_count": 138, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Dict(c => i for (i, c) in enumerate('a':'z'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 転置" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "行列を転置するにはシングルクォート(`'`)を末尾につけるだけです。" ] }, { "cell_type": "code", "execution_count": 139, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×3 Array{Int64,2}:\n", " 1 2 3\n", " 4 5 6" ] }, "execution_count": 139, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = [1 2 3; 4 5 6]" ] }, { "cell_type": "code", "execution_count": 140, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×2 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:\n", " 1 4\n", " 2 5\n", " 3 6" ] }, "execution_count": 140, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`transpose`関数でも転置できます。" ] }, { "cell_type": "code", "execution_count": 141, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×2 LinearAlgebra.Transpose{Int64,Array{Int64,2}}:\n", " 1 4\n", " 2 5\n", " 3 6" ] }, "execution_count": 141, "metadata": {}, "output_type": "execute_result" } ], "source": [ "transpose(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 無名関数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "一時的に使う関数などを、名前を付けずに作ることができます。" ] }, { "cell_type": "code", "execution_count": 142, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "#12 (generic function with 1 method)" ] }, "execution_count": 142, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x -> 2x" ] }, { "cell_type": "code", "execution_count": 143, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Int64,1}:\n", " 2\n", " 4\n", " 6" ] }, "execution_count": 143, "metadata": {}, "output_type": "execute_result" } ], "source": [ "map(x -> 2x, [1,2,3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ブロック引数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "第一引数に関数を取れる関数では、`do ... end`を使うことで無名関数を後置することもできます。" ] }, { "cell_type": "code", "execution_count": 144, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Int64,1}:\n", " 2\n", " 4\n", " 6" ] }, "execution_count": 144, "metadata": {}, "output_type": "execute_result" } ], "source": [ "map([1,2,3]) do x\n", " 2x\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "この機能は、ファイルを開いて処理をし、終わったら自動的にファイルを閉じるといった場面で多用されます。" ] }, { "cell_type": "code", "execution_count": 145, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1: one\n", "2: two\n", "3: three\n" ] } ], "source": [ "open(\"sample.txt\") do io\n", " for (n, line) in enumerate(eachline(io))\n", " println(\"$n: $line\")\n", " end\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 非標準文字列 (non-standard string literals)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`\"...\"`のような文字列リテラルの前に識別子を起き、その文字列を元に様々な操作を行うことができます。\n", "以下は、`r\"...\"`で正規表現オブジェクトを作る例です。" ] }, { "cell_type": "code", "execution_count": 146, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "r\"\\w-\\d \\w+\"" ] }, "execution_count": 146, "metadata": {}, "output_type": "execute_result" } ], "source": [ "r\"\\w-\\d \\w+\"" ] }, { "cell_type": "code", "execution_count": 147, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "RegexMatch(\"B-2 Spirit\")" ] }, "execution_count": 147, "metadata": {}, "output_type": "execute_result" } ], "source": [ "match(r\"\\w-\\d \\w+\", \"B-2 Spirit\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "バージョン番号を作るのにも使われます。" ] }, { "cell_type": "code", "execution_count": 148, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "v\"1.2.3\"" ] }, "execution_count": 148, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v\"1.2.3\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 外部コマンド" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "バッククウォートを使って外部コマンドを作成し、`run`関数で実行できます。" ] }, { "cell_type": "code", "execution_count": 149, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "`\u001b[4mls\u001b[24m \u001b[4m-la\u001b[24m`" ] }, "execution_count": 149, "metadata": {}, "output_type": "execute_result" } ], "source": [ "`ls -la`" ] }, { "cell_type": "code", "execution_count": 150, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "total 1568\n", "drwxr-xr-x 15 kenta staff 510 Sep 3 19:11 .\n", "drwxr-xr-x 47 kenta staff 1598 Aug 30 13:50 ..\n", "-rw-r--r--@ 1 kenta staff 6148 Dec 21 2015 .DS_Store\n", "drwxr-xr-x 14 kenta staff 476 Sep 3 18:57 .git\n", "-rw-r--r-- 1 kenta staff 20 Dec 21 2015 .gitignore\n", "drwxr-xr-x 7 kenta staff 238 Jul 24 2017 .ipynb_checkpoints\n", "-rw-r--r-- 1 kenta staff 576346 Aug 12 13:20 Juliaクックブック.ipynb\n", "-rw-r--r-- 1 kenta staff 126376 Sep 3 19:11 Julia高速チュートリアル.ipynb\n", "-rw-r--r-- 1 kenta staff 1100 Jul 4 2017 README.md\n", "drwxr-xr-x 6 kenta staff 204 Jul 8 2017 data\n", "drwxr-xr-x 4 kenta staff 136 Dec 21 2015 images\n", "-rw-r--r-- 1 kenta staff 167 Jul 2 2017 optimization.jl\n", "-rw-r--r-- 1 kenta staff 14 Apr 29 2015 sample.txt\n", "-rw-r--r-- 1 kenta staff 54968 Jul 2 2017 連続最適化.ipynb\n", "-rw-r--r-- 1 kenta staff 16114 Jul 24 2017 並行・並列計算.ipynb\n" ] }, { "data": { "text/plain": [ "Process(`\u001b[4mls\u001b[24m \u001b[4m-la\u001b[24m`, ProcessExited(0))" ] }, "execution_count": 150, "metadata": {}, "output_type": "execute_result" } ], "source": [ "run(`ls -la`)" ] }, { "cell_type": "code", "execution_count": 151, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 1\tone\n", " 2\ttwo\n", " 3\tthree\n" ] }, { "data": { "text/plain": [ "Process(`\u001b[4mcat\u001b[24m \u001b[4m-n\u001b[24m \u001b[4msample.txt\u001b[24m`, ProcessExited(0))" ] }, "execution_count": 151, "metadata": {}, "output_type": "execute_result" } ], "source": [ "run(`cat -n sample.txt`)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## メタプログラミング" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Juliaには、Juliaのプログラムを使ってJuliaのコードを書き換えることができる**メタプログラミング**という機能があります。JuliaではLisp言語の影響から、この機能を**マクロ**とも呼んでいます。\n", "これを使うと、書くコードの量を劇的に減らしたり、普通の関数では実現できないような特異な働きをさせることができます。\n", "\n", "Lispなどでは一般的に使われるマクロですが、他の言語にはあまり見られず、あったとしても自分で定義して使うことは稀でしょう。このチュートリアルでは既に定義されているマクロを使用してみて、マクロではどのようなことが可能になるのかを見ることにします。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `@show`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`@show`マクロは、変数の値を確認する際に大変便利なマクロです。\n", "これを使うと、コードのある部分で変数が何だったのかを瞬時に知ることができます。" ] }, { "cell_type": "code", "execution_count": 152, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.14" ] }, "execution_count": 152, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = 3.14" ] }, { "cell_type": "code", "execution_count": 153, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 3.14\n" ] }, { "data": { "text/plain": [ "3.14" ] }, "execution_count": 153, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@show x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "挿入ソート(insertion sort)の動作を確認するために、`@show`マクロを使ってみましょう。" ] }, { "cell_type": "code", "execution_count": 154, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "insertionsort! (generic function with 1 method)" ] }, "execution_count": 154, "metadata": {}, "output_type": "execute_result" } ], "source": [ "insertionsort(xs) = insertionsort!(copy(xs))\n", "\n", "function insertionsort!(xs)\n", " @show xs\n", " for i in 2:length(xs)\n", " j = i\n", " while j > 1 && xs[j-1] > xs[j]\n", " xs[j-1], xs[j] = xs[j], xs[j-1]\n", " j -= 1\n", " end\n", " @show xs\n", " end\n", " xs\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "これを実行してみると、ベクトルが左から順にソートされていく様子がよくわかるのではないでしょうか。" ] }, { "cell_type": "code", "execution_count": 155, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "xs = [6, 5, 3, 1, 8, 7, 2, 4]\n", "xs = [5, 6, 3, 1, 8, 7, 2, 4]\n", "xs = [3, 5, 6, 1, 8, 7, 2, 4]\n", "xs = [1, 3, 5, 6, 8, 7, 2, 4]\n", "xs = [1, 3, 5, 6, 8, 7, 2, 4]\n", "xs = [1, 3, 5, 6, 7, 8, 2, 4]\n", "xs = [1, 2, 3, 5, 6, 7, 8, 4]\n", "xs = [1, 2, 3, 4, 5, 6, 7, 8]\n" ] }, { "data": { "text/plain": [ "8-element Array{Int64,1}:\n", " 1\n", " 2\n", " 3\n", " 4\n", " 5\n", " 6\n", " 7\n", " 8" ] }, "execution_count": 155, "metadata": {}, "output_type": "execute_result" } ], "source": [ "insertionsort([6,5,3,1,8,7,2,4])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `@assert`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`@assert`マクロは、簡便な不定条件のチェックに便利なマクロです。" ] }, { "cell_type": "code", "execution_count": 156, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 156, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "条件が満たされるときは、何もしませんが、" ] }, { "cell_type": "code", "execution_count": 157, "metadata": { "collapsed": true }, "outputs": [], "source": [ "@assert x == 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "条件に違反すると、例外を投げます。" ] }, { "cell_type": "code", "execution_count": 158, "metadata": {}, "outputs": [ { "ename": "AssertionError", "evalue": "AssertionError: x == 2", "output_type": "error", "traceback": [ "AssertionError: x == 2", "", "Stacktrace:", " [1] top-level scope at In[158]:1" ] } ], "source": [ "@assert x == 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "エラーメッセージは自分で書くこともできます。" ] }, { "cell_type": "code", "execution_count": 159, "metadata": {}, "outputs": [ { "ename": "AssertionError", "evalue": "AssertionError: xが1ですよ! xは2じゃないとダメです!", "output_type": "error", "traceback": [ "AssertionError: xが1ですよ! xは2じゃないとダメです!", "", "Stacktrace:", " [1] top-level scope at In[159]:1" ] } ], "source": [ "@assert x == 2 \"xが$(x)ですよ! xは2じゃないとダメです!\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `@time`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`@time`マクロは、関数やコード片の実行時間とメモリ使用量を測るのに便利なマクロです。\n", "\n", "先ほど定義した`insertionsort`の`@show`を消して、`quicksort`とパフォーマンスを比較してみましょう。" ] }, { "cell_type": "code", "execution_count": 160, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element Array{Float64,1}:\n", " 0.0\n", " 1.0" ] }, "execution_count": 160, "metadata": {}, "output_type": "execute_result" } ], "source": [ "insertionsort(xs) = insertionsort!(copy(xs))\n", "\n", "function insertionsort!(xs)\n", " #@show xs\n", " @inbounds for i in 2:length(xs)\n", " j = i\n", " while j > 1 && xs[j-1] > xs[j]\n", " xs[j-1], xs[j] = xs[j], xs[j-1]\n", " j -= 1\n", " end\n", " #@show xs\n", " end\n", " xs\n", "end\n", "\n", "insertionsort([1.0, 0.0])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "小さい配列だと`insertionsort`と`quicksort`が同程度に速いようですが、" ] }, { "cell_type": "code", "execution_count": 161, "metadata": {}, "outputs": [], "source": [ "xs = randn(10);" ] }, { "cell_type": "code", "execution_count": 162, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.016046 seconds (100.00 k allocations: 15.259 MiB)\n" ] } ], "source": [ "GC.gc()\n", "@time for _ in 1:100000; insertionsort(xs); end" ] }, { "cell_type": "code", "execution_count": 163, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.015160 seconds (100.00 k allocations: 15.259 MiB)\n" ] } ], "source": [ "GC.gc()\n", "@time for _ in 1:100000; quicksort(xs); end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "大きい配列では`quicksort`の方が断然高速です。" ] }, { "cell_type": "code", "execution_count": 164, "metadata": {}, "outputs": [], "source": [ "xs = randn(10000);" ] }, { "cell_type": "code", "execution_count": 165, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.053030 seconds (6 allocations: 78.359 KiB)\n" ] } ], "source": [ "GC.gc()\n", "@time insertionsort(xs);" ] }, { "cell_type": "code", "execution_count": 166, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.001151 seconds (6 allocations: 78.359 KiB)\n" ] } ], "source": [ "GC.gc()\n", "@time quicksort(xs);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 数値計算" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Juliaを使う人の多くは、数値計算を目的としていると思います。\n", "ここで、標準ライブラリで使える便利な関数を紹介していきます。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 配列の確保" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`[1,2,3]`のようにリテラルで直接配列を確保する以外にも、配列を確保する便利な関数が多数あります。\n", "\n", "`0`で埋められた配列が欲しい場合は、`zeros`を使います。\n", "基本的には、`zeros(<型>, <サイズ>)`という書き方をします。" ] }, { "cell_type": "code", "execution_count": 167, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4-element Array{Float64,1}:\n", " 0.0\n", " 0.0\n", " 0.0\n", " 0.0" ] }, "execution_count": 167, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = zeros(Float64, 4)" ] }, { "cell_type": "code", "execution_count": 168, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4×3 Array{Float64,2}:\n", " 0.0 0.0 0.0\n", " 0.0 0.0 0.0\n", " 0.0 0.0 0.0\n", " 0.0 0.0 0.0" ] }, "execution_count": 168, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = zeros(Float64, (4, 3))" ] }, { "cell_type": "code", "execution_count": 169, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4×3×2 Array{Float64,3}:\n", "[:, :, 1] =\n", " 0.0 0.0 0.0\n", " 0.0 0.0 0.0\n", " 0.0 0.0 0.0\n", " 0.0 0.0 0.0\n", "\n", "[:, :, 2] =\n", " 0.0 0.0 0.0\n", " 0.0 0.0 0.0\n", " 0.0 0.0 0.0\n", " 0.0 0.0 0.0" ] }, "execution_count": 169, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = zeros(Float64, (4, 3, 2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "同様に`1`で埋められた配列が欲しい場合は`ones`です。" ] }, { "cell_type": "code", "execution_count": 170, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4-element Array{Float64,1}:\n", " 1.0\n", " 1.0\n", " 1.0\n", " 1.0" ] }, "execution_count": 170, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ones(Float64, 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "LinearAlgebraモジュールにある`I`という定数は,単位行列のように振る舞います。" ] }, { "cell_type": "code", "execution_count": 171, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×3 Array{Float64,2}:\n", " 4.0 0.0 0.0\n", " 0.0 4.0 0.0\n", " 0.0 0.0 4.0" ] }, "execution_count": 171, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using LinearAlgebra\n", "zeros(3, 3) + 4I" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "もうちょっと複雑な初期化をしたい場合は、内包表記を使うのが便利でしょう。" ] }, { "cell_type": "code", "execution_count": 172, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4×3 Array{Float64,2}:\n", " 0.0 0.0 0.0\n", " 1.0 0.0 0.0\n", " 1.0 1.0 0.0\n", " 1.0 1.0 1.0" ] }, "execution_count": 172, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[Float64(i > j) for i in 1:4, j in 1:3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "特に初期化が必要ない場合は、`Array`のコンストラクタを呼ぶこともできます。" ] }, { "cell_type": "code", "execution_count": 173, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×4 Array{Float64,2}:\n", " 4.94066e-324 4.94066e-324 4.94066e-324 4.94066e-324\n", " 4.94066e-324 0.0 0.0 0.0 \n", " 9.88131e-324 0.0 4.94066e-324 0.0 " ] }, "execution_count": 173, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Array{Float64}(undef, (3, 4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Float64`型はよく使うので、省略可能です。" ] }, { "cell_type": "code", "execution_count": 174, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×4 Array{Float64,2}:\n", " 1.0 1.0 1.0 1.0\n", " 1.0 1.0 1.0 1.0\n", " 1.0 1.0 1.0 1.0" ] }, "execution_count": 174, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ones((3, 4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 線形代数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ベクトルや配列の和や積は、数値の時のように行えます。" ] }, { "cell_type": "code", "execution_count": 175, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 1.0\n", " 2.0\n", " 3.0" ] }, "execution_count": 175, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = [1.0, 2.0, 3.0]" ] }, { "cell_type": "code", "execution_count": 176, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 2.0\n", " 3.0\n", " 4.0" ] }, "execution_count": 176, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x .+ 1.0" ] }, { "cell_type": "code", "execution_count": 177, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 0.0\n", " 1.0\n", " 2.0" ] }, "execution_count": 177, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x .- 1.0" ] }, { "cell_type": "code", "execution_count": 178, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 5.0\n", " 10.0\n", " 15.0" ] }, "execution_count": 178, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x * 5" ] }, { "cell_type": "code", "execution_count": 179, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 5.0\n", " 10.0\n", " 15.0" ] }, "execution_count": 179, "metadata": {}, "output_type": "execute_result" } ], "source": [ "5x" ] }, { "cell_type": "code", "execution_count": 180, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 0.5\n", " 1.0\n", " 1.5" ] }, "execution_count": 180, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x / 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ベクトルどうしの和も計算できます。" ] }, { "cell_type": "code", "execution_count": 181, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 5.0\n", " 10.0\n", " 15.0" ] }, "execution_count": 181, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x + 4x" ] }, { "cell_type": "code", "execution_count": 182, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 1.4 \n", " 2.3999999999999995\n", " 3.4 " ] }, "execution_count": 182, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0.3 * (x .- 1) + 0.7 * (x .+ 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ひとつ気をつけなければいけないことは、ベクトルの要素ごとの積(アダマール積)は、`.*`を使わなければならないということです。これは、`*`がベクトルや行列自体の積に使われているためです。" ] }, { "cell_type": "code", "execution_count": 183, "metadata": {}, "outputs": [ { "ename": "DimensionMismatch", "evalue": "DimensionMismatch(\"Cannot multiply two vectors\")", "output_type": "error", "traceback": [ "DimensionMismatch(\"Cannot multiply two vectors\")", "", "Stacktrace:", " [1] *(::Array{Float64,1}, ::Array{Float64,1}) at /Users/osx/buildbot/slave/package_osx64/build/usr/share/julia/stdlib/v0.7/LinearAlgebra/src/deprecated.jl:566", " [2] top-level scope at In[183]:1" ] } ], "source": [ "x * 2x # 2つの列ベクトルの積は定義されていない" ] }, { "cell_type": "code", "execution_count": 184, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 2.0\n", " 8.0\n", " 18.0" ] }, "execution_count": 184, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x .* 2x # 2つの列ベクトルの要素毎の積は定義されている" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "要素ごとの除算も同様です。" ] }, { "cell_type": "code", "execution_count": 185, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 2.0\n", " 2.0\n", " 2.0" ] }, "execution_count": 185, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2x ./ x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "内積" ] }, { "cell_type": "code", "execution_count": 186, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "14.0" ] }, "execution_count": 186, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dot(x, x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "行列とベクトルの積" ] }, { "cell_type": "code", "execution_count": 187, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×3 Array{Float64,2}:\n", " 2.0 3.0 4.0\n", " 3.0 4.0 5.0" ] }, "execution_count": 187, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = [Float64(i + j) for i in 1:2, j in 1:3]" ] }, { "cell_type": "code", "execution_count": 188, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element Array{Float64,1}:\n", " 20.0\n", " 26.0" ] }, "execution_count": 188, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A * x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "行列の積" ] }, { "cell_type": "code", "execution_count": 189, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×3 Array{Float64,2}:\n", " 13.0 18.0 23.0\n", " 18.0 25.0 32.0\n", " 23.0 32.0 41.0" ] }, "execution_count": 189, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A' * A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "行列の積は次のように`*`を省略しても書けます。" ] }, { "cell_type": "code", "execution_count": 190, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×3 Array{Float64,2}:\n", " 13.0 18.0 23.0\n", " 18.0 25.0 32.0\n", " 23.0 32.0 41.0" ] }, "execution_count": 190, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A'A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "一次線形方程式 $A x = b$ の解 $x$" ] }, { "cell_type": "code", "execution_count": 191, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " 1.0000000000000142\n", " 2.000000000000001 \n", " 2.9999999999999902" ] }, "execution_count": 191, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = [20.0, 26.0]\n", "A \\ b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## モジュール" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "すべての名前が一つの場所にあると、関数名などが衝突して予期せぬ動作をすることがあります。\n", "特に、Juliaのように多重ディスパッチがあるときは問題が複雑になりかねません。\n", "そういった問題を回避するためにも、名前空間を分けるJuliaのモジュールシステムを理解することは重要です。\n", "\n", "Juliaでは、モジュールは`module ... end`で明示的に定義します。\n", "`module`と`end`で囲まれた部分のコードは、他の名前空間とは別の名前空間になります。\n", "つまり、型や関数名は他のモジュールと同じ名前であっても衝突しません。" ] }, { "cell_type": "code", "execution_count": 192, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Main.ModFoo" ] }, "execution_count": 192, "metadata": {}, "output_type": "execute_result" } ], "source": [ "module ModFoo\n", "\n", "struct Foo\n", " x\n", "end\n", "\n", "function show(x)\n", " print(\"!! \" * string(x) * \" !!\")\n", "end\n", "\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "あるモジュール内の型や関数にアクセスするときは、`<モジュール名>.<型/関数名>`というようにアクセスします。" ] }, { "cell_type": "code", "execution_count": 193, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Main.ModFoo.Foo(\"something\")" ] }, "execution_count": 193, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = ModFoo.Foo(\"something\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "このとき、関数`show`は元々用意されている関数`show`とは衝突していません。" ] }, { "cell_type": "code", "execution_count": 194, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "!! Main.ModFoo.Foo(\"something\") !!" ] } ], "source": [ "ModFoo.show(f)" ] }, { "cell_type": "code", "execution_count": 195, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Main.ModFoo.Foo(\"something\")" ] } ], "source": [ "show(f)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "では、`ModFoo`というモジュール名自体にはどのようにアクセスしているのでしょうか。\n", "Juliaには、デフォルトのモジュールが既に用意されており、`Main`と呼ばれています。\n", "今まで書いてきた変数・関数・モジュールはすべてこの`Main`モジュールに紐付けられており、ここを探索しているというわけです。" ] }, { "cell_type": "code", "execution_count": 196, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Main" ] }, "execution_count": 196, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Main" ] }, { "cell_type": "code", "execution_count": 197, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Int64,1}:\n", " 1\n", " 2\n", " 3" ] }, "execution_count": 197, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Main.quicksort([3,1,2])" ] }, { "cell_type": "code", "execution_count": 198, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Main.ModFoo.Foo(\"something\")" ] }, "execution_count": 198, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Main.f" ] }, { "cell_type": "code", "execution_count": 199, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Main.ModFoo" ] }, "execution_count": 199, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Main.ModFoo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### モジュールの使い方" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "以下の様なモジュール`Geo`を定義したとしましょう。" ] }, { "cell_type": "code", "execution_count": 200, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Main.Geo" ] }, "execution_count": 200, "metadata": {}, "output_type": "execute_result" } ], "source": [ "module Geo\n", "\n", "export Point, distance, iswithin, move\n", "\n", "struct Point\n", " x::Float64\n", " y::Float64\n", " z::Float64\n", "end\n", "\n", "distance(a::Point, b::Point) = sqrt(sqdist(a, b))\n", "\n", "iswithin(p::Point, c::Point, r::Real) = sqdist(p, c) <= r^2\n", "\n", "function move(p::Point, dx, dy, dz)\n", " return Point(p.x + dx, p.y + dy, p.z + dz)\n", "end\n", "\n", "sqdist(a::Point, b::Point) = (a.x - b.x)^2 + (a.y - b.y)^2 + (a.z - b.z)^2\n", "\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "このモジュールで定義されている型や関数を、現在のモジュール(`Main`)で使いたいときは、`using`を使います。" ] }, { "cell_type": "code", "execution_count": 201, "metadata": {}, "outputs": [], "source": [ "using .Geo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`using`を使うと、`export`で指定された名前が`Geo.`をつけなくても利用可能になります。" ] }, { "cell_type": "code", "execution_count": 202, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Point(1.0, 1.0, 1.0)" ] }, "execution_count": 202, "metadata": {}, "output_type": "execute_result" } ], "source": [ "po = Point(0, 0, 0)\n", "p1 = Point(1, 1, 1)" ] }, { "cell_type": "code", "execution_count": 203, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.7320508075688772" ] }, "execution_count": 203, "metadata": {}, "output_type": "execute_result" } ], "source": [ "distance(po, p1)" ] }, { "cell_type": "code", "execution_count": 204, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "false" ] }, "execution_count": 204, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iswithin(p1, po, 1.7)" ] }, { "cell_type": "code", "execution_count": 205, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Point(0.0, 1.0, 1.0)" ] }, "execution_count": 205, "metadata": {}, "output_type": "execute_result" } ], "source": [ "move(p1, -1, 0, 0)" ] }, { "cell_type": "code", "execution_count": 206, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "false" ] }, "execution_count": 206, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iswithin(p1, po, 1.7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "しかし、使える関数は3行目の`export`で指定されたものだけです。`sqdist`は`export`に指定されていないため使えません。" ] }, { "cell_type": "code", "execution_count": 207, "metadata": {}, "outputs": [ { "ename": "UndefVarError", "evalue": "UndefVarError: sqdist not defined", "output_type": "error", "traceback": [ "UndefVarError: sqdist not defined", "", "Stacktrace:", " [1] top-level scope at In[207]:1" ] } ], "source": [ "sqdist(po, p1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`export`されていない場合は、モジュール名もつける必要があります。" ] }, { "cell_type": "code", "execution_count": 208, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.0" ] }, "execution_count": 208, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Geo.sqdist(po, p1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### パッケージ" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "人の作ったモジュールを使うことで、開発の時間は短縮できます。\n", "再利用を目的としたモジュールの集まりは、ひとつのパッケージにまとめられGitHubを中心に配布されています。\n", "パッケージは`METADATA`というレポジトリに集められ、JuliaのREPLから`Pkg.add`関数を呼ぶだけで簡単にインストールできます。\n", "\n", "パッケージを探すときは、を参照してみてください。\n", "\n", "ここでは、[Optim.jl](https://github.com/JuliaOpt/Optim.jl)パッケージを利用して、関数の数値最適化をしてみましょう。\n", "まずはインストールです。" ] }, { "cell_type": "code", "execution_count": 209, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m registry at `~/.julia/registries/General`\n", "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m git-repo `https://github.com/JuliaRegistries/General.git`\n", "\u001b[?25l\u001b[2K\u001b[?25h\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m git-repo `git@github.com:bicycle1885/CellFishing.jl.git`\n", "\u001b[?25l\u001b[2K\u001b[?25h\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n", "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m `~/.julia/environments/v0.7/Project.toml`\n", "\u001b[90m [no changes]\u001b[39m\n", "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m `~/.julia/environments/v0.7/Manifest.toml`\n", "\u001b[90m [no changes]\u001b[39m\n", "\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n", "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m `~/.julia/environments/v0.7/Project.toml`\n", "\u001b[90m [no changes]\u001b[39m\n", "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m `~/.julia/environments/v0.7/Manifest.toml`\n", "\u001b[90m [no changes]\u001b[39m\n" ] } ], "source": [ "# 最初に実行するときだけ、下のコメントを消して実行して下さい\n", "using Pkg\n", "Pkg.update()\n", "Pkg.add(\"Optim\")" ] }, { "cell_type": "code", "execution_count": 210, "metadata": {}, "outputs": [], "source": [ "using Optim # インストール直後の読み込みはやや時間がかかる" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Rosenbrock関数](http://en.wikipedia.org/wiki/Rosenbrock_function)を最適化してみましょう。\n", "この関数は以下のように定義され、 $(x, y) = (a, a^2)$ で最小値をとります。\n", "\n", "$$f(x, y) = (a-x)^2 + b(y-x^2)^2$$" ] }, { "cell_type": "code", "execution_count": 211, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "func (generic function with 3 methods)" ] }, "execution_count": 211, "metadata": {}, "output_type": "execute_result" } ], "source": [ "func(x, a=1, b=100) = (a - x[1])^2 + b * (x[2] - x[1]^2)^2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Optim.jlでは、`optimize`関数を`export`しており、Nelder-Meadアルゴリズムを使って、実際の最小解 $(1, 1)$ をほぼ達成できています。" ] }, { "cell_type": "code", "execution_count": 212, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Results of Optimization Algorithm\n", " * Algorithm: Nelder-Mead\n", " * Starting Point: [10.0,10.0]\n", " * Minimizer: [1.0000076575345525,1.0000090769441363]\n", " * Minimum: 3.950131e-09\n", " * Iterations: 103\n", " * Convergence: true\n", " * √(Σ(yᵢ-ȳ)²)/n < 1.0e-08: true\n", " * Reached Maximum Number of Iterations: false\n", " * Objective Calls: 198" ] }, "execution_count": 212, "metadata": {}, "output_type": "execute_result" } ], "source": [ "optimize(func, [10.0, 10.0])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "code", "execution_count": 213, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Julia Version 0.7.0\n", "Commit a4cb80f3ed (2018-08-08 06:46 UTC)\n", "Platform Info:\n", " OS: macOS (x86_64-apple-darwin14.5.0)\n", " CPU: Intel(R) Core(TM) i5-6267U CPU @ 2.90GHz\n", " WORD_SIZE: 64\n", " LIBM: libopenlibm\n", " LLVM: libLLVM-6.0.0 (ORCJIT, skylake)\n", "Environment:\n", " JULIA_SHELL = /bin/bash\n" ] } ], "source": [ "versioninfo()" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Julia 0.7.0", "language": "julia", "name": "julia-0.7" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "0.7.0" } }, "nbformat": 4, "nbformat_minor": 2 }