前置き
最近Juliaを始めてみたのですが、読んでいた本の中でiterate関数を用いたプログラムがあり、なかなか面白かったのでまとめておきます。iterate関数を使えばfor文を使わない繰り返し文が可能になります。
iterate関数とは
Juliaのドキュメントの中で、iterate関数の説明は以下のように書かれています。
Base.iterate — Function.
iterate(iter [, state]) -> Union{Nothing, Tuple{Any, Any}}
Advance the iterator to obtain the next element. If no elements remain, nothing should be returned. Otherwise, a 2-tuple of the next element and the new iteration state should be returned.
上記によれば、iteratorを前進させて、次の要素や状態を返す関数とのことです。ここでiteratorとは順番を持った配列で繰り返し処理をする際の指標となるものと言ったところでしょうか。iteratorに要素が残っていなければ、nothingを返すのでこちらを利用して繰り返し処理を実施することとなります。
iterate関数の実際の挙動
文を読んでいるだけでは動作は分からないので、実際にiterate関数を使ってみましょう。iteratorとしてx = [5,2,3,1,2,3,1,1]というリストを使用して動作を確認します。
[In]
# iterator
x = [5,2,3,1,2,3,1,1]
# 動作検証 itarateにiteratorとindexを与える: iterate(iterator, index)
xi = iterate(x)
x1 = iterate(x,1)
x2 = iterate(x,2)
x3 = iterate(x,3)
x4 = iterate(x,4)
x5 = iterate(x,5)
x6 = iterate(x,6)
x7 = iterate(x,7)
x8 = iterate(x,8)
x9 = iterate(x,9)
# 表示
@show(xi)
@show(x1)
@show(x2)
@show(x3)
@show(x4)
@show(x5)
@show(x6)
@show(x7)
@show(x8)
@show(x9)
[Out]
# 与えたindexに対応する要素と、indexの次の値をタプルで返す。indexに対応する要素がなければnothingを返す
# iterator x = [5,2,3,1,2,3,1,1]
# xi = iterate(x)
xi = (5, 2)
# x1 = iterate(x,1)
x1 = (5, 2)
# x2 = iterate(x,2)
x2 = (2, 3)
# x3 = iterate(x,3)
x3 = (3, 4)
# x4 = iterate(x,4)
x4 = (1, 5)
# x5 = iterate(x,5)
x5 = (2, 6)
# x6 = iterate(x,6)
x6 = (3, 7)
# x7 = iterate(x,7)
x7 = (1, 8)
# x8 = iterate(x,8)
x8 = (1, 9)
# x9 = iterate(x,9)
x9 = nothing
上記をよく見ると、iterate関数にiteratorとindexを与えると、iteratorのindexに対応する要素とindexの次の値をタプル形式で返していることが分かります。(x4を例に上げれば、iterate関数にiteratorとしてx、indexとして4を与えると、xの4番目の要素1とindexの次の値5を返しています。)
また、xiとx1の比較からindexが入力されなければindex = 1と捉えられることが分かります。
x9からはindexの値がiteratorの値を超えるとnothingを返すことが分かりました。
iterate関数を使った繰り返し文の例
Juliaのドキュメントの中にも簡単な使い方が載っていますが、適当なものを自作して遊んでみます。iterate関数を使った繰り返し文で、先ほど使用したiteratorであるxの要素を足し合わせてみましょう。プログラムと実行結果は以下のようになりました。
[In]
x = [5,2,3,1,2,3,1,1]
sumx = 0
# iterate関数により最初の要素と次のindexを取得
next = iterate(x)
# iteratorの要素がなくなるまで繰り返す
while next !== nothing
#現在の要素をi, 次のindexをstateに格納
(i, state) = next
sumx += i
#次の計算に使用する要素と次の次のindexをnextに格納
next = iterate(x, state)
end
@show(sumx)
[Out]
sumx = 18
eachindex関数、pairs関数と組み合わせた時のiterate関数
eachindex関数及びpairs関数と組み合わせると、iterate関数が特殊な振る舞いをするようでしたので記載しておきます。
eachindex関数、pairs関数の簡単な説明
eachindex関数は配列の各インデックスを効率的に訪問するためのiteratorを作製してくれる関数です。
例
[In]
x = [5,2,3,1,2,3,1,1]
indices_ei = eachindex(x)
@show(indices_ei)
[Out]
indices_ei = Base.OneTo(8)
pairs関数はkeyとなるセットと値のセットをペアリングしてたiteratorを作製してくれるです。
例
[In]
x = [5,2,3,1,2,3,1,1]
indices_pair = pairs(x)
@show(indices_pair)
[Out]
indices_pair = Base.Iterators.Pairs(1 => 5,2 => 2,3 => 3,4 => 1,5 => 2,6 => 3,7 => 1,8 => 1)
eachindex関数の詳細な説明についてはこちら、pairs関数に関してはこちらを参照ください。
iterate関数と組み合わせた場合
先述の二つの関数で作成したitaratorにiterate関数を作用した時の動作を確認します。
eachindex関数の場合:
x = [5,2,3,1,2,3,1,1]
indices_ei = eachindex(x)
yi = iterate(indices_ei)
y1 = iterate(indices_ei,1)
y2 = iterate(indices_ei,2)
y3 = iterate(indices_ei,3)
y4 = iterate(indices_ei,4)
y5 = iterate(indices_ei,5)
y6 = iterate(indices_ei,6)
y7 = iterate(indices_ei, 7)
y8 = iterate(indices_ei, 8)
@show(indices_ei)
@show(yi)
@show(y1)
@show(y2)
@show(y3)
@show(y4)
@show(y5)
@show(y6)
@show(y7)
@show(y8)
[Out]
indices_ei = Base.OneTo(8)
yi = (1, 1)
y1 = (2, 2)
y2 = (3, 3)
y3 = (4, 4)
y4 = (5, 5)
y5 = (6, 6)
y6 = (7, 7)
y7 = (8, 8)
y8 = nothing
pairs関数の場合:
[In]
x = [5,2,3,1,2,3,1,1]
indices_pair = pairs(x)
yi = iterate(indices_pair)
y1 = iterate(indices_pair,1)
y2 = iterate(indices_pair,2)
y3 = iterate(indices_pair,3)
y4 = iterate(indices_pair,4)
y5 = iterate(indices_pair,5)
y6 = iterate(indices_pair,6)
y7 = iterate(indices_pair,7)
y8 = iterate(indices_pair,8)
@show(indices_pair)
@show(yi)
@show(y1)
@show(y2)
@show(y3)
@show(y4)
@show(y5)
@show(y6)
@show(y7)
@show(y8)
[Out]
indices_pair = Base.Iterators.Pairs(1 => 5,2 => 2,3 => 3,4 => 1,5 => 2,6 => 3,7 => 1,8 => 1)
yi = (1 => 5, 1)
y1 = (2 => 2, 2)
y2 = (3 => 3, 3)
y3 = (4 => 1, 4)
y4 = (5 => 2, 5)
y5 = (6 => 3, 6)
y6 = (7 => 1, 7)
y7 = (8 => 1, 8)
y8 = nothing
eachindex関数及びpairs関数のどちらにおいても、indexに値を入れる場合とそうでない場合が区別されるようになりました。また、indexを与えると、indexの次の値に対応する要素とindexの次の値が返って来ているようです。(例えば、y2においては、index = 2を与えて、返ってくるのがeachindexでは(3,3)、pairsでは(3=>3,3)) このように、通常のリストとその他の形式を与える場合では、同じ関数の振る舞いが異なることがあるようです。
まとめ
今回はjuliaのiterate関数について色々と動作を見てみました。上述の通り、その他の関数と組み合わせて使用する場合には注意が必要です。
間違いなどがありましたらご連絡いただければ幸いです。
[参考書籍・ウェブ]
Bogumil Kaminski, Premyslaw Szufel(2019)『Juliaプログラミングクックブック』(中田秀基 訳) オライリー・ジャパン
https://docs.julialang.org/en/v1/index.html