Erlang:嵌套案例

时间:2015-02-23 12:05:38

标签: if-statement erlang case

我对Erlang很新。我试图找出列表索引是否超出范围(在尝试之前)所以我想用

之类的东西做一个if子句
if lists:flatlength(A) < DestinationIndex ....

我发现那些功能结果不能用于守卫,所以我用的是大写。这导致嵌套的case语句

case Destination < 1 of
    true -> {ok,NumberOfJumps+1};
    false ->
    case lists:flatlength(A) < Destination of
        true -> 
            doSomething;
        false ->
            case lists:member(Destination,VisitedIndices) of
                true -> doSomething;
                false -> 
                    doSomethingElse
            end
    end
end.

我发现在可读性和代码风格方面这很糟糕。这是你在erlang中做这样的事情,还是有更优雅的方法来做到这一点?

提前致谢

1 个答案:

答案 0 :(得分:9)

在你将以下内容作为一些神奇的福音书之前,请注意输入此功能的方式几乎肯定是单一的。你应该在达到这一点之前设法限制案例 - 对嵌套案例的需求本身通常是代码味道。有时它确实是不可避免的,但我强烈怀疑在代码中可以更早地简化这方面的某些方面(特别是考虑到传递的数据结构以及它们的意思)。

如果没有看到变量A的来源,我就在这里作为一个参数。另外,在没有看到如何输入这个功能的情况下,我组成了一个功能头,因为没有剩余的功能可以很难说出来。

尽管如此,让我们重构一下:

首先,我们希望摆脱我们知道可以进入警卫的一件事,那就是你的第一个case来检查是否Destination < 1。不要使用案例,让我们考虑一下我们真的想要调用一个共同函数的两个不同的子句:

foo(Destination, NumberOfJumps, _, _) when Destination < 1 ->
    {ok, NumerOfJumps + 1};
foo(Destination, _, VisitedIndices, A) ->
    case lists:flatlength(A) < Destination of
        true -> doSomething;
        false ->
            case lists:member(Destination,VisitedIndices) of
               true -> doSomething;
               false -> 
                   doSomethingElse
            end
    end.

不太奇怪。但那些仍然存在的嵌套案例......对他们来说很烦人。这是我怀疑可以在其他地方做些什么来减轻代码中更早出现的路径选择的地方。但是,让我们假装你无法控制那些东西。在这种情况下,布尔值和if的分配可以是可读性增强器:

foo(Destination, NumberOfJumps, _, _) when Destination < 1 ->
    {ok, NumberOfJumps + 1};
foo(Destination, _, VisitedIndices, A) ->
    ALength = lists:flatlength(A) < Destination,
    AMember = lists:member(Destionation, VisitedIncides),
    NextOp = if
        ALength     -> fun doSomething/0;
        AMember     -> fun doSomething/0;
        not AMember -> fun doSomethingElse/0
    end,
    NextOp().

在这里,我只是切入追逐并确保我们只通过将结果分配给变量来执行每次可能昂贵的操作 - 但这使我非常不舒服因为我不应该这样做。在这种情况下开始。

在任何情况下,像这样的东西应该测试与前面的代码相同,并且在过渡期间可能更具可读性。但你应该寻找其他地方来简化。特别是,这个VisitedIndices业务感觉很可疑(为什么我们不知道Destination是否是会员?),变量A需要在我们之后变平。到达这个函数是奇怪的(为什么它还没有被压扁?为什么它有这么多呢?),NumberOfJumps感觉就像一个累加器,但它的存在是神秘的。

你可能会问,是什么让我对这些变量感到奇怪?唯一一直使用的是Destination - 其他只用于foo/4或另一个的一个子句,但不能同时用于两者。这让我觉得这应该是在执行链的某个地方进行的不同执行路径,而不是在超级决策 - o-matic类型的函数中进行清理。

修改

对手头的问题进行更全面的描述(参考下面评论中的讨论),考虑一下如何解决这个问题:

-module(jump_calc).
-export([start/1]).

start(A) ->
    Value = jump_calc(A, length(A), 1, 0, []),
    io:format("Jumps: ~p~n", [Value]).

jump_calc(_, Length, Index, Count, _) when Index < 1; Index > Length ->
    Count;
jump_calc(Path, Length, Index, Count, Visited) ->
    NewIndex = Index + lists:nth(Index, Path),
    NewVisited = [Index | Visited],
    NewCount = Count + 1,
    case lists:member(NewIndex, NewVisited) of
        true  -> NewCount;
        false -> jump_calc(Path, Length, NewIndex, NewCount, NewVisited)
    end.

始终尝试尽可能多地预加载处理,而不是反复执行相同的计算。考虑一下我们如何能够阻止守卫背后的每次迭代,以及我们甚至不得不写出多少有条件的东西。功能匹配是一个强大的工具 - 一旦掌握了它,你就会真正开始享受Erlang。