【问题标题】:Haskell: recursive Replicate functionHaskell:递归复制函数
【发布时间】:2025-12-23 06:45:11
【问题描述】:

我刚刚开始使用 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!",...

【问题讨论】:

  • “不起作用”是什么意思?当你运行这个函数时会发生什么?

标签: haskell recursion replicate


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    您的代码应该生成警告读数(至少在 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 将匹配您传递的任何值,包括用于 x 绑定的 0。 @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 ... 链。

    【讨论】:

      【解决方案3】:

      你可以使用地图:

      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
      

      【讨论】: