打破一个理解
Break a comprehension
有没有办法打断理解?
我正在考虑 Python 的 for
-break
-else
。
例如,假设我正在遍历一个整数列表,当我从中生成一个新列表时,我想检查它是否不包含 0
.
我可以这样做吗:
for x <- [1, 2, 0, 3] do
case x do
0 -> break
_ -> x
end
end
if broke do
:error
else
:ok
end
我想我想的太命令了,正确的函数式方法应该是这样的,带有递归和累加器:
def traverse_and_check(list, acc \ []) do
case list do
[] -> {:ok, reverse(acc)}
[0 | _tail] -> :error
[x | tail] -> traverse_and_check(tail, [x | acc])
end
end
Elixir 有这样的控制流程吗?
查看 getting-started 手册的 comprehensions 页。
Comprehensions 有可选的过滤器,您可以使用它们代替标准 filter/reduce 函数。尝试这样的事情:
for x <- [1, 2, 0, 3], x != 0, into: [] do: x
文档中有一些更好的示例,但这应该适用于您在理解中从列表中删除零的用例。
您可以使用 Enum.reduce_while/3
,但我认为该解决方案比递归更糟糕,因为它需要 case
后缀来减少非中断情况的列表。下面的代码片段包括 reduce_while
实现和基于您的代码的改进递归解决方案:
defmodule A do
def f(list) do
Enum.reduce_while(list, [], fn x, acc ->
if x == 0, do: {:halt, :break}, else: {:cont, [x | acc]}
end)
|> case do
:break -> :break
list -> Enum.reverse(list)
end
end
def g(list, acc \ [])
def g([], acc), do: Enum.reverse(acc)
def g([0 | _], _), do: :break
def g([x | xs], acc), do: g(xs, [x | acc])
end
IO.inspect(A.f([1, 2, 3]))
IO.inspect(A.f([1, 2, 0, 3]))
IO.inspect(A.g([1, 2, 3]))
IO.inspect(A.g([1, 2, 0, 3]))
输出:
[1, 2, 3]
:break
[1, 2, 3]
:break
如果您对返回列表的收集部分不感兴趣,Elixir 提供 throw
/catch
正是为此目的:
In Elixir, a value can be thrown and later be caught. throw
and catch
are reserved for situations where it is not possible to retrieve a value unless by using throw
and catch
.
Those situations are quite uncommon in practice except when interfacing with libraries that do not provide a proper API.
我认为这种情况是 “不提供适当 API 的库 [here—Elixir]” 的完美示例 :)
所以给你:
try do
for x <- [1, 2, 0, 3],
do: if x == 0, do: throw(:break), else: x
catch
:break -> :broken
end
#⇒ :broken
请注意,throw
/catch
旨在控制流量,与 raise
/rescue
不同。
有没有办法打断理解?
我正在考虑 Python 的 for
-break
-else
。
例如,假设我正在遍历一个整数列表,当我从中生成一个新列表时,我想检查它是否不包含 0
.
我可以这样做吗:
for x <- [1, 2, 0, 3] do
case x do
0 -> break
_ -> x
end
end
if broke do
:error
else
:ok
end
我想我想的太命令了,正确的函数式方法应该是这样的,带有递归和累加器:
def traverse_and_check(list, acc \ []) do
case list do
[] -> {:ok, reverse(acc)}
[0 | _tail] -> :error
[x | tail] -> traverse_and_check(tail, [x | acc])
end
end
Elixir 有这样的控制流程吗?
查看 getting-started 手册的 comprehensions 页。
Comprehensions 有可选的过滤器,您可以使用它们代替标准 filter/reduce 函数。尝试这样的事情:
for x <- [1, 2, 0, 3], x != 0, into: [] do: x
文档中有一些更好的示例,但这应该适用于您在理解中从列表中删除零的用例。
您可以使用 Enum.reduce_while/3
,但我认为该解决方案比递归更糟糕,因为它需要 case
后缀来减少非中断情况的列表。下面的代码片段包括 reduce_while
实现和基于您的代码的改进递归解决方案:
defmodule A do
def f(list) do
Enum.reduce_while(list, [], fn x, acc ->
if x == 0, do: {:halt, :break}, else: {:cont, [x | acc]}
end)
|> case do
:break -> :break
list -> Enum.reverse(list)
end
end
def g(list, acc \ [])
def g([], acc), do: Enum.reverse(acc)
def g([0 | _], _), do: :break
def g([x | xs], acc), do: g(xs, [x | acc])
end
IO.inspect(A.f([1, 2, 3]))
IO.inspect(A.f([1, 2, 0, 3]))
IO.inspect(A.g([1, 2, 3]))
IO.inspect(A.g([1, 2, 0, 3]))
输出:
[1, 2, 3]
:break
[1, 2, 3]
:break
如果您对返回列表的收集部分不感兴趣,Elixir 提供 throw
/catch
正是为此目的:
In Elixir, a value can be thrown and later be caught.
throw
andcatch
are reserved for situations where it is not possible to retrieve a value unless by usingthrow
andcatch
.Those situations are quite uncommon in practice except when interfacing with libraries that do not provide a proper API.
我认为这种情况是 “不提供适当 API 的库 [here—Elixir]” 的完美示例 :)
所以给你:
try do
for x <- [1, 2, 0, 3],
do: if x == 0, do: throw(:break), else: x
catch
:break -> :broken
end
#⇒ :broken
请注意,throw
/catch
旨在控制流量,与 raise
/rescue
不同。