2

我是新來的Erlang和相當新的函數式編程一般。尋找我的二郎綱領批判

我一直在使用Erlang一個很好的時間,到目前爲止(即使Erlang的標點符號有過我絆倒了幾次;)),但我真的很喜歡它,如果我能得到 一些反饋我來自更有經驗的Erlang程序員的代碼。

我的代碼似乎好了工作,但我敢肯定,你們能提供很多建議的改進! :)

下面就來解決2nd Project Euler problem(找到所有偶數素數低於400萬的總和),分成兩個模塊的程序:

-module(seqs). 
-export([takewhile/2, take/2]). 

%% Recursively pick elements from a lazy sequence while Pred(H) is true 
takewhile(Pred, [H|T]) -> 
    case Pred(H) of 
     true -> [H|takewhile(Pred, T())]; 
     false -> [] 
    end. 


%% Take a certain number of elements from a lazy sequence 
%% A non-tail recursive version 
take(0, _) -> []; 
take(Number, [H|T]) -> 
    [H|take(Number - 1, T())]. 

第二個模塊解決了實際的問題:

-module(euler002). 
-import(seqs, [takewhile/2]). 
-export([lazyfib/0, solve/0]). 

%% Sums the numbers in a list (for practice's sake) 
sum([]) -> 0; 
sum([H|T]) -> H + sum(T). 


%% Practicing some list comprehensions as well! 
filter(P, Xs) -> [ X || X <- Xs, P(X) ]. 


%% Lazy sequence that generates fibonacci numbers 
lazyfib(A, B) -> [A | fun() -> lazyfib(B, A + B) end]. 
lazyfib() -> lazyfib(0, 1). 


%% Generate all fibonacci terms that are less than 4 million and sum the 
%% even terms 
solve() -> 
    Fibs = seqs:takewhile(fun (X) -> X < 4000000 end, lazyfib()), 
    sum(filter(fun (X) -> X rem 2 =:= 0 end, Fibs)). 

在此先感謝,並請告訴我,這是不是這種問題的合適論壇! :)

+0

是您takewhile功能真正從名單有什麼不同:takewhile/2?你的函數看起來也和列表類似:sublist/2。有人可能會糾正我,但我不認爲你的「seqs」函數真的很懶, Haskell使用懶惰。例如Erlang沒有無限列表。 – 2011-03-31 11:33:35

回答

2

這裏有一個一般的提示。如果可能的話,你應該嘗試使用尾遞歸(再次,爲了實踐的緣故)。

例如,該功能不是尾遞歸:

%% Sums the numbers in a list (for practice's sake) 
sum([]) -> 0; 
sum([H|T]) -> H + sum(T). 

由於sum(T)結果必須被返回,使得它可以被添加到H。每次遞歸調用都會添加到一個調用堆棧中。你會用盡內存或崩潰,如果你的列表是太大,許多素數相加時等。

爲了使函數尾遞歸,使用這樣的蓄電池:

%% Hide the use of the accumulator, so caller can still use sum/1. 
%% Also note the period ending this function definition. 
sum(List) -> sum(List, 0). 
%% Sums the numbers in a list (for practice's sake) 
sum([], Acc) -> Acc; 
sum([H|T], Acc) -> sum(T, H + Acc). 

雖然這看起來類似,請注意,除了被遞歸調用,它最終成爲最後的指令之前完成。這意味着編譯器可以將它優化爲跳轉而不是調用(因爲它永遠不需要回到這個函數),所以你不會創建一個巨大的調用堆棧。

sum/1sum/2也要注意標點符號。即使具有相同的名稱,不同元素的功能也不同。這就是爲什麼第一行以句號結尾而不是分號。

希望有所幫助。祝你好運。

+0

謝謝! :)我一直在試圖解決使用尾遞歸的後續問題。 Erlang的標點符號讓我感覺非常糟糕,但我現在已經習慣了。我發現標點符號也有其優點 - 它允許Emacs的Erlang模式非常智能地表現出來,對於一個人來說:) – Christoffer 2011-03-31 09:20:06

0

考慮通過整潔的運行代碼。

http://tidier.softlab.ntua.gr:20000/tidier/getstarted

我看到了一個簡短的介紹,以及一些代碼風格和LOC優化它製成的人拍案叫絕。

+0

這是一個相當不錯的工具!我用我的最新文件嘗試過,雖然它沒有改變任何代碼,但它確實使它更漂亮! ;)(我會upvote你的答案,但我沒有代表尚未:P) – Christoffer 2011-03-31 09:26:37

1

我明白迭代器和發電機,但無論如何簡單的解決方案推廣功能法美就簡單得多了,漂亮:

-module(euler). 

-export([euler2/1]). 

euler2(Limit) -> euler2(Limit, 0, 1, 1). 

euler2(Limit, Sum, A, _B) when A > Limit -> Sum; 
euler2(Limit, Sum, A, B) -> 
    NewSum = case A rem 2 of 0 -> Sum+A; _ -> Sum end, 
    euler2(Limit, NewSum, B, A+B).