Week 4: Julia言語入門2
今日やること
はじめに
- 5/10は休講にしたいと思います。7/16のどこか、あるいは7/17の1限だとどうか、皆さんの予定を伺います。 
- 先週AWS SageMaker Studio Labsの使い方を学びました。Studio Labsは時間制限があるため、もし時間を気にせずガッツリプログラミングしたいということであれば、juliaを手元のマシンに直接インストールすることもオススメです。マックの人、およびwindowsで直でつかいたい人は公式サイトからバイナリファイルをダウンロードしてインストールできます。WindowsでWSLを使っている人は、WSLの内側でLinuxとしてインストールできます。 
算術演算子・代入演算子
さて、Week3で少し触れた演算子についてみていきましょう。 まずは四則演算などを実行する算術演算子です。以下のように、数学と同様に和・差・積・商を計算することができます。
a = 8
b = 3
println(a + b)
println(a - b)
println(a * b)  # 掛け算は「*」です
println(a / b)この結果は以下になります。
11
5
24
2.6666666666666665計算機は実数の割り算を正確に計算できないので、最後の例は近似的な数字になっていることがわかります。
また、「割ったあまり」を計算するには%を使います。
a % b2また、^を用いて累乗を計算することもできます。
a ^ b512上記のうち、和と差については、素朴にベクトル・行列に適用できます。
x = [1; 2; 3]
y = [4; 5; 6]として、
x + y3-element Vector{Int64}:
 5
 7
 9となり、また
x - y3-element Vector{Int64}:
 -3
 -3
 -3となります。加えて、スカラー倍も素直に定義されます。
2 * x3-element Vector{Int64}:
 2
 4
 6先週やった通り、行列積も*にて素直に計算できます。
A = [1 2 3; 4 5 6]
B = [7 8; 9 10; 11 12]
A * B2×2 Matrix{Int64}:
  58   64
 139  154次に、値を代入する代入演算子を見てみましょう。代入には=を用います。繰り返しになりますが、これは数学のとは違う概念です。
x = a + 311また、自分自身への代入を行うこともできます。例えば
x = x + 314このような処理は、省略して次のように書けます。
x += 317同様に、-=, *=, /=, ^=なども使えます。
やってみよう:
上記を写経してみましょう。
算術演算子・代入演算子は、整数ではなく小数に対しても適用できます。やってみましょう。
行列・ベクトルに対しても
+=といった表記を適用することができます。やってみましょう。
「横ベクトル * 縦ベクトル」は内積になり、一つの整数が得られます。「縦ベクトル * 横ベクトル」は直積となり、行列が得られます。「縦ベクトル * 縦ベクトル」および「横ベクトル * 横ベクトル」は計算できません。このあたりは全て通常の数学と同じです。
juliaには他にも様々な演算子があります。調べて実行してみましょう。
行列・ベクトルの初期化
さて、次に行列やベクトルの初期化について見ていきましょう。既にこれまで見ている通り、行列・ベクトルは直接要素を書くことで初期化できます。
x = [1; 2; 3]3-element Vector{Int64}:
 1
 2
 3他にも、次のような初期化が可能です。まず、onesを使うことで、要素が全て1の行列作れます。
ones(2, 3)2×3 Matrix{Float64}:
 1.0  1.0  1.0
 1.0  1.0  1.0ここで、2, 3は行列の形状を表しています。要素が全てある値(例えば3)である行列を作りたければ、onesに3を掛ければ大丈夫です。
3 * ones(2, 3)2×3 Matrix{Float64}:
 3.0  3.0  3.0
 3.0  3.0  3.0また、zerosを使うことで、要素が全て0の行列を作れます。
zeros(3, 5)3×5 Matrix{Float64}:
 0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0randを使うことで、要素が0から1の間のランダムな値を持つ行列を作れます。
rand(3, 4)3×4 Matrix{Float64}:
 0.404049  0.591327  0.341229  0.145054
 0.721222  0.613711  0.561991  0.966213
 0.746307  0.151635  0.087969  0.844366また、reshape(1:12, 3, 4)というような表記を使うことで、以下のように連続する数を折りたたんだ行列を作れます。
reshape(1:12, 3, 4)3×4 reshape(::UnitRange{Int64}, 3, 4) with eltype Int64:
 1  4  7  10
 2  5  8  11
 3  6  9  12これは、1:12という表記で1から12までの連続する数を作り、それをreshapeという関数で並べ直して行列を構成しています。 とりあえず今の段階ではreshape(1:NM, N, M)という表記で、N行M列の行列を作れるんだなということを覚えておいてください。
実は縦ベクトルは、[1; 2; 3]とセミコロンで区切る他に、[1, 2, 3]とコンマで区切った形でも作れます。
[1, 2, 3]3-element Vector{Int64}:
 1
 2
 3実はプログラミング的にはこちらのほうが素直で最初に来る表記になります。ですが、行列を書く際にはコンマは使えません。
A = [1 2 3, 4 5 6]これを実行すると、次のようにエラーになります。
ERROR: syntax: unexpected comma in array expression
Stacktrace:
 [1] top-level scope
   @ none:1よって本講義では、行列を表記する際の表記であるセミコロンを、ベクトルを作る際にも使っていきます。
