【问题标题】:Shortcut for pattern matching on same value-key name in Elixir mapElixir 映射中相同值键名称的模式匹配快捷方式
【发布时间】:2017-11-02 03:12:48
【问题描述】:

我用这种风格做了相当多的模式匹配:

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

【问题讨论】:

  • 在我的拙见中,这节省了一些字符,但引入了一定的隐含性。在你给出的那个例子中这可能并不明显,但是随着越来越多的这种隐含性,代码变得难以阅读,需要更多的间接思考。保持代码明确并付出额外字符的代价。
  • @smoes “间接思考”的定义取决于对您而言什么是“直接”以及您的习惯是什么。有时,当涉及到许多要显式匹配的地图成员时,它会变得嘈杂,需要更多的“间接阅读”。我从来没有为此使用任何短缺/语法糖,但我可以很容易地想到一些情况,这很有意义。例如。 Stream 模块声明了一堆宏,目的只有一个:关闭函数定义中的杂音。
  • 仅供参考,github.com/whatyouhide/short_maps 使用~m sigil 完成了类似的事情。
  • @mudasobwa 我完全同意你的部分帖子。它有时有助于隐藏一些细节,使其具有声明性和更明确。示例whatyouhide postet 例如在使用赋值进行匹配时,实现了一些非常直观的东西。人们可以以某种方式猜测,发生了什么。在函数声明中,我必须查找它或谷歌以找出它的作用。在这一点上,我愿意接受一些代码噪音。

标签: elixir


【解决方案1】:

我引入了一个印记~m{...},以实现破坏赋值。

~m{foo bar} = %{foo: 1, bar: 2}
foo  #=> 1
bar  #=> 2

这是我实现印记的方法

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

用法

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

【讨论】:

  • 为什么要将importuse 别名?显式import DestructingAssignment 有什么问题?这看起来有点误导,虽然它在打字时节省了 3 个字符:)
  • 因为否则你必须require DestructingAssignment(因为印记是一个宏,而不是一个函数)然后import DestructingAssignmentuse 将首先隐式地 require 模块。
【解决方案2】:

AFAIK,没有什么是开箱即用的,但人们可以简单地为自己创建一个宏来达到这个目的:

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 更智能,并考虑绑定等),但我相信这解释了这个想法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-04
    • 1970-01-01
    • 2023-03-27
    • 2016-04-20
    • 1970-01-01
    • 2017-10-19
    • 2020-06-30
    • 1970-01-01
    相关资源
    最近更新 更多