【问题标题】:Check if function head has matching pattern?检查功能头是否有匹配的模式?
【发布时间】:2016-11-13 21:46:11
【问题描述】:

鉴于我的函数直接在头部进行模式匹配,有没有办法让我检查一个函数是否具有与给定输入匹配的模式而不调用它?有点像match?,但用于功能。我不关心when,我只匹配元组中的原子或原子。

例如

def init(:ok) do
  ...
end

check(&init/1, :ok)  # return true
check(&init/1, :other)  # return false

我正在修改 GenServer 中的状态,使用处理某些输入并忽略其他输入的函数列表。每个函数都接受一个元组,一些状态,如果函数头匹配,则返回修改后的状态,否则返回状态。我现在有一个 try/rescue 包装函数,它不是那么漂亮。输入是可变长度的元组,第一个元素是原子标识符。

【问题讨论】:

  • 你想用这个来完成什么?考虑到更多的上下文,我们可能会想出一个更直接的解决方案来解决您的问题。
  • @PatrickOscity 已更新。如果还不清楚,请随时询问。
  • @FilipHaglund 在不执行函数的情况下无法检查函数头是否匹配,但我认为可以通过多种方式使用现有工具实现相同目标。你能展示更多你的代码吗?我很难得到你想要做的事情:|
  • 考虑到我的回答被否决了,我想我也错过了你问的问题。我认为您需要某种 case 语句——这通常是我在函数中进行模式匹配的方式。但我想我错过了一些东西。
  • @OnorioCatenacci 我不想在函数中进行模式匹配,而是检查def init(:ok) 是否会在函数头的某个已知输入上引发FunctionClauseError。我想知道我的哪些函数可以处理这个输入,而无需执行它们。

标签: elixir


【解决方案1】:

严格来说,这是不可能的。 Elixir.FunctionClauseError 只是底层erlang function_clause runtime error 的包装器。

检查是即时完成的,可能会检查任何特定的子句,但没有准备检查的子句列表。一个很好的例子是一只贪婪的猫,坐在你面前;你可以试着用苹果、胡萝卜甚至回形针喂她,但除非你试过,否则不能说她会不会吃。

另一方面,人们总是可以选择查询Module.__info__ :functions。它以一个 arity 响应,因此可能会检测并拒绝所有 Elixir.UndefinedFunctionErrors。

但是子句匹配是在运行时完成的,因此无法接收允许的子句列表。考虑到子句可能是一堆繁琐的守卫 (when,) 显式参数 (:ok),它们甚至可能重叠。


问题,正如它所说:

check(&init/1, :ok)  # return true
check(&init/1, :other)  # return false

不过,有一个蛮力解决方案。我不推荐它,但它仍然可用:实现check 作为调用函数并从Elixir.FunctionClauseError 救援

另外,请不要说我是提出这个建议的人。

【讨论】:

  • 您的蛮力解决方案是我目前正在使用的解决方案,但我不同意在一般情况下这是不可能的。我可以编写一个宏来提取函数子句的第一个参数并使用match? 或针对它的案例。我只是要求一个更惯用的解决方案,更少的魔法。这里没有停机问题。
  • “我可以编写一个宏来提取函数子句的第一个参数”——除非你有复杂的守卫。这是一个非常有限且人为的示例,说明什么是子句匹配,并且此实现将在合法的 Elixir 子句上失败。我的观点是“一般来说这是不可能的,这就是标准 Elixir 功能无法实现的原因。”
  • 但是我没有这么硬的约束。
  • 我了解到您有一个非常具体的用例。语言本身支持一般用例。一般来说,子句匹配不可逆,我刚刚解释了原因。更重要的是,这种方法完全违背了 erlang/elixir 的范式:“快速失败”意味着您的代码在没有匹配项时应该失败;这是预期的行为。出于某种原因,您想提前处理这些问题——好吧,但是为什么期望语言会有所帮助呢?
  • 我不认为提取所有def 参数的宏在提取守卫时不会有任何问题——实现起来比仅提取第一个参数要复杂一些。为什么你认为它“一般来说是不可能的”?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-17
  • 1970-01-01
  • 2017-11-30
  • 2010-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多