Каков альтернативный способ возврата в середине цикла в мире Elixir

#elixir

#elixir

Вопрос:

Я все еще новичок в Elixir. Я пытаюсь создать метод, который принимает список запросов и обрабатывает каждый запрос. Верните {:ok, «success»}, если все прошло или {:error, error_reason}, если один не удался.

На других языках я могу сделать что-то подобное. Предположим, что функция процесса возвращает либо {:ok, «success»}, либо {:error, error_reason} .

 def func(requests):
   for request in requests:
       if {:error, error_reason} <- process(request):
           return {:error, error_reason}

   return {:ok, "success"}
end
  

Каков правильный способ сделать это в мире Elixir?

Ответ №1:

Я бы использовал рекурсию:

 def func([head | tail]) do
  case process(head) do
    {:error, reason} -> {:error, reason}
    {:ok, _} -> func(tail)
  end
end

def func([]), do: {:ok, "success"}
  

Циклы по своей сути обязательны, а Elixir по сути является функциональным языком, поэтому рекурсия и функции более высокого порядка являются более естественной альтернативой использованию циклов, я думаю.

Комментарии:

1. Ах, извините, я как-то замалчивал эту часть. Тогда Enum.find это определенно не подходит. Я бы всегда выбирал функцию более высокого порядка, если она доступна, но, поскольку я не думаю, что есть одна легкодоступная, которая применима к этому сценарию, я бы реализовал ее, используя вместо этого рекурсию. Я отредактировал свой ответ, слегка изменив предыдущее рекурсивное предложение, которое, я надеюсь, лучше соответствует тому, о чем вы просите.

2. Enum.find/2 на самом деле работает. Я немного поиграл с этой функцией, как funs = (1..100) |> Enum.map(amp;(fn-> IO.inspect(amp;1) end)) и тогда Enum.find(funs, amp;(amp;1.() == 5)) , она печатает только 5 чисел, а не 100 чисел.

3. Интересно, @Aetherus, я действительно могу воспроизвести и ваш результат, но это, похоже, противоречит документации: «Наконец, обратите внимание, что функции в модуле Enum готовы: они будут проходить через перечислимое, как только они будут вызваны. Это особенно опасно при работе с бесконечными перечисляемыми. В таких случаях вам следует использовать модуль Stream, который позволяет лениво выражать вычисления, не проходя коллекции, и работать с, возможно, бесконечными коллекциями. » Поэтому я не уверен, насколько безопасно было бы полагаться на это.

4. В Stream документации также уточняется, что это определяющее различие. И, к сожалению, также нет Stream.find или эквивалентных функций.

5. В Stream данном случае лень функций не имеет значения. Stream функции выполняются таким образом, что по запросу «нисходящего потока» он извлекает только один элемент из «восходящего потока», обрабатывает его и передает его нисходящему потоку. Stream функции могут выполнять только локальные операции (для которых требуется только текущий элемент). С другой стороны, Enum функции просто используют столько элементов, сколько необходимо для достижения вывода, и возвращают весь вывод, поэтому Enum функции могут выполнять нелокальные операции, такие как reduce and group_by и find .