Haskell:递归复制函数

时间:2015-06-20 21:08:57

标签: haskell recursion replicate

我刚刚开始使用Haskell。我试图在Haskell中创建一个模仿标准replicate函数但使用递归的函数。例如,

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!",...

3 个答案:

答案 0 :(得分:7)

你必须把第二种情况放在第一种情况之前,否则它永远不会进入第二种情况。

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

答案 1 :(得分: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将与您传递的任何值匹配,包括0绑定x。 @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 ...链。

答案 2 :(得分:1)

您可以使用地图:

myReplicate :: Int -> a -> [a]
myReplicate n x = map (const x) [1..n]

您还可以使用Data.Functor中的$>

import Data.Functor 
myReplicate :: Int -> a -> [a]
myReplicate n x = [1..n] $> x