3

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

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

3 回答 3

9

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

myReplicate :: Int -> a -> [a]
myReplicate 0 y = [ ]
myReplicate x y = y : myReplicate (x-1) y
于 2015-06-20T21:16:03.983 回答
5

您的代码应生成警告读数(至少在 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. 正如我所说,x它将y与您传递的任何值匹配,包括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 ...命令式语言中的链。

于 2015-06-20T21:24:42.717 回答
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
于 2020-02-08T21:23:03.630 回答