Tasks
を使用すると、遅延コレクション/ジェネレータを表現するのが非常に便利です。レイジーコレクションの `Task/produce/consume`をコルーチンとして表現するよりも良い方法
例:
function fib()
Task() do
prev_prev = 0
prev = 1
produce(prev)
while true
cur = prev_prev + prev
produce(cur)
prev_prev = prev
prev = cur
end
end
end
collect(take(fib(), 10))
出力:
10-element Array{Int64,1}:
1
1
2
3
5
8
13
21
34
しかし、彼らはすべての良いイテレータの規則に従いません。 彼らは
彼らは彼らではなく、イテレータオブジェクト自体を変異されて返された状態state
start(fib()) == nothing #It has no state
を使用しないことができるように彼らがひどく振る舞っています。 適切なイテレータは、それ自体を変更するのではなく、その状態を使用するため、複数の呼び出し元が一度に反復することができます。 start
でその状態を作成し、next
の間にそれを前進させます。
この状態はimmutable
となります。next
は新しい状態を返すため、普通はtee
となります。 (一方で、新しいメモリの割り当て - スタック上でも)
さらに、隠された状態では、next
の間に進んでいません。 以下は動作しません:
@show ff = fib()
@show state = start(ff)
@show next(ff, state)
出力:
ff = fib() = Task (runnable) @0x00007fa544c12230
state = start(ff) = nothing
next(ff,state) = (nothing,nothing)
が代わりに隠された状態がdone
中に進んでいる: 次のような作品:
@show ff = fib()
@show state = start(ff)
@show done(ff,state)
@show next(ff, state)
出力:
ff = fib() = Task (runnable) @0x00007fa544c12230
state = start(ff) = nothing
done(ff,state) = false
next(ff,state) = (1,nothing)
進行中のdone
の状態は、世界で最悪のことではありません。 結局のところ、次の状態を見つけようとすることなく、完了した時点を知ることが難しい場合がよくあります。 1つは、の前に常にdone
が呼び出されると思います。次の問題が発生したため、 はまだそれは、素晴らしいではありません。
ff = fib()
state = start(ff)
done(ff,state)
done(ff,state)
done(ff,state)
done(ff,state)
done(ff,state)
done(ff,state)
@show next(ff, state)
出力:本当に今、あなたが期待するものである
next(ff,state) = (8,nothing)
。 done
は複数回も安全に呼び出すことができます。
基本的にはTask
です。多くの場合、イテレータを必要とする他のコードと互換性がありません。 (多くの人はそうですが、どの人からそれを知るのは難しいです)。 Task
は実際にはこれらの「ジェネレーター」関数のイテレーターとして使用されていないためです。低レベルの制御フローを意図しています。 そのように最適化されています。
だから、もっと良い方法は何ですか?
immutable Fib end
immutable FibState
prev::Int
prevprev::Int
end
Base.start(::Fib) = FibState(0,1)
Base.done(::Fib, ::FibState) = false
function Base.next(::Fib, s::FibState)
cur = s.prev + s.prevprev
ns = FibState(cur, s.prev)
cur, ns
end
Base.iteratoreltype(::Type{Fib}) = Base.HasEltype()
Base.eltype(::Type{Fib}) = Int
Base.iteratorsize(::Type{Fib}) = Base.IsInfinite()
しかし少し小さい直感的である: fib
のためのイテレータを書くことは悪くないです。 もっと複雑な関数の場合は、それほど優れていません。
私の質問は次のとおりです。 単一の関数からイテレータを構築する方法として、タスクがそうであるように機能するものはありますが、うまく動作しますか?
これを解決するために誰かがすでにマクロを使ってパッケージを書いていても、私は驚くことはありません。