【问题标题】:Use of function in Guard not allowed. Suggestions for alternate implementation wanted不允许在 Guard 中使用函数。需要替代实施的建议
【发布时间】:2011-09-21 08:37:13
【问题描述】:

我正在尝试使用fermats method 创建一个素数分解器。

此行产生错误

find_factors(A, B, FactorThis) when is_a_square(B) == true ->

对本地/导入函数 is_a_square/1 的调用在警卫中是非法的

我看到的唯一可能的替代方案是在函数中使用某种 case 语句。我正在避免这种情况,因为它可能会搞砸尾递归。我是一个 Erlang 菜鸟。 还有哪些其他方法可以实现此功能?

get_int_part_of_sqrt(N) ->
    trunc(math:sqrt(N)).

is_a_square(N) ->
    get_int_part_of_sqrt(N) * get_int_part_of_sqrt(N) == N.

calculate_new_b(A, FactorThis) ->
    NewB = trunc(abs((A * A) - FactorThis)),
    io:format("Calculate_new_b A^2 ~w- FT ~w= NB ~w ~n",[A*A,FactorThis,NewB]),

find_factors(A, B, FactorThis) when is_a_square(B) == true ->
    io:format("find_factors true ~w ~w~n", [A, B]),
    {ok, A + get_int_part_of_sqrt(B), A - get_int_part_of_sqrt(B)};

find_factors(A, B, FactorThis) ->
    io:format("find_factors false ~w ~w~n", [A, B]),
    NewA = A + 1,
    NewB = calculate_new_b(NewA, FactorThis),
    find_factors(NewA, NewB, FactorThis).

Research1

Research2

已编辑。 调用calculate_new_b时的固定参数

添加了缺少的 get_int_part_of_sqrts。

【问题讨论】:

    标签: erlang


    【解决方案1】:

    Erlang 刻意限制了哪些函数可以调用警卫。 Here's 近期讨论了这样做的理由、优点和缺点。

    解决它的唯一方法是使用case。你可以很容易地重写这段代码来使用case

    find_factors(A, B, FactorThis) ->
        case is_a_square(B) of
            true -> io:format("      find_factors true ~w ~w~n", [A, B]),
                    {ok, A + B, A - B};
    
            false-> io:format("      find_factors false ~w ~w~n", [A, B]),
                    NewA = A + 1,
                    NewB = calculate_new_b(NewA, FactorThis),
                    find_factors(NewA, NewB, FactorThis).
    

    请注意,上面的代码仍然是正确的尾递归。

    (我稍微修改了你的代码,去掉了我猜你不应该有的部分)

    【讨论】:

    • 是的。这就是我的想法。我从有问题的代码中删除了废话。这是评论残留,被错误地删除。谢谢。
    • 不幸的是,许多支持在守卫中允许用户定义函数的人都忽略了这一点,或者建议进行此类更改以使其“安全”,因为它会从根本上改变语言的某些部分。
    • 我将不得不重新阅读讨论。我来自 Haskell 背景的直觉是不同意并建议应该有方法将函数标记为无副作用(从 Haskeller 的角度来看,即使是一些已批准的函数,如 node/0self/0 ) 但这似乎不是正确的地方。
    • 我昨晚试了一下,没有得到预期的结果。我找到了另一种方法。今天我将尝试进行编辑,因为这个答案看起来确实正确。目前有一些语言方面是不可见的。
    • 如果是与您的问题相关的语言问题,请随时提问 :)
    【解决方案2】:

    这是围绕该问题进行重构的另一种方法。

    在调用者处添加所需的保护函数作为参数。这将它从一个可能有副作用的函数变成了没有副作用的真或假。然后直接模式匹配就可以了。

    main() ->
        List2 = find_factors_2 (10, 5, 105, is_a_square(5)),
        io:format("method 2 ~w~n", [List2]).
    
    find_factors_2(A, B, _FactorThis, true) ->
        Offset = get_int_part_of_sqrt(B),
        {A + Offset, A - Offset};
    
    find_factors_2(A, _B, FactorThis, false) ->
        NewA = A + 1,
        NewB = calculate_new_b(NewA, FactorThis),
        find_factors_2(NewA, NewB, FactorThis, is_a_square(NewB)).
    

    【讨论】:

    • 是的,这也可以。两者确实是等价的,所以这是审美偏好的问题。我确实发现支持这种模式匹配样式的所有语言(据我所知)都允许在函数头和主体中使用它,这很有趣。
    • 我不同意。您的解决方案更好,因为它不需要额外的参数。实际上,我正在向调用者暴露一些内部结构。无论如何,这是另一种方法,它可能有一天对某人有用。
    猜你喜欢
    • 1970-01-01
    • 2011-09-09
    • 2010-09-16
    • 2021-01-13
    • 1970-01-01
    • 1970-01-01
    • 2011-11-02
    • 2017-10-21
    • 1970-01-01
    相关资源
    最近更新 更多