Какой-нибудь более простой способ получить первый элемент в списке, который соответствует функции, и вернуть элемент, и список без этого элемента? - PullRequest
0 голосов
/ 05 ноября 2018

Пример: у нас есть список [1,2,3,4] и fn &(&1 >= 3) Я хочу вернуть 3 и [1,2,4]

В настоящее время я делаю это как:

index = Enum.find_index(list, func)
elem = Enum.at(list, index)
rest = List.delete_at(list, index)

Это 3 строки кода, которые кажутся общим шаблоном, который можно сделать короче. Есть ли лучший способ добиться чего-то подобного?

Ответы [ 2 ]

0 голосов
/ 05 ноября 2018

Чтобы выполнить задачу, как указано (разделить на первое вхождение), используйте кувалду, которая всегда работает: Enum.reduce_while/3.

input = [1, 2, 3, 4]

input
|> Enum.with_index()
|> Enum.reduce_while({nil, []}, fn {e, idx}, {value, rest} ->
  if e >= 3,
      do: {:halt, {e, Enum.reverse(rest) ++ tl(Enum.slice(input, idx..-1))}},
    else: {:cont, {value, [e | rest]}} end)
#⇒ {3, [1, 2, 4]}

Трюк with_index нужен здесь только для производительности. Как только элемент найден, мы хотим немедленно остановить итерации, поэтому нам нужен индекс следующего элемента для массового добавления хвоста к результату.


Другой способ - использовать Enum.split_while/2

with {h, [e | t]} <- Enum.split_while(input, fn x -> not(x >= 3) end),
  do: {e, h ++ t}
#⇒ {3, [1, 2, 4]}
0 голосов
/ 05 ноября 2018

Предполагая, что есть только один элемент, соответствующий вашей функции, вы можете использовать split_with + сопоставление с шаблоном:

iex(1)> {[item], rest} = Enum.split_with([1,2,3,4], & &1 == 3)
{[3], [1, 2, 4]}
iex(2)> item
3
iex(3)> rest
[1, 2, 4]

Конечно, это приведет к сбою, если в списке более одного совпадения. Если вы хотите извлечь только первый файл, который вы можете {[item | _], rest}, но он все равно удалит их из rest, поэтому я не уверен, что вы этого хотите.

...