所有文章

学习haskell(一)

上周去国图还书的时候无意中看到了一本讲haskell的书,就顺手借回来了。想想第一次听说haskell这个名字还是从王垠的博客里看到的,那时候听他在博客里讲惰性求值,列表解析,lambda函数啊,什么的,根本听不懂。不过到现在为止,深入学习了python,对于列表解析,lambda函数还算是有一个浅浅的了解。而且已经学习过scheme和sml了,虽然都没有花太多时间去学习它们,但是至少对函数式编程了解了一个大概。

以我目前对于函数式编程的理解来说,主要就是一切都是函数,数据是函数,函数也是函数。一个变量只有一个值,不能通过赋值来改变一个变量的值,只能让它重新绑定到另外一个值,所以变量的状态不会改变,不会出现副作用。这些都是一些很粗浅的理解,毕竟没有正儿八经的研究过函数式编程。主要有一个原因就是现在用函数式语言来开发的项目不多,我们学习编程主要还是用来解决实际问题,如果这门语言现实中没什么人用,或者是没法用到,那么学习它的激情就不会太高,虽然说学习过之后可能会提升编程思维。目前我了解的比较有名的函数式语言开发的项目有emacs,这个是用elisp语言来开发的一个编辑器,很强大。texmacs,一个所见即所得的tex编辑器,用的是scheme开发的。guile,gnu世界中的一个lisp编译器,能把lisp代码编译为c语言代码。chicken scheme,又一个scheme的编译器,按照官网说的它编译机器码的效率也是很高的,而且好像第三方库也比较多。lisp族就这些了,而sml族就没有听说过什么大的项目,不得不说很令人沮丧。相比现在流行的c/c++,java,perl/python/php等语言,函数式语言处于一个很小众的地位。为什么这种语言这么强大但是却没什么发展呢?真是搞不懂。

后面开始去搜索haskell的相关信息,发现在80年代的时候一伙科学家发现函数式语言类别太多了,各种lisp变种太多,都有自己的优点,于是这伙人就想要一统函数式语言的天下,他们组建了一个小团体来研究未来的函数式语言。不得不说,开发这事就是得有很多人参与,没什么人参与的项目肯定不会发展的太好,linux的发展是这样,python是这样,都是有一个很大的志愿者团体来维护的,而像freebsd和scheme这样的东西则是在各个很牛逼的大学小范围流传。

haskell算是一个严格类型的语言,也就是说每个变量都有一个类型,数字和字符串之间不能直接操作。目前python和php代码写多了导致我不是很喜欢这个特性,每个变量都有一个类型,多麻烦,数字和字符串之间不能强制转换,不能比较,不能拼接,诸多不便。即使是scheme也不是强类型的,把一个数字添加到一个列表后面在scheme里面是很正常的。sml在这点和haskell差不多,不过sml比haskell更加严格,在定义函数的时候得写明函数接收哪些类型的参数。

不过强类型语言也有一个优点就是执行效率高,在编译过程中执行类型检测,发现问题直接报告异常然后退出,而不是在运行时报告异常。而且强类型语言生成的代码跑起来的时候需要更少的内存。

haskell有自己的编译器,似乎种类有好几种,我在archlinux上用的是ghc,这个编辑器自带了一个解释器叫做ghci,安装好之后在终端输入ghci就能进入解释器,一行一行的执行输入的表达式了。python,php,ruby,scheme,sml都有自己的解释器,能够对每一个表达式求值,真方便。我最讨厌要编译机器码的语言了。

haskell有多种内置的类型,我现在接触了数字,字符串,列表,元组这4个类型。要定义一个变量也很简单,直接let a = 1这样就定义了一个变量a,它绑定了一个数字1,当然我们也可以理解为这行代码定义了一个函数,当执行这个函数的时候会返回一个数字1,在这里函数也变成普通变量了。

字符串是用双引号引起来的,let s = "hello" 就生成了一个字符串变量s,是把它当作函数还是当作普通变量就看个人喜好了。如果要获得某个字符串中间的一个字符,可以使用!!操作符,比如说"hello" !! 2 会返回一个’l’,看惯了c数组风格的字符串索引再来看haskell的形式,真是不习惯。

列表是函数式语言的最重要的数据结构,从50年代麦卡锡发明的表处理语言开始,在各个函数式语言里都可以看到列表的各种表现形式。scheme的car和cdr,sml的hd和tl,而在haskell里面则是head和tail。定义一个列表let l = [1,2,3,4]head l返回列表第一个元素,tail l返回列表除了第一个元素外剩下的元素,init l返回列表除了最后一个元素外的所有元素,last l返回最后一个元素。剩下的还有maximum, minimum, reverse, length, null, take, drop, sum, product, elem等方法,看得眼花缭乱啊,至于为一个这么基本的数据结构写这么多方法吗? 对于列表还有一个很重要的特性就是可以生成一个列表范围,如果一个集合的元素是可以用很清晰的语言枚举出来的话那么这个集合可以用列表表示出来,比如说如果要生成1到100的所有整数,那么只要[1 .. 100]就可以了,如果要确定步长的话只要枚举前2个元素就好了[1, 3 .. 100]这样就生成了1到100的所有奇数。那么能不能生成[1,2,4,8 …]这样的变步长的列表呢?很遗憾,haskell没有把这种列表写入最基本的列表方法里面,也很好理解,对于变步长的列表给出了前几个元素不是很好推测下一个元素,如果不清楚它那么就不能实现它,写程序的至理名言。

接下来就涉及到列表解析了,它对应数学里面一个很重要的概念—集合,{x | x < 10,x是正整数}这是一个很简单的集合,在haskell里面就是[1 .. 9]。如果集合是{x | x < 10,x是偶数},在haskell里面就是[x | x <- [1 .. 9], x `mod` 2 == 0],这就是很简单的列表解析了。

元组和列表差不多,不过限制比较松散,列表要求所有的元素都是同一个类型的,而元组没有这个限制(1, 2, "hello")这样就建立了一个三个元素的元组,

haskell是一门很有前景的语言,得好好学学,希望能够坚持每天学点haskell,每天积累一点新知识。