Elixir地图中相同值键名称的模式匹配快捷方式
我以这种风格进行了大量的模式匹配:Elixir地图中相同值键名称的模式匹配快捷方式,elixir,Elixir,我以这种风格进行了大量的模式匹配: def action(%{start_date: start_date, amount: amount, notify: notify %}) do # some action end 大多数情况下,我为参数选择的名称与映射中的名称相同。是否有一种快捷方式可以指定模式匹配大小写,而不必为键和值重复相同的名称 以下伪代码行中的某些内容: def action(%{start_date: %s, amount: %s, notify: %s}) do I
def action(%{start_date: start_date, amount: amount, notify: notify %}) do
# some action
end
大多数情况下,我为参数选择的名称与映射中的名称相同。是否有一种快捷方式可以指定模式匹配大小写,而不必为键和值重复相同的名称
以下伪代码行中的某些内容:
def action(%{start_date: %s, amount: %s, notify: %s}) do
IO.inspect(start_date)
# some action
end
好了,没有现成的东西,但是可以简单地为自己创建一个宏来达到这个目的:
defmodule M do
defmacro struct(params) do
{:%{}, [], Enum.map(params, fn e -> {e, {e, [], Elixir}} end)}
end
end
defmodule Test do
require M # to use macro
def action(M.struct([:a, :b]) = params),
do: IO.inspect params, label: "Params are"
end
Test.action(%{a: 42, b: :ok})
#⇒ Params are: %{a: 42, b: :ok}
Test.action(%{a: 42})
** (FunctionClauseError) no function clause matching in Test.action/1
上面的代码当然只是一个MCVE,您可能需要以某种方式对其进行增强,以更优雅地处理角落案例(并且,可能有一个更明确的可读宏,它的行为比吐出AST更智能,并考虑绑定等),但我相信这就解释了这个想法。我引入了一个sigil,
~m{…}
,以实现销毁分配
~m{foo bar} = %{foo: 1, bar: 2}
foo #=> 1
bar #=> 2
下面是我如何实现sigil的
defmodule DestructingAssignment do
defmacro __using__(_) do
quote do: import unquote(__MODULE__)
end
defmacro sigil_m({:<<>>, _line, [string]}, []) do
spec = string
|> String.split
|> Stream.map(&String.to_atom/1)
|> Enum.map(&{&1, {&1, [], nil}})
{:%{}, [], spec}
end
end
依我拙见,这保留了一些角色,但也带来了一定程度的含蓄。在您给出的单个示例中,这可能并不明显,但随着这种含蓄性的增加,代码变得难以阅读,需要更多的间接思考。保持代码清晰,并为增加字符付出代价。@smos“间接思维”的定义取决于对你来说什么是“直接的”以及你的习惯是什么。有时,当涉及到许多要显式匹配的map成员时,它会变得嘈杂,需要更多的“间接阅读”。我从来没有为此使用过任何短缺/语法糖,但我可以很容易地想到一些情况,在这些情况下,这很有意义。例如,module声明了一组宏,目的只有一个:关闭函数定义中的噪音。仅供参考,用
~m
符号完成类似的操作。@mudasobwa我完全同意你文章的部分内容。它有时有助于隐藏一些细节,使其具有声明性和更明确的含义。例如,示例whatyouhide
poset实现了在使用赋值进行匹配时非常直观的功能。人们可以猜测,到底发生了什么。在函数声明中,我必须查找它,或者通过谷歌来了解它的功能。在这一点上,我愿意接受一些代码干扰。为什么要将导入
别名为使用
?显式导入销毁分配有什么问题?这看起来有点误导,尽管它在键入时节省了3个字符:)因为否则您必须要求销毁分配
(因为该符号是一个宏,而不是一个函数),然后导入销毁分配
<代码>使用将隐式地首先要求
模块。
defmodule Foo do
use DestructingAssignment
def foo(~m{bar}) do
# Do whatever you want with bar
IO.inspect(bar)
end
end
Foo.foo(%{bar: 1, baz: 2})
1