Erlang:嵌套案例 [英] Erlang : nested cases

查看:152
本文介绍了Erlang:嵌套案例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Erlang很新。我试图找出一个列表索引是否超出范围(在尝试之前),所以我想做一个if子句,如

  if lists:flatlength(A)< DestinationIndex .... 

我发现那些功能结果不能使用,如果守卫,所以我用例代替。这导致一个嵌套的case语句

  case Destination< 1的
true - > 【OK,NumberOfJumps + 1};
false - >
案例列表:flatlength(A)<目的地
true - >
doSomething;
false - >
案例列表:
的成员(Destination,VisitedIndices)true - >做一点事;
false - >
doSomethingElse
end
end
end。

在可读性和代码风格方面我发现这个不好。这是你如何做这样的事情在erlang还是有一个更优雅的方式来做到这一点?



提前感谢

解决方案

在你以下作为一些神奇的福音之前,请注意,这个功能的输入方式几乎肯定是单一的。你应该设法限制案例的方式,直到这一点 - 嵌套案件的需要本身通常是一个代码的气味。有时它真的是不可避免的,但我强烈怀疑,这一点的一些方面可以在代码中更早地简化(特别是对于传递的数据结构有更多的想法,以及它们的含义)。



没有看到变量 A 来自哪里,我在这里作为参数。另外,没有看到这个函数是如何输入的,我正在组成一个函数头,因为没有其余的功能可以很好地说出来。



<所有这一切,让我们重构一下:



首先,我们想摆脱我们知道的一件事情可以进入守卫,那个是您的第一个案例,检查是否目的地< 1 。而不是使用一个案例,让我们考虑一下,我们真的要调用两个不同的常用函数的子句:

  foo(Destination, NumberOfJumps,_,_)when Destination< 1  - > 
{ok,NumerOfJumps + 1};
foo(Destination,_,VisitedIndices,A) - >
案例列表:flatlength(A)<目的地
true - >做一点事;
false - >
案例列表:
的成员(Destination,VisitedIndices)true - >做一点事;
false - >
doSomethingElse
end
end。

不是太奇怪了但是那些仍然存在的嵌套案例...令人讨厌他们。这是我怀疑在其他地方可以做的事情,以减轻在代码中早期采用的路径选择。但是假设你无法控制那些东西。在这种情况下,分配布尔值和如果可以是可读性增强器:

  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 - >有趣的doSomething / 0;
AMember - >有趣的doSomething / 0;
不是AMember - > fun doSomethingElse / 0
end,
NextOp()。

这里我刚刚切到追逐,并确保我们只执行每个潜在的昂贵的操作一次通过分配结果是一个变量 - 但是这让我非常不舒服,因为我不应该在这种情况下开始。



在任何情况下,这样的东西应该和以前的代码一样测试,而且在这个过程中可能会更加可读。但您应该寻找其他地方来简化。特别是,这个 VisitedIndices 业务感到腥味(为什么我们不知道目标是否是成员?),在我们达到这个功能之后需要变平的变量 A 是奇怪的(为什么还没有被平坦化,为什么还有这么多?)和 NumberOfJumps 感觉像一个累加器,但它的存在是神秘的。



什么让我对这些变量感到奇怪,你可能会问一直使用的唯一一个是目标 - 其他仅在 foo / 4 的一个子句中使用或另一个,但不是两者。这使我认为这应该是不同的执行路径,而不是在超级决策类型函数中全部清除。



编辑



有关手头问题的更全面的描述(参考下面的评论中的讨论),请考虑如何实现:

  -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;索引>长度 - >
计数
jump_calc(路径,长度,索引,计数,访问) - >
NewIndex = Index + lists:nth(Index,Path),
NewVisited = [Index |访问],
NewCount = Count + 1,
案例列表:
的成员(NewIndex,NewVisited)true - > NewCount;
false - > jump_calc(Path,Length,NewIndex,NewCount,NewVisited)
end。

始终尝试前端加载尽可能多的处理,而不是反复执行相同的计算。考虑我们如何容易地阻止守卫后面的每个迭代,以及由于这个原因,我们甚至不必编写有条件的条件。功能匹配是一个强大的工具 - 一旦你得到它,你将真正开始享受Erlang。


I'm very new to Erlang. I tried to find out if a list index is out of bounds (before trying it) so i wanted to do an if clause with something like

if lists:flatlength(A) < DestinationIndex ....

I discovered that those function results cannot be used in if guards so i used case instead. This results in a nested case statement

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.

I found this bad in terms of readability and code style. Is this how you do things like that in erlang or is there a more elegant way to do this?

Thanks in advance

解决方案

Before you take the following as some magical gospel, please note that the way this function is entered is almost certainly unidiomatic. You should seek to limit cases way before you get to this point -- the need for nested cases is itself usually a code smell. Sometimes it is genuinely unavoidable, but I strongly suspect that some aspects of this can be simplified way earlier in the code (especially with some more thought given to the data structures that are being passed around, and what they mean).

Without seeing where the variable A is coming from, I'm making one up as a parameter here. Also, without seeing how this function is entered I'm making up a function head, because without the rest of the function to go by its pretty hard to say anything for sure.

With all that said, let's refactor this a bit:

First up, we want to get rid of the one thing we know can go into a guard, and that is your first case that checks whether Destination < 1. Instead of using a case, let's consider that we really want to call two different clauses of a common function:

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.

Not too weird. But those nested cases that remain... something is annoying about them. This is where I suspect something can be done elsewhere to alleviate the choice of paths being taken here much earlier in the code. But let's pretend that you have no control over that stuff. In this situation assignment of booleans and an if can be a readability enhancer:

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().

Here I have just cut to the chase and made sure we only execute each potentially expensive operation once by assigning the result to a variable -- but this makes me very uncomfortable because I shouldn't be in this situation to begin with.

In any case, something like this should test the same as the previous code, and in the interim may be more readable. But you should be looking for other places to simplify. In particular, this VisitedIndices business feels fishy (why don't we already know if Destination is a member?), the variable A needing to be flattened after we've arrived in this function is odd (why is it not already flattened? why is there so much of it?), and NumberOfJumps feels something like an accumulator, but its presence is mysterious.

What makes me feel weird about these variables, you might ask? The only one that is consistently used is Destination -- the others are only used either in one clause of foo/4 or the other, but not both. That makes me think this should be different paths of execution somewhere further up the chain of execution, instead of all winding up down here in a super-decision-o-matic type function.

EDIT

With a fuller description of the problem in hand (reference the discussion in comments below), consider how this works out:

-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.

Always try to front-load as much processing as possible instead of performing the same calculation over and over. Consider how readily we can barrier each iteration behind guards, and how much conditional stuff we don't even have to write because of this. Function matching is a powerful tool -- once you get the hang of it you will really start to enjoy Erlang.

这篇关于Erlang:嵌套案例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