Могу ли я захватить переменную вне блока, например, закрытие другого языка в Elixir?

#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)>