【问题标题】:Howto dynamically generate function clauses如何动态生成函数子句
【发布时间】:2017-05-27 17:26:22
【问题描述】:

我需要根据用户的配置动态生成函数子句。为了清楚起见,假设我有一个原子列表:

@atoms ~w|foo bar baz|a

来自,比如说,config.exs。我需要的是生成这个函数(MCVE 过于简单,但它给人的印象是我真正需要什么):

@checker fn
  {:foo, _} -> false
  {:bar, _} -> false
  {:baz, _} -> false
  _ -> true
end

我目前正在做的是:

@clauses Enum.map(@atoms, fn tag ->
  {:->, [], [[{:{}, [], [tag, {:_, [], Elixir}]}], false]}
end) ++ [{:->, [], [[{:_, [], Elixir}], true]}]

defmacrop checker, do: {:fn, [], @clauses}

它工作得很好,但我希望我把事情复杂化了,错过了一些简单的事情。所以,我的问题是:

有没有一种简单的方法可以在编译时生成函数子句?

【问题讨论】:

标签: macros elixir metaprogramming


【解决方案1】:

我使用quote 使其更具可读性(见下文):

defmodule A do
  @atoms ~w|foo bar baz|a
  @clauses Enum.flat_map(@atoms, fn tag ->
    quote do: ({unquote(tag), _} -> false)
  end) ++ quote(do: (_ -> true))

  defmacro checker, do: {:fn, [], @clauses}
end

defmodule B do
  require A
  f = A.checker
  IO.inspect f.({:foo, :ok})
  IO.inspect f.({:bar, :ok})
  IO.inspect f.({:baz, :ok})
  IO.inspect f.({:quux, :ok})
end

输出:

false
false
false
true

我预计 quote(do: a -> b) 可以工作,但现在这是一个解析错误,所以我们必须做 quote(do: (a -> b)) 它将我们想要的引用片段包装在一个列表中。

我还希望 unquotefn 内部工作时,它在 quote 内部,但事实并非如此。

iex(1)> quote do
...(1)>   fn
...(1)>     unquote()
...(1)>     _ -> true
...(1)>   end
...(1)> end
** (SyntaxError) iex:2: expected clauses to be defined with -> inside: 'fn'

我认为这两个要么是错误,要么是缺少功能。

【讨论】:

  • quote(do: (a -> b)) wow :) 我试过 quote(do: a -> b) 并放弃了。我还尝试在引用的代码中取消引用,似乎优先级错误(语法检查 before unquote)。是的,这更容易阅读,但我仍然想等待更神奇的答案。已投票,将在 2 天 + 赏金 + 1 周后接受。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-22
  • 1970-01-01
  • 1970-01-01
  • 2019-08-20
  • 2011-03-27
  • 2020-04-21
相关资源
最近更新 更多