やってみよう:
上記を写経してみましょう。
様々に初期化した行列・ベクトルを使って、足したり引いたりを実行してみましょう。
要素アクセス
さて、ベクトルや行列への要素アクセスについてみてみましょう。まず縦ベクトルを作ります。
x = [10; 20; 30; 40; 50]この結果は以下になりますね。
5-element Vector{Int64}:
 10
 20
 30
 40
 50ここで、このベクトルの1つ目の要素にアクセスするには次のようにします。
x[1]その結果は以下のようになり、1つ目の要素が取り出せていることがわかります。
10ここでは要素が5つあるため、最後の要素にアクセスするにはx[5]とします。また、最後の要素の省略表記としてx[end]とすることもできます。
x[end]これはx[5]と同じ結果を返します。
50a[0]とします。juliaはMATLABの影響を受けており、要素は1から始まります。ベクトルのある部分を切り抜くこともできます。そのような処理はスライスと呼ばれます。例えば、2つ目から4つ目までの要素を取り出すには次のようにします。
x[2:4]この結果は以下のようになります。
3-element Vector{Int64}:
 20
 30
 40上記の議論は横ベクトルでも成り立ちます。
y = [100 200 300 400 500]1×5 Matrix{Int64}:
 100  200  300  400  500ここで
println(y[1])
println(y[end])100
500となります。
一点注意なのが、横ベクトルに対するスライスは縦ベクトルになります(!)
y[2:4]3-element Vector{Int64}:
 200
 300
 400これはjuliaにおいては横ベクトルの表記はベクトル(Vector)ではなく行列(Matrix)になるからなのですが、あまり気にしないでください。横ベクトルのままスライスしたい場合は、 転置の記号である'を使って明示的に転置する
y[2:4]'1×3 adjoint(::Vector{Int64}):
 200  300  400か、行列のままスライスする以下の表記を使ってください。
y[1:1, 2:4]1×3 Matrix{Int64}:
 200  300  400上記ではadjointだとかMatrixだとか色々出てきますが、このあたりは気にしなくて良い(気にしなくても大丈夫であるべき)というのがjuliaの思想なので、行列の形状さえ揃うのであれば気にしないでください。
さて、行列に対しても要素アクセスやスライスが適用可能です。
A = [1 2 3 4; 5 6 7 8; 9 10 11 12]3×4 Matrix{Int64}:
 1   2   3   4
 5   6   7   8
 9  10  11  12行列の場合は、i行目のj列にアクセスする場合にA[i, j]といった表記を使います。
A[2, 3]7スライスも出来ます。ある行を切り抜く場合、
A[2:2, 2:4]  # 「2行目」の「2列目から4列目」1×3 Matrix{Int64}:
 6  7  8とします。ある列を切り抜く場合、
A[1:2, 3:3]  # 「3列目」の「1行目から2行目」2×1 Matrix{Int64}:
 3
 7とします。また、同様に、行列の小行列(ある部分行列)を切り抜くこともできます。
A[2:3, 2:4]2×3 Matrix{Int64}:
  6   7   8
 10  11  12となります。
このスライスという処理は切り抜くだけでなく代入のための位置指定にも使えます。例えば、
B = [100 100 100; 100 100 100]2×3 Matrix{Int64}:
 100 100 100
 100 100 100として、
A[2:3, 2:4] = Bとすると、画面には代入したBが表示されます。
2×3 Matrix{Int64}:
 100  100  100
 100  100  100ここでAがどうなったか確かめてみましょう
A3×4 Matrix{Int64}:
 1    2    3    4
 5  100  100  100
 9  100  100  100このように、Aの一部分がBに置き換わっていることがわかります。
やってみよう:
上記を写経してみましょう。
ベクトル 、、および行列 を考えます。行列を作り、それらの要素を表示してみましょう。ここでは、まず4x4のゼロ行列を定義したうえで、ベクトルや行列を代入することで行列を作成してください。
文字列
さて、juliaを始めとしたプログラミング言語では、数値以外にも「文字」を扱うことができます。「文字」とは「あ」とか「a」といった単一の語を表し、「文字列」というと文字が並んだものを指します。 juliaでは、他の多くの言語と同じように、ダブルクオーテーションである"で囲むことで文字列を表現します。
"abc"こうすると、以下のように単にそれを繰り返してくれるでしょう。
"abc"また、文字列は変数に代入もできますし、printlnに入れて表示することもできます。
s = "def"
println(s)defprintlnは複数のものをコンマ区切りで表示してくれるので、たとえば以下のようなこともできます。
a = 3.5
println("The value is ", a)こうすると以下のようになります。
The value is 3.5文字列は、*の演算子を使って連結することができます。
"abc" * "def""abcdef"この挙動はちょっと不思議かもしれない(足し算の+を使うほうが自然な気がする)ですが、とりあえず気にしないでください。
また、英数字の文字列に関して、それをベクトルだと思って要素を取得することができます。
myname = "Yusuke Matsui"として以下を実行してみましょう。
println(myname[3])
println(myname[3:5])すると以下のようになります。
s
sukやってみよう:
上記を写経してみましょう。
いろいろな文字列を作ってみて、それらに対する連結や要素取得を試してみてください。
endなども使えることを確認しましょう。
日本語の文字列に対しては、要素取得しようとするとどうなるでしょうか。たとえば
s = "あいうえお"として、s[1]やs[2]を見てみましょう。
次回までの宿題
次回は線形代数の問題について、チームで取り組んでもらいます。現在線形代数の講義で使っている教科書とか、あるいは発展的なトピックでもよいので関連しそうな数学の本などを持ってきてください。