#elixir
Вопрос:
defmodule Test do
use Retry
import Stream
def call_service(count) do
IO.puts "call_service #{count}"
%{"errors" => true}
end
def hello do
count = 0
retry_while with: linear_backoff(500, 1) |> take(5) do
count = count 1 # incorrect !!!
call_service(count)
|> case do
result = %{"errors" => true} -> {:cont, result}
result -> {:halt, result}
end
end
end
end
Приведенный выше пример приведен в take(5) do
call_service
|> case do
result = %{«errors» => true} -> {:cont, result}
result -> {:halt, result}
end
end» rel=»nofollow noreferrer»>документе модуля повторной попытки.
Что я хочу здесь сделать, так это узнать значение счетчика в функции call_service. Как вы, возможно, уже заметили, пример работает не так, как я ожидал.
Могу ли я иметь значение count, указывающее, сколько раз функция вызывается в функции call_service?
Комментарии:
1. полезно, если вы укажете, какой результат вы получили по сравнению с какого результата вы ожидали. Я не знаком с
Retry
этим , но похоже, что он использует некоторые макросы, которые имеют свои собственные ограничения на синтаксис из-за их выполнения во время компиляции. В документах, похоже, отсутствует несколько важных битов информации, например, показывающих, какие именно «атомы» должен возвращать вызов службы.2. FWIW, я предоставил PR для первоначального репо
reduce_while/3
, чтобы поддержать проходящий через аккумулятор.
Ответ №1:
Нет, вы не можете назначить переменную «вне [ее] блока» — это часть того, что это означает, когда вы слышите, что «в Эликсире все является назначением». Общая схема
# pseudo-code
x = 0
for y in z {
# do something
x = x 1
}
НЕ работает в Эликсире. Вместо этого вам часто нужно полагаться на карту и сокращать функции (часто предоставляемые модулем перечисления).
Рассмотрим этот простой модуль, который полагается на Enum
многократный вызов функции. Здесь мы используем защитное предложение, чтобы ограничить количество вызовов, которые мы фактически совершаем, даже если мы перечисляем в большем диапазоне.
defmodule Foo do
def repeat do
Enum.each(1..10, fn n ->
call_service(n)
end)
end
defp call_service(count) when count > 7 do
IO.puts("Whoops... called too many times!")
{:error, "Called too many times"}
end
defp call_service(count) do
IO.puts("Count is #{count}")
{:ok, "Called the service!"}
end
end
Если мы запустим этот код путем выполнения Foo.repeat()
, мы увидим вывод, подобный следующему:
Count is 1
Count is 2
Count is 3
Count is 4
Count is 5
Count is 6
Count is 7
Whoops... called too many times!
Whoops... called too many times!
Whoops... called too many times!
Это показывает, что мы перечислили весь диапазон чисел (1-10), но мы выполнили какое-то другое действие, когда количество было больше 7.
Чаще всего, когда вы хотите повторно назначить номер в ходе «цикла», вы в конечном итоге используете Enum.map/2
или Enum.reduce/3
.
Ответ №2:
После просмотра Повторной попытки я не думаю, что вы сможете достичь желаемых результатов с retry_while
помощью . Однако вы можете использовать потоки задержек с помощью простого Enum.reduce_while/2
, который отслеживает количество, как показано в следующем примере.
defmodule World do
def call_service(count, limit) do
IO.inspect(count, label: "count")
cond do
limit == 7 -> %{errors: true}
count == limit -> %{errors: false}
true -> %{errors: true}
end
end
def hello(limit) do
50
|> Retry.DelayStreams.linear_backoff(1)
|> Enum.take(limit * 2)
|> Enum.reduce_while(0, fn delay, count ->
Process.sleep(delay)
case call_service(count, limit) do
%{errors: false} = success -> {:halt, {:ok, success}}
error when count == limit -> {:halt, {:error, error}}
_ -> {:cont, count 1}
end
end)
end
end
с этими результатами:
iex(9)> World.hello 3
count: 0
count: 1
count: 2
count: 3
{:ok, %{errors: false}}
iex(10)> World.hello 7
count: 0
count: 1
count: 2
count: 3
count: 4
count: 5
count: 6
count: 7
{:error, %{errors: true}}
iex(11)>