本节书摘来自异步社区《Haskell趣学指南》一书中的第1章,第1.4节得州区间2,作者 【斯洛文尼亚】Miran Lipovaca,更多章节内容可以访问云栖社区“异步社区”公众号查看
1.4 得州区间2
该怎样得到一个由1~20所有数组成的列表呢?我们完全可以用手把它们全都录入一遍,但显而易见,这并不是完美人士的方案,完美人士都用区间(range)。区间是构造列表的方法之一,而其中的值必须是可枚举的,或者说,是可以排序的。
例如,数字可以枚举为1、2、3、4等。字符同样也可以枚举:字母表就是A~Z所有字符的枚举。然而人名就不可以枚举了,“John”后面是谁?我不知道。
要得到包含1~20中所有自然数的列表,只要录入[1..20]即可,这与录入[1,2,3,4,5,6,7,8, 9,10,11,12,13,14,15,16,17,18,19,20]完全等价。两者的唯一区别是手写一串非常长的列表比较笨。
下面是一些例子:
ghci> [1..20]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
ghci> ['a'..'z']
"abcdefghijklmnopqrstuvwxyz"
ghci> ['K'..'Z']
"KLMNOPQRSTUVWXYZ"
区间很聪明,允许你告诉它一个步长。要得到1~20中所有的偶数,或者3的倍数该怎样?只要用逗号将前两个元素隔开,再标上区间的上限就好了:
ghci> [2,4..20]
[2,4,6,8,10,12,14,16,18,20]
ghci> [3,6..20]
[3,6,9,12,15,18]
尽管区间很聪明,但它恐怕还是难以满足人们对它过分的期许。比如,你就不能通过[1,2,4,8,16..100]这样的语句来获得100以下的所有2的幂。一方面是因为步长只能标明一次,另一方面就是仅凭前几项,数组后面的项也有可能是无法确定的。
注意:
要得到从20到1之间的列表,[20..1]是不可以的,必须得[20,19..1]。对于没有提供步长的区间(如[20..1]),Haskell会先构造一个空的列表,随后从区间的下限开始,不停地赠长,直到大于等于上限为止。既然20已经大于1了,那么所得的结果只能是个空列表。
你也可以不标明区间的上限,从而得到一个无限长度的列表。在后面我们会讲解关于无限列表的更多细节。取前24个13的倍数该怎样?下面是一种方法:
ghci> [13,26..24*13]
[13,26,39,52,65,78,91,104,117,130,143,156,169,182,195,208,221,234,247,260,273,286,
299,312]
但有更好的方法——使用无限长度的列表:
ghci> take 24 [13,26..]
[13,26,39,52,65,78,91,104,117,130,143,156,169,182,195,208,221,234,247,260,273,286,
299,312]
由于 Haskell 是惰性的,它不会对无限长度的列表直接求值(不然会没完没了)。它会等着,看你会从它那儿取哪些元素。在这里它见你只要前24个元素,便欣然交差。
下面是几个生成无限列表的函数。
cycle函数接受一个列表作为参数并返回一个无限列表。
ghci> take 10 (cycle [1,2,3])
[1,2,3,1,2,3,1,2,3,1]
ghci> take 12 (cycle "LOL ")
"LOL LOL LOL "
repeat函数接受一个值作为参数,并返回一个仅包含该值的无限列表。这与用cycle处理单元素列表的效果差不多。
ghci> take 10 (repeat 5)
[5,5,5,5,5,5,5,5,5,5]
若只是想得到包含相同元素的列表,直接使用replicate函数将更加简单,它取一个参数表示列表的长度,一个参数表示列表中要复制的元素:
ghci> replicate 3 10
[10,10,10]
最后,在区间中使用浮点数要格外小心!浮点数依据定义,只能实现有限的精度。若是在区间中使用浮点数,你就会得到如下的糟糕结果:
ghci> [0.1, 0.3 .. 1]
[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]