Haskell 复制的定义不足

Haskell 复制的定义不足,haskell,Haskell,我有一个问题,我觉得很棘手 标准前奏曲包含该功能 replicate :: Int -> a -> [a] 下面的定义似乎是合理的 replicate n x = take n [x,x,..] 但这实际上是不够的。为什么不呢 我知道,复制功能的定义如下: replicate :: Int -> a -> [a] replicate n x = take n (repeat x) repeat :: a -&g

我有一个问题,我觉得很棘手

标准前奏曲包含该功能

replicate :: Int -> a -> [a]
下面的定义似乎是合理的

replicate n x = take n [x,x,..]
但这实际上是不够的。为什么不呢

我知道,
复制
功能的定义如下:

replicate        :: Int -> a -> [a]       
replicate n x    =  take n (repeat x)
repeat           :: a -> [a]  
repeat x         =  xs where xs = x:xs
repeat
定义为:

replicate        :: Int -> a -> [a]       
replicate n x    =  take n (repeat x)
repeat           :: a -> [a]  
repeat x         =  xs where xs = x:xs

(从问题中)定义是否不够,因为它使用无限列表?

首先,问题中有一个小的语法错误,应该是:

replicate n x = take n [x,x..]
--                         ^ no comma
但我们不要挑剔

现在,当您使用范围语法(即
x..
)时,
x
应该是
Enum
的实例类型。事实上:

Prelude> :t \n x -> take n [x,x..]
\n x -> take n [x,x..] :: Enum a => Int -> a -> [a]
Prelude>:t\nx->take n[x,x..]
\nx->take n[x,x..]::Enum a=>Int->a->[a]
您可以争辩说,
x,x..
只会生成
x
,但Haskell编译器在编译时并不知道这一点

因此,
replicate
(问题中)中的类型太具体了:它意味着一个类型约束-
枚举a
,这实际上是不必要的


另一方面,你自己的定义很好。Haskell对无限列表没有问题,因为它使用惰性计算。此外,由于您使用
xs
作为尾部定义
xs
,因此实际上构建了一个循环链表,它在内存使用方面也更好。

您是否尝试过编译建议的定义?编译器可能会告诉你为什么它有问题。(顺便说一句,您应该复制问题中的文本,避免使用图像。)用于定义
repeat
的循环链表并不能使
replicate
的定义比以更直接的递归方式编写的定义更好<代码>复制固有地需要分配列表的完整长度(惰性地)。在GHC中实际发生的情况是,这里使用的
repeat
将(通过重写规则)与
take
相融合,产生类似于手写
replicate
@dfeuer的东西:它确实没有改进
replicate
的定义,但我的观点是,在某些情况下,它可以改进依赖于
repeat
(从而改进
repeat
本身)的其他定义。例如,假设的
sumLess
,如果总和较小,则测试成功。由于您一直在处理同一个引用,如果
sumLess
repeat
中穿行,内存使用不会增加。太棒了!非常感谢你的解释。