解构 Elixir 地图时对缺失字段的惯用处理?
Idiomatic handling of missing fields when deconstructing an Elixir map?
在地图解构中出现模式匹配错误的情况下,在 Elixir 中获取“默认”值或捕获错误的最佳方法是什么?
iex(1)> %{"a" => a} = %{"a" => 1, "b" => 2}
%{"a" => 1, "b" => 2}
iex(2)> a
1
iex(3)> %{"m" => m} = %{"a" => 1, "b" => 2}
** (MatchError) no match of right hand side value: %{"a" => 1, "b" => 2}
在深层嵌套的情况下也必须干净地工作:
iex(4)> %{"b" => %{"c" => %{"e" => myvar}}} = %{"a" => 1, "b" => %{"c" => %{"d" => 4, "e" => 5}}}
%{"a" => 1, "b" => %{"c" => %{"d" => 4, "e" => 5}}}
iex(5)> myvar
5
iex(6)> %{"b" => %{"c" => %{"e" => myvar}}} = %{"a" => 1, "f" => 6}
** (MatchError) no match of right hand side value: %{"a" => 1, "f" => 6}
所以在上述情况下,我喜欢 a
和 myvar
回退到默认值,或者以某种干净的方式分支到处理程序函数。如果可能的话,我更喜欢不涉及错误处理程序的解决方案。
我会让其他有更多经验的人来权衡什么更符合习惯,但我通常会通过模块属性提供可选的默认值,然后允许通过合并函数进行覆盖。像这样(使用 Map.merge/2 or Keyword.merge/2 作为关键字列表):
defmodule Something do
@defaults %{"a" => "alpha", "b" => "beta"}
def foo(input) do
input_or_defaults = Map.merge(@defaults, input)
# ...
end
end
如果你的问题真的是深入研究错误捕获,那么这里有一个“隐式救援”的例子,它可以处理没有匹配的情况:
def risky_stuff(input) do
do_match(input)
rescue e in UndefinedFunctionError ->
{:error, "Unable to match"}
end
defp do_match(%{"a" => a}), do: "something with a"
defp do_match(%{"b" => b}), do: "something with b"
查看 "implicit try" 以获得对此的一些解释。
不过,我认为您会通过合并找到所需的功能。
显然不能用 match 来完成,因为实际上不可能用 match 语法来适应默认值。
如果您知道地图没有 nil
值,您可能会像 get_in/2
那样做,然后在结果为 nil
.
时使用默认值
另外,自己做一个函数也没那么复杂
safe_get_in = fn keys, input, default ->
Enum.reduce_while(keys, input, fn key, acc ->
case acc do
%{^key => level_down} -> {:cont, level_down}
_ -> {:halt, default}
end
end)
end
safe_get_in.(~w|a b c d|, %{"a" => 1, "f" => 6}, 42)
#⇒ 42
safe_get_in.(~w|b c d|, %{
"a" => 1, "b" => %{"c" => %{"d" => 4, "e" => 5}}}, 42)
#⇒ 4
在不知道它是否存在的情况下获取深层嵌套值的情况并不常见,这就是标准库中没有针对这种情况的实现的原因。
从删除的评论到Aleksei的回答:
yes I can write a function, but I'm looking for an idiomatic answer.
在 Elixir 中惯用的做法是如果匹配失败则失败,或者确保匹配总是成功。
根据上下文,有多种方法可以提供始终匹配的“默认”子句。以下是使用 case
、with
和多个函数子句的 3 个示例。
案例:
case value do
%{"a" => a} -> a
_ -> "default value"
end
有:
with %{"a" => a} <- value do
a
else
_ -> "default value"
end
函数子句:
def default_function_clause(%{"a" => a}), do: a
def default_function_clause(_), do: "default value"
在所有情况下,提供带有 "a"
键的映射将 return 值,否则 "default value"
.
在地图解构中出现模式匹配错误的情况下,在 Elixir 中获取“默认”值或捕获错误的最佳方法是什么?
iex(1)> %{"a" => a} = %{"a" => 1, "b" => 2}
%{"a" => 1, "b" => 2}
iex(2)> a
1
iex(3)> %{"m" => m} = %{"a" => 1, "b" => 2}
** (MatchError) no match of right hand side value: %{"a" => 1, "b" => 2}
在深层嵌套的情况下也必须干净地工作:
iex(4)> %{"b" => %{"c" => %{"e" => myvar}}} = %{"a" => 1, "b" => %{"c" => %{"d" => 4, "e" => 5}}}
%{"a" => 1, "b" => %{"c" => %{"d" => 4, "e" => 5}}}
iex(5)> myvar
5
iex(6)> %{"b" => %{"c" => %{"e" => myvar}}} = %{"a" => 1, "f" => 6}
** (MatchError) no match of right hand side value: %{"a" => 1, "f" => 6}
所以在上述情况下,我喜欢 a
和 myvar
回退到默认值,或者以某种干净的方式分支到处理程序函数。如果可能的话,我更喜欢不涉及错误处理程序的解决方案。
我会让其他有更多经验的人来权衡什么更符合习惯,但我通常会通过模块属性提供可选的默认值,然后允许通过合并函数进行覆盖。像这样(使用 Map.merge/2 or Keyword.merge/2 作为关键字列表):
defmodule Something do
@defaults %{"a" => "alpha", "b" => "beta"}
def foo(input) do
input_or_defaults = Map.merge(@defaults, input)
# ...
end
end
如果你的问题真的是深入研究错误捕获,那么这里有一个“隐式救援”的例子,它可以处理没有匹配的情况:
def risky_stuff(input) do
do_match(input)
rescue e in UndefinedFunctionError ->
{:error, "Unable to match"}
end
defp do_match(%{"a" => a}), do: "something with a"
defp do_match(%{"b" => b}), do: "something with b"
查看 "implicit try" 以获得对此的一些解释。
不过,我认为您会通过合并找到所需的功能。
显然不能用 match 来完成,因为实际上不可能用 match 语法来适应默认值。
如果您知道地图没有 nil
值,您可能会像 get_in/2
那样做,然后在结果为 nil
.
另外,自己做一个函数也没那么复杂
safe_get_in = fn keys, input, default ->
Enum.reduce_while(keys, input, fn key, acc ->
case acc do
%{^key => level_down} -> {:cont, level_down}
_ -> {:halt, default}
end
end)
end
safe_get_in.(~w|a b c d|, %{"a" => 1, "f" => 6}, 42)
#⇒ 42
safe_get_in.(~w|b c d|, %{
"a" => 1, "b" => %{"c" => %{"d" => 4, "e" => 5}}}, 42)
#⇒ 4
在不知道它是否存在的情况下获取深层嵌套值的情况并不常见,这就是标准库中没有针对这种情况的实现的原因。
从删除的评论到Aleksei的回答:
yes I can write a function, but I'm looking for an idiomatic answer.
在 Elixir 中惯用的做法是如果匹配失败则失败,或者确保匹配总是成功。
根据上下文,有多种方法可以提供始终匹配的“默认”子句。以下是使用 case
、with
和多个函数子句的 3 个示例。
案例:
case value do
%{"a" => a} -> a
_ -> "default value"
end
有:
with %{"a" => a} <- value do
a
else
_ -> "default value"
end
函数子句:
def default_function_clause(%{"a" => a}), do: a
def default_function_clause(_), do: "default value"
在所有情况下,提供带有 "a"
键的映射将 return 值,否则 "default value"
.