【问题标题】:Erlang generate anonymous function of an arbitary arity?Erlang生成任意数量的匿名函数?
【发布时间】:2021-11-13 14:15:59
【问题描述】:

是否可以编写一个返回指定数量的匿名函数的函数?我希望能够生成一个可以作为第三个参数传递给meck:expect/3 的函数,这样我就可以动态模拟任何数量的现有函数?

我已经做了很多搜索,似乎解决这个问题的唯一方法是通过硬编码这样的事情:

gen_fun(1, Function) ->
    fun(A) -> Function([A]) end;
gen_fun(2, Function) ->
    fun(A, B) -> Function([A, B]) end;
...

【问题讨论】:

    标签: erlang arity


    【解决方案1】:

    它并不漂亮,但您可以使用与 shell 相同的技巧并构建您的函数从头开始

    -module(funny).
    
    -export([gen_fun/1, gen_fun/2]).
    
    -spec gen_fun(function()) -> function().
    gen_fun(Function) ->
        {arity, Arity} = erlang:fun_info(Function, arity),
        gen_fun(Arity, Function).
    
    -spec gen_fun(non_neg_integer(), function()) -> function().
    gen_fun(Arity, Function) ->
        Params = [{var, 1, list_to_atom([$X| integer_to_list(I)])} || I <- lists:seq(1, Arity)],
        Anno = erl_anno:new(1),
        Expr =
            {'fun',
             Anno,
             {clauses, [{clause, Anno, Params, [], [{call, Anno, {var, Anno, 'Function'}, Params}]}]}},
        {value, Fun, _Vars} = erl_eval:expr(Expr, [{'Function', Function}]),
        Fun.
    

    然后,在 shell 中……

    1> F = funny:gen_fun(fun io:format/2).
    #Fun<erl_eval.43.40011524>
    2> F("~ts~n", ["?"]).
    ?
    ok
    3> F1 = funny:gen_fun(fun io:format/1).
    #Fun<erl_eval.44.40011524>
    4> F1("?~n").
    ?
    ok
    5>
    

    【讨论】:

    • 感谢您的回答@BrujoBenavides!该解决方案比我的解决方案更漂亮。我在使用此解决方案时遇到的一件事是我很难理解的透析器错误:src/funny.erl Line 6: Function gen_fun/1 has no local return Line 11: Function gen_fun/2 has no local return Line 17: The call erl_eval:expr(Expr::{'fun',1,{'clauses',[{'clause',1,[{'var',1,atom()}],[],[{'call',1,{'var',1,'Function'},[]},...]},...]}}, [{'Function',_},...]) does not have a term of type {'nil',erl_anno:anno()} | {atom(),erl_anno:anno(),_} | {'bc' | 'call' | 'case' | 'cons' | 'lc' ...
    • 知道这可能是什么原因吗? Erlang 文档似乎并没有真正定义第一个参数的类型应该是什么 - erlang.org/doc/man/erl_parse.html#type-abstract_expr
    • 看起来我们正在构建的东西(即Expr)没有合适的格式来成为erl_eval:expression(),而erl_parse:abstract_expr()又只是一个erl_parse:abstract_expr()
    • 我使用erl_anno:new/1 修复了它的行号。
    • 解决了,谢谢!
    猜你喜欢
    • 2018-10-28
    • 1970-01-01
    • 2012-11-22
    • 2017-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-13
    • 1970-01-01
    相关资源
    最近更新 更多