2015-06-20 3 views
2

我剛剛開始使用Haskell。我試圖創建一個函數,模仿Haskell中的標準replicate函數,但使用遞歸。例如,Haskell:遞歸重複函數

Prelude> replicate 3 "Ha!" 
["Ha!","Ha!","Ha!"] 

它應該是Int -> a -> [a]。到目前爲止,我有:

myReplicate :: Int -> a -> [a] 
myReplicate x y = y : myReplicate (x-1) y 
myReplicate 0 y = [ ] 

然而,我的函數總是產生無限列表:

Prelude> myReplicate 3 "Ha!" 
["Ha!","Ha!","Ha!","Ha!","Ha!","Ha!","Ha!",... 
+2

你是什麼意思「不起作用」?當你運行這個功能會發生什麼? –

回答

7

之前已率先把第二種情況,否則將永遠無法與第二種情況。

myReplicate :: Int -> a -> [a] 
myReplicate 0 y = [ ] 
myReplicate x y = y : myReplicate (x-1) y 
4

您的代碼應該產生一個警告閱讀(GHC中,至少):

Pattern match(es) are overlapped 
In an equation for 'myReplicate': myReplicate 0 y = ... 

正在發生的事情是,代碼試圖您輸入對你寫的每一個定義相匹配,在你寫的順序(自上而下)。當您編寫f x = ...時,x變量將始終與它所表示類型的任何值相匹配。如果定義中的所有綁定匹配,那麼將使用該定義。

就你而言,第一個定義是myReplicate x y = y : myReplicate (x-1) y。正如我所說,xy將與您通過的任何值匹配,包括0x的綁定。 @Alec提出的解決方案展示瞭如何通過先寫入最具體的模式以及最後寫入的全部模式來避免此問題。

另一種解決方案是使用侍衛:

myReplicate :: Int -> a -> [a] 
myReplicate x y 
    | x > 0 = y : myReplicate (x-1) y 
    | x == 0 = [] 
    | otherwise = [] -- or throw an exception, or use Maybe 

這種方式,你可以寫在任何順序中使用的表述,如果你寫的條件正確(換句話說,如果條件是互相排斥的)。請注意,條件仍將首先從頂部進行評估,然後一直下降,直到條件成立,這與命令式語言中的if ... else if ... else if ... else ...鏈非常相似。