どのようにしてエリキシル剤に遅延パターンを導入できますか?エリクサーに遅延パターンを実装する方法は?
私はそれが何であるか説明しましょう。私はいくつかのfn()
が実装されるべきであるとしましょう。それ以降はn
秒遅れます。しかし、私がこのfn()
を2度目に呼び出すと、この関数は2回目の呼び出し後にn
秒に実装されるべきです。この関数評価をすべて終了する方法もあるはずです。
Lodashの_.debounce関数を参照してください。ここ
どのようにしてエリキシル剤に遅延パターンを導入できますか?エリクサーに遅延パターンを実装する方法は?
私はそれが何であるか説明しましょう。私はいくつかのfn()
が実装されるべきであるとしましょう。それ以降はn
秒遅れます。しかし、私がこのfn()
を2度目に呼び出すと、この関数は2回目の呼び出し後にn
秒に実装されるべきです。この関数評価をすべて終了する方法もあるはずです。
Lodashの_.debounce関数を参照してください。ここ
オーケーあなたが軌道に乗るように簡略化する場合である:ここ n
をご遅延を確認するために、大きなn
年代を必要としていますので、秒が、ループ・ステップでではありません。ここでは、関数を呼び出す例としてIO.puts
を使用します。
defmodule myModule do
def loop(list,count) do
receive do
n -> list = list ++ n
:die ->
Process.exit(self(), :kill)
end
if count == 0 do
IO.puts("timeout")
[head|tail] = list
loop(tail, head)
else
loop(list, count-1)
end
end
end
これはどのようにしてOPが要求したものですか?質問は単純な遅延ではありません。 – michalmuskala
@michalmuskala各遅延タイムアウト後に関数の例としてIO.putsを呼び出します。もちろん、最初にプロセスを生成し、必要なときにn値のpidメッセージを送信する必要があります。 – GavinBrelstaff
状態を保存するには、プロセスが必要です。単純な関数では十分ではありません。このためのプロセスを作成すると、わずか数行のコードである:
defmodule Debounce do
def start_link(f, timeout) do
spawn_link(__MODULE__, :loop, [f, timeout])
end
def loop(f, timeout) do
receive do
:bounce -> loop(f, timeout)
:exit -> :ok
after timeout ->
f.()
end
end
end
あなたは、このプロセス:bounce
を送信することができ、それがDebounce.start_link/2
で指定されたものにそのタイムアウトをリセットします。また、このプロセスを:exit
に送信することもできます。機能を実行しないで終了します。
テスト:
f = Debounce.start_link(fn -> IO.inspect(:executing) end, 1000)
IO.puts 1
send f, :bounce
:timer.sleep(500)
IO.puts 2
send f, :bounce
:timer.sleep(500)
IO.puts 3
send f, :bounce
:timer.sleep(500)
IO.puts 4
send f, :bounce
:timer.sleep(2000)
IO.puts 5
出力:
1
2
3
4
:executing
5
非常に素朴でシンプルなソリューションは、生のプロセスを使用することができます。
defmodule Debounce do
def start(fun, timeout) do
ref = make_ref()
# this function is invoked when we wait for a next application
recur = fn recur, run ->
receive do
^ref ->
# let's start counting!
run.(recur, run)
end
end
# this function is invoked when we "swallow" next applications
# and wait until we finally apply the function
run = fn recur, run ->
receive do
^ref ->
# let's reset the counter
run.(recur, run)
after
timeout ->
# time is up, let's call it for real & return to waiting
fun.()
recur.(recur, run)
end
end
pid = spawn_link(fn -> recur.(recur, run) end)
fn -> send(pid, ref) end
end
end
のは
iex> f = Debounce.start(fn -> IO.puts("Hello"), 5000)
iex> f.()
iex> f.()
# wait some time
Hello
iex> f.() # wait some time
Hello
例を見てみましょうしかし、これは多くの問題を抱えている - 私たちの「デバウンサ」プロセスは、効果的に永遠に住んでいる、我々は、デバウンス性と信頼性は、せいぜい、大ざっぱでキャンセルすることはできません。私たちは改善することができますが、私たちが呼び出すことができる簡単な楽しみの戻り値を失い、その代わりに私たちのデバウンサーを "適用"する特別な関数を呼び出す必要があります。
defmodule Debounce do
def start(fun, timeout) do
ref = make_ref()
# this function is invoked when we wait for a next application
recur = fn recur, run ->
receive do
{^ref, :run} ->
# let's start counting!
run.(recur, run)
{^ref, :cancel} ->
:cancelled
end
end
# this function is invoked when we "swallow" next applications
# and wait until we finally apply the function
run = fn recur, run ->
receive do
{^ref, :run} ->
# let's reset the counter
run.(recur, run)
{^ref, :cancel} ->
:cancelled
after
timeout ->
# time is up, let's call it for real & return to waiting
fun.()
recur.(recur, run)
end
end
pid = spawn_link(fn -> recur.(recur, run) end)
{pid, ref}
end
def apply({pid, ref}) do
send(pid, {ref, :run})
end
def cancel({pid, ref}) do
send(pid, {ref, :cancel})
end
end
の例を見てみましょう:
iex> deb = Debounce.start(fn -> IO.puts("Hello"), 5000)
iex> Debounce.apply(deb)
iex> Debounce.apply(deb)
# wait some time
Hello
iex> Debounce.apply(deb)
iex> Debounce.cancel(deb)
# wait some time
# nothing
これはまだいくつかの可能なコーナーケースを持っている - 製品版は、おそらく、タスクやGenServerを使用します。
私の頭の上から離れて、あなたは単純なgenserverでそれを実装できます。たぶんシンプルな方法があります。 – JustMichael
ええ、ええ、私はより単純な抽象化を探しています – asiniy
プロセスを望む機能は望んでいません。 http://elixir-lang.org/getting-started/processes.htmlプロセスが現在の値をカウントダウンする間に、n - の新しい入力値も受け取ります。この値は、ローカル状態で維持されるリストに置きます。カウントダウンが完了すると、リストの次のメンバーに進み、もう一度受信しながらダウンをカウントします。 – GavinBrelstaff