2014-12-01 82 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