2015-06-20 105 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 ...链非常相似。