Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Elixir-动态调用私有函数_Elixir - Fatal编程技术网

Elixir-动态调用私有函数

Elixir-动态调用私有函数,elixir,Elixir,我找到了Kernel.apply/3,它允许通过将方法指定为原子来动态调用模块中的公共方法,例如result=apply(\uuuu模块,:my\u方法,[arg])转换为result=my\u方法(arg) 让我困惑的是调用私有方法的方法;给定如下代码: defmodule MyModule do def do_priv(atom, args) when is_list(args) do apply(__MODULE__, atom, args) end

我找到了
Kernel.apply/3
,它允许通过将方法指定为原子来动态调用模块中的公共方法,例如
result=apply(\uuuu模块,:my\u方法,[arg])
转换为
result=my\u方法(arg)

让我困惑的是调用私有方法的方法;给定如下代码:

defmodule MyModule do
    def do_priv(atom, args) when is_list(args) do
        apply(__MODULE__, atom, args)
    end

    # (change defp => def, and this all works)
    defp something_private(arg), do: arg #or whatever
end
我希望
MyModule.do_priv(:something_private,[1])
是允许的,因为它是从模块内部对私有方法的调用。我可以理解,UndertheHood Elixir使用的是Erlang的apply/3,因此这种方法可能无法实现这一目标

我也尝试过使用
code.eval_quoted/3
方法,但它甚至不能调用硬编码的私有方法(因此没有时间手动构建AST,而不是像下面那样使用
quote do
,不过如果有人看到如何实现这一点,这是一个选项):

同样,它是从包含模块中访问私有函数,因此我希望它是允许的。可能是我不理解引用到
eval\u的
\uu环境
参数

目前唯一有效的解决方案是将
defp
更改为
def
,这对于我的个人代码来说是一个很好的解决方案;但由于我编写的代码支持其他真正关心的程序员,所以我想找到一个解决方案


我对其他方法持开放态度,但我个人对如何实现这一点感到困惑。

AFAIK动态调用私有函数在Erlang中是不可能的(因此在Elixir中也是不可能的)。如果需要进行动态调度,可以考虑使用多子句函数。一个人为的例子(当然是个糟糕的例子,但想不出更好的ATM机):

另一种选择是将函数公开,但用
@doc false
表示它们是内部函数,即不打算由客户端公开使用。您还可以考虑将这些函数移动到单独的模块,并将整个模块标记为<代码> @ MeimeDoc false >代码>内部。这两种方法偶尔在Elixir代码中使用


不过,我建议从简单开始,使用模式匹配+多子句函数。如果代码变得更复杂,那么我会考虑其他选项。

首先,您应该知道,在MyMoo模块中调用的<代码> f>(<代码> < />代码)与在同一地方调用的<代码> MyMeult.For(<)>代码不一样。看

只能调用私有函数
f()
style。编译器也会检查这些调用-如果函数不存在,则会出现编译错误。当您在同一位置使用
MyModule.f()
时,不会出现编译错误,因为这些调用仅在运行时检查(即使您是从内部调用模块),并且效果(AFAIK)与调用
MyModule.f()时相同
来自任何其他模块-在运行时查找该模块,您只能调用导出(公共)函数

因此,除了普通的
f()
,您不能以任何其他方式调用私有函数
apply(mod,fun,[])
相当于
mod.fun.(
style-模块在运行时解析,私有函数不可访问

在此示例中,您可以自己尝试所有变体:

您现在可以看到,对私有函数的调用在编译时必须始终是已知的,因此您甚至不能使用eval_-quoted magic或任何其他magic来使它们“动态”

Sasa Juric建议使用
@doc false
是正确的解决方案。

使用宏 您可以使用动态调用同一模块内的私有方法。下面是一个简单的宏,可以实现以下功能:

defmacro-local\u-apply(方法,args)什么时候执行
引述
unquote(方法)(unquote_拼接(args))
结束
结束
要在模块中调用它,您可以这样做(只需记住在调用宏之前定义它!):

def call_priv(某些参数)do
局部应用(:某个私有对象,[某个参数])
结束
解除私有的东西(arg),执行:arg

local\u apply
将在调用时使用参数扩展到所需的方法调用(但仅在编译时),这意味着您无法在运行时将宏动态扩展到函数调用。

代码已经很复杂,并且代码与您建议的一样(上文多子句
run\u op
)这是问题的两个动机之一。我现在正在做的就是把它们公诸于众,但我喜欢你关于
@doc false
的想法。另一个用例不会支持这一点,因为私有函数将由其他开发人员编写,他们可能会想遵守他们的访问保护方案。如果您打算编写一个通用代码,需要在代码中的不同地方为多个DEVs服务,那么您也可以考虑使用ILIXIR协议()或行为()。这里简要解释了这些区别:顺便说一句,调用my_module:nonexistent()是完全正确的,因为代码可以热重新加载,这一行实际上可以调用新的模块版本,该版本可以导出nonexistent/0。
defmodule MyModule do
    def do_priv_static do
        something_private(1) #this works just fine
    end

    def do_priv_dynamic do
        code = quote do
            something_private(1)
        end
        Code.eval_quoted(code, [], __ENV__)   #nope.  fails
    end

    defp something_private(arg), do: arg #or whatever
end
iex(1)> defmodule Math do
          def operation(op) do
            IO.puts "invoking #{inspect op}"
            run_op(op)
          end

          defp run_op({:add, x, y}), do: x + y
          defp run_op({:mul, x, y}), do: x * y
          defp run_op({:square, x}), do: x * x
        end

iex(2)> Math.operation({:add, 1, 2})
invoking {:add, 1, 2}
3

iex(3)> Math.operation({:mul, 3, 4})
invoking {:mul, 3, 4}
12

iex(4)> Math.operation({:square, 2})
invoking {:square, 2}
4