我是 Haskell 的绝对新手,但仍在尝试了解它是如何工作的。
我想编写自己的惰性整数列表,例如 [1,2,3,4,5...]。
查看我写的列表
ones = 1 : ones
经过尝试,效果很好:
*Main> take 10 ones
[1,1,1,1,1,1,1,1,1,1]
我怎样才能对增加的整数做同样的事情?
我已经尝试过,但确实失败了:
int = 1 : head[ int + 1]
在那之后我怎样才能制作一个将两个流相乘的方法?如:
mulstream s1 s2 = head[s1] * head[s2] : mulstream [tail s1] [tail s2]
int = 1 : head [ int + 1]
不起作用的原因是:
:
的第二个参数需要是一个列表。int + 1
尝试添加列表和数字,但这是不可能的。创建从 1 到无穷大的列表的最简单方法是
[1..]
要以 1 以外的步数进行计数,您可以使用
[firstElement, secondElement ..]
,例如创建所有正奇数的列表:[1, 3 ..]
要获取
[x, f x, f (f x), f (f (f x)),...]
形式的无限列表,您可以使用 iterate f x
,例如iterate (*2) 1
将返回列表 [1, 2, 4, 16,...]
。
要对两个列表的每对元素成对应用操作,请使用 zipWith:
mulstream s1 s2 = zipWith (*) s1 s2
为了使这个定义更加简洁,您可以使用无点形式:
mulstream = zipWith (*)
对于自然数,你必须使用映射:
num1 = 1 : map (+1) num1
或理解:
num2 = 1 : [x+1 | x <- num2]
或者当然:
num3 = [1..]
语言中有这样的语法:
take 10 [1,2..]
=> [1,2,3,4,5,6,7,8,9,10]
您甚至可以做不同的步幅:
take 10 [1,3..]
=> [1,3,5,7,9,11,13,15,17,19]
我不确定这是否是您要问的,但在我看来,您想构建一个自然数递增的列表,而不依赖于任何其他列表。因此,据此,您可以做类似的事情
incr a = a : inrc (a+1)
lst = inrc 1
take 3 lst
=> [1,2,3]
从技术上来说,这被称为累积函数(我相信),然后我们所做的就是使它的特殊情况可以通过“
lst
”轻松使用
你可以从那里发疯,做这样的事情:
lst = 1 : incr lst where incr a = (head a) + 1 : incr (tail a)
take 3 lst
=> [1,2,3]
等等,虽然这可能依赖于一些你还没有学到的东西(在哪里)——根据OP判断——但它仍然应该很容易阅读。
哦,对了,然后是列表乘法。好吧,你可以像上面提到的那样使用
zipWith (*)
,或者你可以像这样重新发明轮子(这更有趣,相信我:)
lmul a b = (head a * head b) : lmul (tail a) (tail b)
safemul a b
| null a || null b = []
| otherwise
= (head a * head b) : safemul (tail a) (tail b)
safemul
的原因,我相信,你可以通过尝试lmul
这个函数来找到答案,但它与'tail
'(还有'head
')有关。问题是,lmul
中不存在空列表、不匹配列表等情况,因此您要么必须将各种定义拼凑在一起(lmul _ [] = []
),要么使用守卫和或where
和等等...或者坚持zipWith
:)
您可以定义一个最多一定数量的列表,然后通过保持前者完整(依此类推)将第一个与第二个求和,如下所示:
ones :: Integer -> [Integer]
ones n
| n <= 0 = []
| otherwise = one n []
where
one 1 a = (1:a)
one n a = one (n-k) (one k a)
where
k = (n-1)
sumOf :: [Integer] -> [Integer]
sumOf l = sof l []
where
sof [] a = a
sof (x:[]) a = (x:a)
sof (x:y:zs) a = sof (x:a) (sof ((x+y):zs) a)
由于它们都是 1,因此您可以通过更改它们的总和顺序,以任何您喜欢的方式增加它们,从左到右,到中间点等等。您可以使用以下方法测试最多一百个(或更多):
(sumOf . ones) 100
编辑:为了简化,请阅读 Will Ness 下面的评论。
为了完整起见,这里有另一种与
[0..]
等效的技术,使用 as-pattern (@
) 来生成无限的自然数流:
naturals = 0 : allthefollowingnat naturals where
allthefollowingnat (current : successors@(_)) = immediateSuccessor : allthefollowingnat successors where
immediateSuccessor=current+1
虽然这种技术对于生成自然数流来说可能有些过大,但它遵循的模板对于定义各种流非常有用,其中下一个值依赖于前一个值。例如,这是一个斐波那契流:
fibstream = 0 : 1 : allthefollowingfib fibstream where
allthefollowingfib (previous : values@(current : _)) = next : allthefollowingfib values where
next = current+previous