erlang 如果为零,则跳过酏剂

mm9b1k5b  于 2022-12-08  发布在  Erlang
关注(0)|答案(3)|浏览(122)

我目前正在尝试迭代Mongo中的一个集合。为此,我使用这个正则表达式来过滤该集合。在某些情况下,正则表达式返回nil,在这种情况下,它会破坏List.first,因为列表中没有任何内容。这会导致后面的问题。
我来自Ruby,在那里我可以做一个next unless recipient,或者使用一个单独的操作符List&.first,然后从那里开始。我在Elixir中如何做这件事呢?我主要感兴趣的是如果接收者的值为零,跳过当前的迭代。

recipient =
  Regex.run(~r/(?<=recipient-).+\/*/, engagement["_id"])
  |> List.first()
  |> String.split("/")
  |> List.first()
bxfogqkk

bxfogqkk1#

If I understood you correctly, looks like you need start use case blocks, eg:

case Regex.run(~r/(?<=recipient-).+\/*/, engagement["_id"]) do
    nil -> IO.puts "Skip List.first for Regex.run"
    out -> out |> List.first |> String.split("/") |> List.first
end

OR

Regex.run(~r/(?<=recipient-).+\/*/, engagement["_id"]) |>
case  do
    nil -> IO.puts "Skip List.first for Regex.run"
    out -> out |> List.first |> String.split("/") |> List.first
end
lnvxswe2

lnvxswe22#

作为case语句的替代方法,您可以使用函数子句的头部来匹配正则表达式的结果,即nil或match:

defmodule A do
  def go() do
    Enum.each(["a/x", "c", "b/x"], fn str ->
      Regex.run(~r{(a|b)/x}, str, capture: :first)
      |> handle_recipient
    end)
  end

  defp handle_recipient(nil), do: :no_match  #does nothing
  defp handle_recipient([match]) do
    match
    |> String.split("/") 
    |> List.first()
    |> IO.inspect()

  end

end

在iex中:

~/elixir_programs$ iex "a.ex"
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> A.go
"a"
"b"
:ok

iex(2)>

听起来您可能希望使用Enum.reduce/3来过滤您的集合:

defmodule A do
  def go() do
    Enum.reduce(["a/x", "c", "b/x"], [], fn str, acc ->
      Regex.run(~r{(a|b)/*}, str, capture: :first)
      |> handle_recipient(acc)
    end)
  end

  defp handle_recipient(nil, acc), do: acc
  defp handle_recipient([match], acc) do
    result = match
             |> String.split("/")
             |> List.first
    [result|acc]
  end

end

在iex中:

~/elixir_programs$ iex "a.ex"
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> A.go
["b", "a"]

您也可以使用for编写:

defmodule A do
  def go() do
    for str <- ["a/x", "c", "b/x"], reduce: [] do 
      acc -> Regex.run(~r{(a|b)/x}, str, capture: :first)
             |> handle_recipient(acc)
    end
  end

  defp handle_recipient(nil, acc), do: acc  
  defp handle_recipient([match], acc) do
    result = match
             |> String.split("/") 
             |> List.first()
    [result|acc]
  end

end
mqkwyuun

mqkwyuun3#

TL;DR Use for/1 comprehension filtering out unwanted input (everything that is not a list, having a binary head)

recipient =
  for [match | _] when is_binary(match) <-
      Regex.run(~r/(?<=recipient-).+\/*/, engagement["_id"]),
    do: match |> String.split("/") |> List.first()

As per documentation for Regex.run/3, it returns the type

nil | [binary()] | [{integer(), integer()}]

That said, one might use List.wrap/1 to produce an empty list out of nil .

recipient =
  ~r/(?<=recipient-).+\/*/
  |> Regex.run(engagement["_id"])
  |> List.wrap()
  |> List.first()
  |> String.split("/")
  |> List.first()

Unfortunately, it would then blow up on String.split/2 down the pipeline.
That said, one might resort to Regex.scan/3 instead of Regex.run/3 that always returns a list.

recipient =
  ~r/(?<=recipient-).+\/*/
  |> Regex.scan(engagement["_id"])
  |> List.first()
  |> List.first() # to get to the first capture
  |> String.split("/")
  |> List.first()

But all this looks like an XY problem. You stated

  • I’m mainly interested in skipping the current iteration if the value of the recipient is nil .*

That is impossible out of the box, but there are many workarounds.

  • Enum.reduce/3 with a reducer having two clauses
  • Enum.map/2 followed by Enum.reject/2
  • for/1 comprehension filtering out nil s

I would vote for the latter.

recipient =
  for [match | _] when is_binary(match) <- Regex.run(~r/(?<=recipient-).+\/*/, engagement["_id"]),
  do: match |> String.split("/") |> List.first()

相关问题