Haskell 笔记(二)List和元组

时间:2023-01-31 17:03:06

最简单的函数


函数式编程,首先要有函数吧,来看一个最简单的函数,第一个是函数名,后面是输入变量,表达式是输出

doubleMe x=x+x
  • Haskell的函数没有return语句,最后一句话是自然的返回值,函数都要保存为文件,文件以.hs结尾。
  • 读取编写好的文件(以.hs结尾)

    :l 文件名
    

List 数据结构


LIST基本操作

  • List中各个元素的类型需要一致
  • 使用 let XXX = [1,2,3,4,5]进行声明
  • 字符串也是一个List
  • 使用:添加新元素,如 5 : [1,2,3,4]将5添加到List中,可以加在之前或者之后
  • 使用!!来获取List索引的数据,如 [1,2,3,4]!!2 结果是2
  • head返回第一个元素,tail返回除了第一个以为的后面元素
  • last返回最后一个元素,init返回除最后一个元素的前面其他元素
  • length返回list长度
  • null 检查list是否为空,为空返回True
  • reverse反转一个List
  • take返回list的前几个元素 take 2 [1,2,3,4]
  • drop删除前几个元素
  • maximum返回list最大值,minmum返回最小值
  • sum求和
  • elem判断元素是否在list中,使用中缀表达式 4 \elem` [1,2,3,4]`

RANGE操作

  • 使用..来生成大量连续数据,如[1..100]
  • 可以使用前两个元素来表示步长,如[1,3...100]生成100内的奇数
  • Haskell支持无限长的List,如[1,2..],Haskell是惰性语言,只有在运行的时候才会去求值,所以支持无限长序列
  • cycle用来循环一个List,生成一个无限长的List,如cysle [1,2,3],将生成[1,2,3,1,2,3..]
  • repeat用来重复一个数据,并生成一个无限长List,如repeat 3将生成[3,3,3,3,3..]

LIST的包含和过滤

Haskell是函数式编程语言,所以有一些我们之前在普通编程语言中不一样的东西,Haskell更接近数学描述,比如数学集合中的comprehension

List可以这么写:

[x*2 | x <- [1..10]]

这个List将返回[2,4,6,8,10,12,14,16,18,20],很显然,|左边是表达式,右边是集合,将集合[1..10]分别赋值给x,然后进行x*2的运算,产生一个新的List。

对于上面那个式子,我们还可以修改一下,加一些约束,这个叫过滤

[x*2 | x <- [1..10], x*2 >= 12]

这样,返回的是[12,14,16,18,20]了,只有表达式>=12才会输出

来些实在的


好,我们来个复杂的,写一个函数,函数名为testfunc,在输入的集合中找出所有大于10并且除以3余2的所有奇数,并且将找出来的这些奇数都乘以5

testfunc abc=[ x*5 | x<-abc,x>10,x `mod` 3==2,odd x]

好,把这个保存为一个.hs结尾的文件func.hs。然后打开ghci终端,输入:l func,运行 testfunc [20..60],结果将输出[115,145,175,205,235,265,295],怎么样,简单把,你想想用命令式的编程语言(比如C语言)实现这个将如何写?呵呵

好吧,再来个难一点的,找出给定序列中的所有素数,先想想C语言怎么写,我来写个Haskell的,为了好看,我把一行的内容分开到几行了。

primeNum inNum=[x| x<- inNum,
                    odd x, 
                    sum[1|_<-[y|y<-[2..x-1],
                                x `mod` y == 0]
                        ]==0
                ]

在命令行输入:

primeNum [3..100]

输出结果为:

[3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]

对,就一行,我来解释一下,inNum是输入序列,先判断是否是奇数,然后从2到x-1重新生成一个序列赋值给__表示我们不关心值是什么,然后x取模y如果等于0,就输出一个1,否则不输出,输出的新序列求和,如果和等于0,表示没有一个x取模y等于0,表示这是一个素数,那么就把这个x输出出来。听起来挺绕口吧,仔细体会一下就明白了,你会发现函数式编程比命令行更接近我们的思维和数学模型。


Tuple(元组)数据结构


从某种意义上讲,Tuple (元组)很像 List –都是将多个值存入一个个体的容器。但它们却有着本质的不同,一组数字的 List 就是一组数字,它们的类型相同,且不关心其中包含元素的数量。

  • Tuple 则要求你对需要组合的数据的数目非常的明确,它的类型取决于其中项的数目与其各自的类型。
  • Tuple 中的项 由括号括起,并由逗号隔开。
  • 另外的不同之处就是 Tuple 中的项不必为同一类型,在 Tuple 里可以存入多类型项的组合。
  • fst返回元组的第一个元素 fst(8,11),返回8
  • snd返回元组的最后一个元素
  • zip函数,通过两个List生成元组List,两两集合,比如zip [1,2,3] [4,5,6]将生成[(1,4),(2,5),(3,6)]不同长度的List通过zip组合按照短的那个List组合

来些实在的


我们有这么一个要求,在每条边都小于100的三角形中,找出所有直角三角形,并列出三条边的长度,首先,你还是想想用C语言怎么写这个程序,我还是来个haskell的。

[(a,b,c) | a<-[1..100],b<-[1..a],c<[1..b],c^2+b^2==a^2]

恩,还是一行搞定,都不需要函数了。


本教程参考了《Haskell趣学指南》,是这篇指南的学习笔记,不过参入了我自己的一些想法和总结,首先,感谢《Haskell趣学指南》的作者bonus 和大陆翻译者Fleurer和*翻译者MnO2,谢谢你们之前的工作。