『プログラミング Elixir』の練習問題を解く。
Q.で問題を示し、#で自分が回答を導くまでの考えを示し、A.で実行結果/回答を示す
練習問題:PatternMatching-1
Q.次のどれがマッチするだろうか
Q1. a = [1, 2, 3]
Q2. a = 4
Q3. 4 = a
Q4. [a,b] = [1, 2, 3]
Q5. a = [ [ 1, 2, 3 ] ]
Q6. [a] = [ [ 1, 2, 3 ] ]
Q7. [[a]] = [ [ 1, 2, 3 ] ]
・Answer
・パターンマッチは、パターン(左辺)と値(右辺)の構造が同じで、かつパターンのそれぞれの項が、値の中の対応する項とマッチするときに成立する
Q1. a = [1, 2, 3]
# 変数aは1つの右辺の1つの配列とマッチする
A1. Match: a => [1, 2, 3]
Q2. a = 4
# 左辺aは右辺の1つの値4とマッチする
A2. Match: a => 4
Q3. 4 = a
# 基本的に左辺の値4は右辺aとはマッチしないが、事前にaを4にマッチさせておくとこの例でもマッチする
A3. ** (CompileError) iex: undefined function a/0
Q4. [a,b] = [1, 2, 3]
# 左辺の配列にはaとbの2つの項があるが、値(右辺)の項は3つあり、構造が異なるためマッチしない
A4. ** (MatchError) no match of right hand side value: [1, 2, 3]
Q5. a = [ [ 1, 2, 3 ] ]
# 左辺はaのみで、値は配列[1,2,3]を持つ配列1つだけで構造は同じで、aは配列[1,2,3]を持つ配列1つとマッチする
A5. Match: a => [[1, 2, 3]]
Q6. [a] = [ [ 1, 2, 3 ] ]
# 左辺は配列だが項はaのみで、右辺も配列だが項は配列[1,2,3]の1つのみであり左辺と右辺の構造は同じ。よって左辺の項aは配列[1,2,3]とマッチする
A6. Match: a => [1,2,3]
Q7. [[a]] = [ [ 1, 2, 3 ] ]
# 左辺は配列[a]を持つ配列、右辺は配列[1,2,3]を持つ配列で、左辺aに対して右辺が1,2,3の3つある構造となっているため、マッチしない
A7. ** (MatchError) no match of right hand side value: [[1, 2, 3]]
練習問題:PatternMatching-2
・変数の束縛はマッチ中では1度だけ
・キャレット(^)を使うと、変数の現在の値をパターンの中で使えるようになる
iex> a = 1
1
iex> [^a, 2] = [5, 2] #^aは既にあるaの値を使うから、その中の値は1
**MatchError
iex> [^a, 2] = [1, 2]
[1, 2]
Q.次のどれがマッチするだろうか
Q1. [ a, b, a ] = [ 1, 2, 3 ]
Q2. [ a, b, a ] = [ 1, 1, 2 ]
Q3. [ a, b, a ] = [ 1, 2, 1 ]
・Answer
Q1. [ a, b, a ] = [ 1, 2, 3 ]
# このマッチの中では、左辺の1つ目の項であるaは1にマッチしている。マッチ中では変数の束縛は1度だけのため、左辺3つ目の項aも1にマッチする必要があるが、上記Q1では対応する右辺の3つ目の項の値は3担っているため、マッチしない.
A1. ** (MatchError) no match of right hand side value: [1, 2, 3]
Q2. [ a, b, a ] = [ 1, 1, 2 ]
# Q1と同様に、左辺の1つ目の項でaは1とマッチしているため、左辺3つ目の項aと右辺3つ目の項2がマッチしない
A2. ** (MatchError) no match of right hand side value: [1, 1, 2]
Q3. [ a, b, a ] = [ 1, 2, 1 ]
# 左辺でaが項として2度出ているが、そのどちらも値は1であり、「変数の束縛はマッチ中で1度だけ」という条件も満たしており、マッチする
A3. Match
練習問題:PatternMatching-3
Q.変数aが、もともと2であるとき、次のどれがマッチするか
Q1. [ a, b, a ] = [ 1, 2, 3 ]
Q2. [ a, b, a ] = [ 1, 1, 3 ]
Q3. a = 1
Q4. ^a = 2
Q5. ^a = 1
Q6. ^a = 2 - a
・Answer
Q1. [ a, b, a ] = [ 1, 2, 3 ]
# 変数の束縛はあくまでマッチの中で1度だけであり、左辺の1つ目の項aは右辺の1つ目の項1とマッチする。
が、左辺3つ目の項aについては「変数の束縛はマッチ中で1度だけ」という条件があるため、右辺3つ目の項3とはマッチせず、MatchErrorになる
A1. ** (MatchError) no match of right hand side value: [1, 2, 3]
Q2. [ a, b, a ] = [ 1, 1, 2 ]
# 変数aがもともと2であっても、あくまでも「変数の束縛はマッチ中1度だけ」なので左辺1つ目の項aは右辺1つ目の項1とマッチする。
が、左辺3つ目の項もaで、それに対応する右辺の項は2であり、既にこのマッチ中でのaは1とマッチしているため、MatchErrorになる
A2. ** (MatchError) no match of right hand side value: [1, 1, 2]
Q3. a = 1
# 変数は新たな値とマッチし直すことができるため、 a = 1はマッチする
A3. Match
Q4. ^a = 2
# 変数aはもともと2であり、^aもその中の値は2であるため、マッチする
A4. Match
Q5. ^a = 1
# 変数aはもともと2であるが、新たに異なる値とマッチさせることもできる。
が、接頭語としてキャレットを変数につけた場合には、変数の現在の値を使うことになる。
そのため^aの中の値は2であり、^a = 1はMatchErrorになる
A5. ** (MatchError) no match of right hand side value: 1
Q6. ^a = 2 - a
# ^aの中の値は2で、右辺は計算すると0になり、マッチしない
A6. ** (MatchError) no match of right hand side value: 0
*等号記号の別の見方
Elixir: 等号記号は左辺と右辺をマッチさせる、束縛させるもの
Erlang的な等号記号の見方: 等号記号を代数での等号記号の使われ方に例える
例) x = a + 1
代数では、例えばa = 5の時、上記の式ではx = 6というように答えを求める。つまり、その値をx自体に代入したりはせず、単にxの値とa + 1の値が同じであることを表す。
そのため、xの値がわかっていれば、aの値を求めることもできる。
例)x=6のとき 6 = a + 1 -> a = 5
・Javaなどの手続き型プログラミングでは代数的な等号記号の意味を忘れる必要があったが、Elixirにおいては「代数的な意味を忘れるのを忘れる」必要がある