2014-12-01 84 views
2

我刚开始学习Erlang和,因为我发现没有for循环我试着重新创建一个使用递归:二郎递归循环结束

display(Rooms, In) -> 
    Room = array:get(In, Rooms) 
    io:format("~w", [Room]), 
    if 
     In < 59 -> display(Rooms, In + 1); 
     true -> true 
    end. 

有了这个代码,我需要显示的内容(或真或假)在房间内的每个阵列,直到达到59。然而,这会产生一个奇怪的代码,显示所有房间内容大约60次(?)。当我放下if语句并且只放入递归代码时,它正在工作,除了一个异常错误:错误的参数。

所以基本上我的问题是我怎么把一个适当的结束了我的“for循环”。

在此先感谢!

+0

你如何称呼'陈列'?你怎么定义'房间'阵列? – raina77ow 2014-12-01 18:43:23

+0

喜欢这个:'Rooms = array:new([{default,false},{size,?SIZE}])'然后'display(Rooms,0)' – 2014-12-01 18:47:24

+1

在我看来你的代码和它应该一样,不明白你想达到什么。你可以使用[array:foldl/3](http://www.erlang.org/doc/man/array.html#foldl-3)迭代一个数组。 – 2014-12-01 19:08:13

回答

7

这是递归循环定义一个通用的误解。你试图检查的是所谓的“基本条件”或“基本情况”。这是最容易处理的匹配:

display(0, _) -> 
    ok; 
display(In, Rooms) -> 
    Room = array:get(In, Rooms) 
    io:format("~w~n", [Room]), 
    display(In - 1, Rooms). 

然而,这是相当单一的。而不是使用手工递归函数,像fold或map这样的东西更常见。

尽管如此,大多数人可能会选择将房间表示为一个集合或列表,并使用列表操作对其进行迭代。当手书的“基本情况”将是一个空列表,而不是0:

display([]) -> 
    ok; 
display([Room | Rooms]) -> 
    io:format("~w~n", [Room]), 
    display(Rooms). 

这将是可以避免的青睐,再次,列表操作像foreach

display(Rooms) -> 
    lists:foreach(fun(Room) -> io:format("~w~n", [Room]) end, Rooms). 

一些人真的不喜欢这样朗读朗达数据。 (在这种情况下,我觉得可读,but the larger they get the more likely the are to become genuinely distracting)完全相同的功能的另一种表示:

display(Rooms) -> 
    Display = fun(Room) -> io:format("~w~n", [Room]) end, 
    lists:foreach(Display, Rooms). 

可能本身赞成使用列表理解为一个速记的向上传递迭代:

_ = [io:format("~w~n", [Room]) | Room <- Rooms]. 

只有试图得到一个副作用,但是,我真的认为lists:foreach/2是语义原因的最佳选择。

我认为你遇到的困难的一部分是,你选择使用一个相当不寻常的结构作为你的第一个Erlang程序的基础数据,它执行任何操作(数组不常用,而且不是非常习惯于功能语言)。尝试使用列表首先 - 不吓人 - 一些成语和其他代码示例以及关于列表处理和函数式编程的一般讨论将更有意义。

等等!还有更多...

我没有处理的情况下,你有一个不规则的房间布局。假设一直是所有东西都放在一个很好的偶数网格中 - 当你进入真正有趣的东西(或者因为地图不规则或者因为拓扑很有趣)从来就不是这种情况。这里

的主要区别是,而不是简单地携带[Room]一个列表,其中每个Room值是表示室的状态的单个值,你就换了房间元组也包含一些额外的数据的状态值例如它的位置或坐标,名称等(您知道,“元数据” - 这是一个如此过载,充满喧嚣的术语,今天我讨厌这样说)。

假设我们需要维护坐标在房间所在的三维空间中,并且每个房间都有一个居住者列表。在数组的情况下,我们可以将数组除以布局的维数。 10 * 10 * 10空间将具有数组索引从0到999,并且每个位置将通过类似于

locate({X, Y, Z}) -> (1 * X) + (10 * Y) + (100 * Z). 

的操作和各Room的值来找到将[Occupant1, occupant2, ...]

定义这样一个数组然后将其任意大的区域标记为“不可用”以给出不规则布局的印象,然后解决尝试模拟3D宇宙的问题将是一个真正的烦恼。

相反,我们可以使用列表(或类似列表的东西)来表示一组房间,但Room值现在将是一个元组:Room = {{X, Y, Z}, [Occupants]}。你可能有一个额外的元素(或十个!),如房间的“名称”或其他状态信息或其他,但坐标是你可能获得的最确定的真实身份。拿到房状态,你会做和以前一样,但标志着你在看什么元素:

display(Rooms) -> 
    Display = 
     fun({ID, Occupants}) -> 
      io:format("ID ~p: Occupants ~p~n", [ID, Occupants]) 
     end, 
    lists:foreach(Display, Rooms). 

做任何事情比打印顺序更有趣,你可以与使用功能替换Display的内部在图表上绘制房间的坐标,检查Occupants的空白列表或完整列表(使用模式匹配,不要在程序上进行!)或其他您可能想到的任何事情。

8

嗯,这段代码重写,而不是粘贴。它在Room = array:get(In, Rooms)之后缺少冒号。该Bad argument错误大概是这样的:

exception error: bad argument 
in function array:get/2 (array.erl, line 633) 
in call from your_module_name:display/2 

这意味着,你叫array:get/2不良参数:要么房间是不是一个数组,或者你使用的索引超出范围。第二个更有可能是原因。要检查,如果:

In < 59 

,然后再调用显示,所以它会得到58,计算结果为真,并呼吁:

display(Rooms, 59) 

这实在是太多了。

还有其他几件事:

  1. io:format/2它通常是更好地使用~p而不是~w。它完全一样,但印刷漂亮,所以它更易于阅读。

  2. 在Erlang if是不自然的,因为它评估守卫,其中一个必须匹配或者你得到错误......这真的很奇怪。

case是更可读:

case In < 59 of 
    false -> do_something(); 
    true -> ok 
end 

case你平时写的东西,总是相匹配:

case Something of 
    {One, Two} -> do_stuff(One, Two); 
    [Head, RestOfList] -> do_other_stuff(Head, RestOfList); 
    _ -> none_of_the_previous_matched() 
end 

下划线是模式匹配非常有用。

  • 在函数式语言,你永远不应该担心类似指标的详细信息!数组模块具有map函数,该函数将函数和数组作为参数并调用每个数组元素上的给定函数。
  • 所以,你可以写你的代码是这样的:

    display(Rooms) -> 
        DisplayRoom = fun(Index, Room) -> io:format("~p ~p~n", [Index, Room]) end, 
        array:map(DisplayRoom, Rooms). 
    

    这不是十全十美,因为除了调用io:format/2和显示内容,这也将建立新的数组。io:format完成后返回原子ok,因此您将获得58个原子的阵列。还有array:foldl/3,它没有这个问题。

    如果您不必随机访问,最好只使用列表。

    Rooms = lists:duplicate(58, false), 
    DisplayRoom = fun(Room) -> io:format("~p~n", [Room]) end, 
    lists:foreach(DisplayRoom, Rooms) 
    

    如果您对高阶函数不满意。列出让你轻松地编写递归算法与功能的语句:

    display([]) ->     % always start with base case, where you don't need recursion 
        ok;      % you have to return something 
    display([Room | RestRooms]) -> % pattern match on list splitting it to first element and tail 
        io:format("~p~n", [Room]), % do something with first element 
        display(RestRooms).  % recursive call on rest (RestRooms is quite funny name :D) 
    

    总结 - 二郎不要写forloops :)

    +0

    很好的逐行分解,提及'〜p'并注意到'如果'在Erlang中是一个奇怪的表亲。 :-) – zxq9 2014-12-01 20:06:07

    +0

    尼斯和完整的答案:)仍然没有数组:foldl/2;)顺便说一句,我没有反对在erlang for循环。这里是我的尝试:[我的循环](http://stackoverflow.com/questions/27105799/iterate-over-a-cartesian-product-in-erlang-without-generating-a-list-first/27132827#27132827 )。我不认为这是愚蠢的想法:) – 2014-12-01 20:09:25

    +0

    @Łukasz:当然foldl有3个参数!感谢您指出 - 编辑我的答案。有些情况下,您想要交换速度或内存的可读性。我只想指出,Erlang的“默认值”更容易理解。直接从命令式语言移植解决方案可能会导致难以调试代码的尴尬。 – tkowal 2014-12-01 21:28:05