【问题标题】:In Erlang, how do you invoke a function dynamically?在 Erlang 中,如何动态调用函数?
【发布时间】:2010-11-21 13:49:16
【问题描述】:

我想用要调用的函数的名称来调用 xyz。

-module(sample).
-export([xyz/1]).

xyz(Name) -> Name().

p() -> "you called p".
g() -> "you called g".

但我收到以下错误:

1> c(sample.erl).
./sample.erl:6: Warning: function p/0 is unused
./sample.erl:7: Warning: function g/0 is unused
{ok,sample}
2> sample:xyz('p').
** exception error: bad function p
     in function  sample:xyz/1
3>

【问题讨论】:

  • 我的 Erlang 知识接近于零,但我想你也必须导出 p(如果你想使用它,可能还有 g)。

标签: erlang


【解决方案1】:

必须导出 p 和 g 是正确的。然后您可以使用apply/3 调用它。

erlang:apply(sample, p, []).

只有 fun-values 可用于 Fun(...) 语法。您正在传递一个原子值。正如错误消息所说,原子是一个“坏功能”。你可以做类似的事情

xyz(p) -> fun p/0;
xyz(g) -> fun g/0.

然后继续打电话

Fun = xyz(p),
Fun()

【讨论】:

  • 谢谢。我现在有了这个:-模块(示例)。 -导出([xyz/1,p/0,g/0])。 xyz(名称)-> 应用(样本,名称,[])。 p() -> “你叫 p”。 g() -> “你叫 g”。我能够做到:26> c(sample.erl)。 {ok,sample} 27> 样本:xyz('p')。 “你叫 p” 28> 样本:xyz(p)。 “你叫 p”29> 样本:xyz('g')。 “你叫 g” 30> 样本:xyz(g)。 “你叫 g” 但是没有办法不导出这些函数吗?我不希望它对模块用户可见。似乎也无法使其与 apply/2 一起使用。当然,我是 erlang 新手
  • 你要么用模式匹配硬编码你的调用映射,要么导出你的函数。
  • “泄露”未导出函数的唯一方法是返回一个引用它的有趣值。就像我的显式 xyz/1 函数返回一个有趣的值。
【解决方案2】:

最简单的方法是尝试将 p 和 g 与 xyz 一起导出。

-export([xyz/1, p/0,g/0]).

导出函数p和g后可以如下调用:

1> sample:xyz(fun sample:p/0).
"you called p"
2> sample:xyz(fun sample:g/0).
"you called g"

【讨论】:

    【解决方案3】:

    另一种看待它的方式是(取决于您要解决的问题)对函数的动态调用不一定是正确的方法。鉴于进程和消息传递是您在 Erlang 中组织代码的方式,因为它是一种“面向并发的语言”,也许您可​​以只使用带有选择性接收的消息传递而不是模仿顺序语言的习语?发送您想要的消息并根据该消息获得自定义回复。毕竟,这是关于每个函数的结果,而不是函数本身。 (另外还有消息传递的灵活性和可扩展性等)

    虽然与从库模块中调用相比,进程并不是完全免费的,但 Erlang 级别的进程非常便宜(特别是如果消息通信在同一个节点内)。它们不是操作系统级别的进程。开销将与较重的脚本语言中的动态函数调用和对象实例化相当(或更好)。

    【讨论】:

      【解决方案4】:
      -module(sample).
      -export([xyz/1, p/0, g/0]).
      
      xyz(Name) -> ?MODULE:Name().
      
      p() -> "you called p".
      g() -> "you called g".
      
      
      1> sample:xyz(p).
      "you called p"
      

      【讨论】:

      • 这很酷。我在哪里可以阅读“?模块”?现在,如果我们能取消导出 p 和 q 就好了。
      • 这里是预定义的宏:erlang.org/doc/reference_manual/macros.html#7.3。不幸的是,Erlang 只有“公共”和“私有”可见性,但没有保护和包保护。所以你要么导出它,要么“硬编码”调用。
      • 但是为什么我不需要在静态绑定调用 xyz(Name)-> p() 的情况下导出。并且需要在动态绑定时导出?考虑到“私人”可见性,我是在私人范围内,不是吗?
      • 如果您按照 Gordon 或 Christian 的建议进行操作,则无需导出它们。作为权衡,如果您稍后添加 s() 函数,则还需要修改 xyz() 的代码。如果您使用动态解决方案,则需要导出函数。在您的代码文档中,您可以清楚地声明 p 和 g 是内部导出函数,不应调用。此外,在函数式语言中,仅仅为了好玩而调用函数的人不会给您带来大问题。
      【解决方案5】:

      模式匹配是使用的成语:

      -module(sample).
      -export([xyz/1]).
      
      xyz(p) -> p();
      xyz(q) -> g().
      
      p() -> "you called p".
      g() -> "you called g".
      

      如果您想要动态,可以使用gen_event 服务器。

      本质上这是一个服务器,它拥有一个由键/函数对组成的状态,如下所示:

      [{p, #func1},
       {g, #func2},
       {..., ...},
       ...]
      

      然后,您基本上可以将事件绑定到函数。 (不用说,还有更多。

      【讨论】:

      • 虽然这在技术上是一种方法,但我认为这个问题更适合 erlang 语言中动态调用函数的机制。 apply 函数就是他正在寻找的答案。
      猜你喜欢
      • 1970-01-01
      • 2016-05-18
      • 1970-01-01
      • 1970-01-01
      • 2012-02-13
      • 2019-10-07
      • 2010-10-08
      • 1970-01-01
      • 2014-02-17
      相关资源
      最近更新 更多