Chap3. 语言基础 **坑未填完**

haskell与C Java 这些大家熟悉的语言相比,语法很不相同,或许看熟了你会觉得她非常的优雅美丽~

首先要牢记Haskell是一门 Lazy 的语言。
Lazy的好处就是可以很方便的定义一个无限的数据结构:

makeList = 1 : makeList
makeList 是一个列表,它的第一个元素是1,第二个元素又是一个 makeList,于是第二个元素就是 1 和一个 makeList , and on and on.....
我们并不需要一个无限的内存空间来存储这样一个列表,因为除非我们用到这个列表中的那些元素,他们才会存在。当编译器看到这一行时,只是记下了这么一条定义,然后什么也不做,当我们需要这个列表中的元素的时候,它才会去按这个规则来进行计算产生出这个列表(的一部分)。

Haskell是大小写敏感的, haskell 中的值的定义用小写字母开头,而 Type 的定义用大写字母开头。

关于 Side Effect 副作用
通俗的说,副作用就是除了返回值的计算之外的一切东西:print、写文件、改变一个全局变量这些都是副作用。可以把一个函数当作一个黑箱子,一端输入一端输出。箱子之外的世界隔绝开,如果对于同样的输入肯定有相同的输出,并且没有改变箱子外面的任何东西,那么这个函数就是纯函数的。从这个说法我们知道,随机数函数并不是一个纯函数,因为每次调用结果不相同。
没有副作用的函数就称为纯函数。

好了,准备好清空自己的脑子吧,C中的思维要受到挑战了:

int x = 5;
x = 10;

这样的代码在haskell中是不可能了! C语言与计算机硬件结构有种隐喻的对应关系,我们可以把一个变量想象成内存中的一个单元,甚至可以精确说出它占用几个字节在什么偏移量上。在haskell中,我们完全不知道这些,我们甚至不能保证它在运行时是否存在。当然,如果问题能解决,何必要去关心呢?haskell 等fp语言是建立在lambda演算等数学概念上面的,他们并不对应着图灵机(虽然理论上他们等价,但那毕竟又远了一层),现在我们暂时忘记CPU,忘记内存,只考虑数学概念上的函数。
没有变量,没有变量的改变,没有 for (i=0; i < n; i++ ) 我们怎么办?把这看作福利吧,我们有更好的方式解决,程序将变的更好理解:
比较:

  • map (*2) [1..10]
  • for (i = 0;i<10;i++){
    i * 2;
    }

熟悉后,我们可以一眼说出第一种程序到底 干什么,而对于第二种,我们知道它 干了 什么,但是它到底 干什么,需要想一下才能确定。这只是简单的情况,如果循环里面再加点杂七杂八的东西就更好玩了。

----

3.1 算术

没什么好说的,基本的算术运算,运算符之间有优先级关系。

Hugs.Base> 3 * 5
15
Hugs.Base> 2 ^ 200
1606938044258990275541962092341162602522202993782792835301376
Hugs.Base> 10 / 3
3.33333333333333
Hugs.Base> sqrt 2
1.4142135623731

3.2 Pairs, Triples and More

通过括号组合起来的数据结构,称作 Tuple ,可以存放不同类型的数据,比如: ("albert", 26) , 而后面会介绍到的 List 则只能存放相同类型的数据。
对于两个元素的 tuple, 称为 pair ,可以用 fst snd 来分别存取这两个元素。

Hugs.Base> fst ("albert", 26)
"albert"
Hugs.Base> snd ("albert", 26)
26

3.3 List 列表

tuple 只能存固定数量的元素,每个元素可以不同类型。
list 可以存任意数量的元素,但是每个元素类型必须相同。
(haskell是为了体现世界是公平的才这样设计的?)

Hugs.Base> [1,2,3]
[1,2,3]
Hugs.Base> [1..5]
[1,2,3,4,5]
Hugs.Base> 0 : [1..3]
[0,1,2,3]
Hugs.Base> 1:2:3:4:[]
[1,2,3,4]
Hugs.Base>

几个简单的例子, :表示列表的连接(cons操作符)。 实际上 [1,2,3] 这样的表示方式就是 1:2:3:[] 的一个语法糖。

List 是Haskell程序中非常常用的一种数据结构(Python中的 List也是),有很多的函数可以操作list,如 length, head, tail 等
看到 head, tail 有没有想起Lisp中的 car, cdr ?

String

String 是一种特殊的 List ,包含的元素是 Char类型 [Char] ,这很容易理解,和 C语言中的情况很相似。
两个字符串可以用 ++ 来连接,同样 List 也可以。

通过 show 可以把非字符串的数据转成字符串,通过 read 则相反:把字符串读出成其他数据。

Hugs.Base> "num is:" ++ show 5
"num is:5"
Hugs.Base> read "5" + 3
8

List的简单函数操作

大部分的程序会用到 List 操作,可以代替很多传统语言中的循环、选择等功能。常用的List处理函数有三个: map, filter, foldr 。 map , filter 与python中的 map, filter 函数基本一致。 map, filter, foldr 即所谓的高阶函数 High-order Function,高阶函数以函数为参数或返回结果,是FP中重要的武器。
一图胜千言~
map (*2) [1,2,3,4] 图示:

filter (>0) [-1,2,-3,4,-5] 图示:

foldr, foldl ,这两个函数都是将一个列表按一定规则生成一个值, foldr可以应用在无限列表中,而foldl更加高效一些但却不能应用到无限列表里。具体执行顺序看书吧。

Functions函数

函数是Haskell语言的核心概念,是这么语言中最重要的东西,执行一个haskell程序就是计算一个函数的值。
square x = x * x
左侧的 x 就是参数(argument, parameter) , x * x 就是 square x 的值。

条件判断

haskell 也支持标准的条件表达式 if-then-else
fact n = if n == 0 then 1 else n * fact(n-1)
有一点要注意的是 haskell中的 if-then-else 中 then 与 else 两个分支都要必须存在,这一点不难理解: 在C类语言中,如果没有 else 分支,那么不符合 if 条件的时候只是什么都不做,而在 haskell 中如果没有 else 那么当不符合 if 条件的时候,这个表达式便不知道结果值是什么,也就没办法计算。

case
case 类似于 C 中的 switch

f x = case x of
        "albert" -> 26
        "foo" -> 3
        _ -> 0

最后一行的 _ 类似于 C 中的 default 语句。这里涉及到程序布局,用过 Python 的人应该会很快适应这种通过缩进和对齐来区分语言层次结构的方式,避免写很多的 {} ; 之类的符号,程序看上去好清爽~~

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

你那两张图怎么做出来的我倒是庭感兴趣

我一般用dia,你也是吧?