`

Learn Haskell(二)

阅读更多

1.Ranges
有时候我们有这样一种需求:我们需要一个List,它的元素从1到20。从1到20敲出每一个元素肯定是一种可以搞定的办法,但肯定不是好办法。这时候,我们可以使用Haskell的Ranges来处理这种需求。
Ranges用来构建元素可以按照某种顺序枚举的List。像数字1,2,3,4就是可枚举的;字母a,b,c,d也是。我们看看两个Ranges的例子:上面说到的1-20,使用Ranges可以这样表示:

Prelude> [1..20]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]

 同理,所有的小写字母可以这样表示:

Prelude> ['a'..'z']
"abcdefghijklmnopqrstuvwxyz"

 这只是最基本的,我们还可以设置Ranges的步进(step )。比如,我们需要一个含有1-20中所有偶数的List,我们可以这样:

Prelude> [2,4..20]
[2,4,6,8,10,12,14,16,18,20]

 但是需要注意的是,只能够设置一个步进,下面这种就不是Ranges了:

Prelude> [1,2,4,8..100]
<interactive>:5:9: parse error on input..'

 如果你不设置上限,那么得到的就是一个无限的List:

Prelude> [1..]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27, 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51, 52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75, 76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99, 100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117, 118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135, 136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153, 154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171, 172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189, 190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225, 226,227,228,229,230,231,232,233,234,235,236
......

 我们可以结合之前介绍的take函数,按你的要求获得指定数目的元素构成的List:

Prelude> take 5 [1..]
[1,2,3,4,5]

 这种方式是可行的,因为Haskell一旦得到了指定数目的元素就拉到了,不会去评估整个无限的List。 当然还有几个函数可以创建无限的List:

  • cycle函数

直接看例子吧:

Prelude> take 10 (cycle [1,2,3])
[1,2,3,1,2,3,1,2,3,1]

 cycle的参数是一个列表:

  • repeat函数

看例子:

Prelude> take 10 (repeat 5)
[5,5,5,5,5,5,5,5,5,5]

 repeat的参数是单个元素或者List:

Prelude> take 10 (repeat [1,2,3])
[[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3]]
 
  • replicate函数

看例子吧:

Prelude> replicate 3 5
[5,5,5]

 replicate创建一个含有a个元素b的List
需要特别指出的是,浮点数使用Ranges会出现精度问题。
2.List Comprehension
List Comprehension是一种过滤、转换和连接List的方式。List Comprehension与数学中集合的定义非常类似,比如:

要完成这样的工作,我们可以用之前提到take 函数:

Prelude> take 10 [2,4..]
[2,4,6,8,10,12,14,16,18,20]

 我们也可以使用List Comprehension的方式完成这个工作:

Prelude> [x*2|x<-[1..10]]
[2,4,6,8,10,12,14,16,18,20]

 这个的意思是,从List[1..10]中依次取出每个元素给x,然后x乘以2。在List Comprehension中,|左边的部分是输出,这一部分我们可以定义我们想要的输出是什么样子。感觉这种方式其实不是很方便,实际上List Comprehension更适合于比较复杂的问题。List Comprehension可以加条件。例如:

Prelude> [x * 2 | x <- [1..10], x > 5]
[12,14,16,18,20]

这个List Comprehension的意思很直接,1-10中,大于5的翻倍输出。条件是由, 隔开的,可以有多个。还可以在函数定义中使用List Comprehension:

let boomBangs xs = [ if x < 10 then "BOOM" else "BANG" | x <- xs, odd x]

 函数boomBangs接收一个List,将List中的大于10的奇数替换为"BANG",将小于10的替换为"BOOM"。我们可以使用一下这个函数:

Prelude> boomBangs [7..13]
["BOOM","BOOM","BANG","BANG"]

 我们还可以同时添加多个条件:

Prelude> [x | x <- [10..20], x /= 11,x /= 13, x /= 15]
[10,12,14,16,17,18,19,20]

 不仅条件可以添加多个,也可以同时从多个List中取值:

Prelude> [x * y | x <- [1,2,3], y <- [4,5,6]]
[4,5,6,8,10,12,12,15,18]

 再看几个例子:

 

Prelude> [x * y | x <- [2,5,10], y <- [8,10,11]]
[16,20,22,40,50,55,80,100,110]
Prelude> [x * y | x <- [2,5,10], y <- [8,10,11], x * y > 50]
[55,80,100,110]
Prelude> let nouns = ["hobo","frog","pope"]
Prelude> let adjectives = ["lazy","grouchy","scheming"]
Prelude> [adjective ++ " " ++ noun | adjective <- adjectives, noun <- nouns] ["lazy hobo","lazy frog","lazy pope","grouchy hobo","grouchy frog","grouchy pope","scheming hobo","scheming frog","scheming pope"]

 我们可以使用List Comprehension来实现一个length函数:

length' xs = sum [1 | _ <- xs]

 这个函数对于List xs中出现的每一个元素输出1,最后通过sum函数计算有多少个元素。再看一个去除字符串中小写字母的函数:

 

removeNonUppercase st = [ c | c <- st, c elem ['A'..'Z']] Prelude> let removeNonUppercase st = [ c | c <- st, c elem ['A'..'Z']]
Prelude> removeNonUppercase "Hahaha! Ahahaha!"
"HA"
Prelude> removeNonUppercase "IdontLIKEFROGS"
"ILIKEFROGS"

 

此外,List Comprehension也是可以嵌套的:

Prelude> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]
Prelude> [ [ x | x <- xs, even x ] | xs <- xxs]
[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]

 3.小结
看了这么多例子,个人感觉List Comprehension在处理一些比较复杂的情况很灵活,尤其是允许嵌套List Comprehension。

1
5
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics