🍡

Julia でいろんな繰り返し処理・イテレーションを書く

2024/05/26に公開

概要

Julia では(邪道を含めて)いろんな繰り返し処理・イテレーションの書き方ができます。思いつく限りの書き方を列挙していきます。次のイテラブルなオブジェクトを考えます。

xs = "あいうえお"

一覧

for =

一番最初に習う書き方です。C 風です。

for x = xs
    println(x)
end

for in

次に習う書き方です。一番一般的です。Python 風です。

for x in xs
    println(x)
end

for ∈

LaTeX で言うところの \in です。REPL でも \in で入力します。数学風です。

for x ∈ xs
    println(x)
end

while

for よりも細かい制御をする場合には whlie が必要になります。一般には iterate を使って返却値が nothing になるまで処理をします。

i = iterate(xs)
while !isnothing(i)
    x, state = i
    println(x)
    i = iterate(xs, state)
end

goto

一般には多重ループから抜け出すときぐらいしか使わない @goto ですが、もちろん繰り返し処理にも使えます。古い秘伝のスパゲティソースをそのまま移植したいときにどうぞ。

let
    i = iterate(xs)
    @label loop
    x, state = i
    println(x)
    i = iterate(xs, state)
    if !isnothing(i)
        @goto loop
    end
end

recursive

goto の代わりに関数の再帰でも書けます。関数の書き方にも色々ありますが、それは省略します。

function f(xs, i)
    if isnothing(i)
        nothing
    else
        x, state = i
        println(x)
        i = iterate(xs, state)
        f(xs, i)
    end
end

i = iterate(xs)
f(xs, i)

foreach

単純な処理しかしない場合には for を使うよりも短くなります。ちょっと関数型言語風に書きたいときにどうぞ。後述する map と違って値は返しません(nothing を返す)。

foreach(println, xs)

map

関数型風に書くときに便利な map です。一般的な iterable に対してイテレートするには collect する必要があります。foreach と違って、値を返します。もちろんこの println の場合に返ってくるのは役に立たない型 Vector{Nothing} の配列です。

map(println, collect(xs))

foreach do

do を使うと第一引数として渡す関数の処理を書き下すことができます。foreach に渡す関数がその場でしか使わない場合にどうぞ。for と役割は被っています。

foreach(xs) do x
    println(x)
end

map do

さっきの map 版です。forforeach では値を返せない一方で、こちらでは値を返せるので役割が被っているということはないです。

map(collect(xs)) do x
    println(x)
end

comprehension for =

内包 (comprehension) 記法というやつです。値を返します。

[println(x) for x = xs]

comprehension for in

さっきの in 版です。Python 風です。

[println(x) for x in xs]

comprehension for ∈

さっきの 版です。もともと数学の記法(例えば、\left\{ x - y \mid \forall x, y \in \mathbb{N} \right\} = \mathbb{Z})ですから、これが元祖というべきか。

[println(x) for x ∈ xs]

fold

この辺から、一般の手続き的な処理をするには邪道です。関数型言語でよく見るやつです。値を返します。

foldl((x, y) -> println(y), xs, init=nothing)

mapfold

一般のイテレート処理をするには邪道だと思います。数式などで使いましょう。値を返します。

mapfoldl(println, (x, y) -> nothing, xs, init=nothing)

accumulate

邪道です。

accumulate((x, y) -> println(y), xs, init=nothing)

Iterators.map

map の 遅延評価版です。

collect(Iterators.map(println, xs))

Iterators.accumulate

accumulate の 遅延評価版です。

collect(Iterators.accumulate((x, y) -> println(y), xs, init=nothing))

まとめ

合計 18 種類ものイテレート処理の書き方を見てきました。使ったことのあるやつもあればないやつもありました。他にもあったら教えて下さい。

Discussion