懂一点Haskell(二)

函数式编程基础

编程有两种根本不同的方式,顺序式和函数式。顺序式最好的例子是C语言,它依赖于一个特定的模型,比如冯诺依曼模型。写C语言程序,你得懂一些计算机基础知识,得自己分配内存......你的每一行程序甚至都能找到对应的计算机指令。而函数式则侧重于从数学角度分析问题。重点关注计算,而不是电脑。相比而言,函数式编程语言的函数更像数学里的函数(接下来会讲Haskell中函数必须遵循的几点准则),而C语言的函数则没有这么严格的要求。Haskell是一门纯函数式编程语言。而更多其它语言则是在这两种方式之间寻求某种折衷。

function

我们从函数式编程中最基础的概念函数开始说起。那么,什么是函数?Haskell中的函数来源于数学中的函数概念。在数学中,当我们定义一个函数 ƒ(x)=y,意思是有一个函数 ƒ,它接受一个参数 x, 映射到值 y 。对于函数 ƒ而言,每一个 x 只能有一个特定的 y 与之对应。Haskell的函数就和数学里的函数一样!
现在我们定义一个函数addThree,它接受三个参数并返回他们的和。

-- 函数名 addThree,参数 x y z,返回 x+y+z
addThree x y z= x + y + z

如上例,定义一个Haskell函数就是这么简单!

Haskell函数必须遵循的三条守则:

  • 所有的函数必须接受至少一个参数
  • 所有的函数必须返回一个值
  • 无论何时以相同参数调用一个函数时,必须保证相同的输出。

第三条规则又叫做 引用透明性(referential transparency) 这里有必要重点说一下引用透明性,Haskell号称可以写出零bug的程序正是因为这一特性。因为你每做一次函数调用都可以保证得到期待的输出!而没有任何副作用(side effect)!
有人就要问了,难道C语言的函数就不能保证确定的输出吗?下面是实现三个数相加的C语言代码。

int addThree(int x, int y, int z){
    return x+y+z;
}

就这个代码而言,当然可以保证确定的输出。可是,在C语言中你还可以这样做。

int a = 8;
int change_a(){
    ++a;
}

上面这个C程序不仅没有参数,而且每次调用你根本不知道它干了什么!你也不知道它对谁进行了什么操作!当然,除非你看源代码。可是我们创建函数不就是为了用吗?难道每个函数都得去查看它的源代码?全局变量和静态变量的存在使程序变得复杂起来,当然,某种程度上也带来了一定的便利。在Haskell中,就没有全局变量和静态变量这一说。甚至变量都不是真正的”变量“,比如 x=2,变量一旦定义便不能更改其值了,x在这个程序中永远都会等于2。你可以把Haskell中的变量理解为“定义”,这样更贴切些。然而,Haskell允许在GHCi中进行变量更改,也算是给大家一个方便。但在.hs文件中是坚决不允许对变量进行重新赋值的!

Lambda

lambda function,又叫匿名函数。即没有名字的函数。它是函数式编程中基础的概念。以一个例子说明Haskell中匿名函数的定义方法。

-- \ 后是参数,本例x,-> 后是函数体即x的映射。
-- 和它等价的函数是 double x = x * 2
\x -> x * 2

先来使用一下这个匿名函数。打开ghci:

Prelude> (\x -> x * 2) 6
12
Prelude> double 6
12

两个函数完全相同,那么为什么不用有名字的函数呢?事实上,确实推荐使用有名字的函数。lambda一般用于只使用一次的函数,因为只使用一次,也就懒得定义函数了。

first-class function

函数作为参数

假如你有一个函数ifevenInc,如果参数n为偶数就给n加一,否则返回它本身。

ifEvenInc n = if even n
              then n + 1
              else n

这时你又想写另一个函数ifEvenDouble,如果参数n为偶数,就翻倍,否则返回它自身。

ifEvenDouble n = if even n
                 then n * 2
                 else n

这两个函数除了then后面的部分,其它部分完全相同!说不定以后你还会想写ifEvenSquare,Haskell推荐的做法是把大函数分拆成多个小函数。虽然这个函数并不大,但可以说明这个思路。我们的做法是把then后面的部分提出来写成函数。

inc n = n + 1
double n = n * 2

接下来就进入主题了,把函数作为参数!把函数作为参数,我们就可以把上面两个函数ifEvenIncifEvenDouble改为一个函数ifEven,然后把incdouble函数以参数方式传进去。

ifEven func n = if even n
                  func n
                  else n
                  

你也可以把匿名函数当参数用。打开ghci:

Prelude> ifEven (\x -> x+1) 6
7
Prelude> ifEven double 8
16
                         

函数作为返回值

以下面的例子说明函数作为返回值的用法及用途。

wuhanOffice name = name ++ ": Box 789 - wuhan, 10013"

shanghaiOffice name = name ++ " Box 456 - shanghai, 89523"

xianOffice name = name ++ " Box 123 - xian, 65535"

假如有以上三个函数,它们分别生成 name 对应不同城市的邮寄地址。
它们这样使用:

*Main> wuhanOffice "Bob"
"Bob: Box 789 - wuhan, 10013"
*Main> xianOffice "Alice"
"Alice Box 123 - xian, 65535"

这几个函数功能很简单,只是为了说明问题。现在我们知道name在哪个城市,所以可以直接调用。但如果我们事先不知道name属于哪个城市,那么该如何选择调用wuhanOffice还是xianOffice呢?我们可以再写一个函数addressLetter,把城市也作为参数和name一起传进去,像下面这样:

addressLetter name city = ...

在函数体里面进行城市的选择,然后执行相关的操作。而这些“相关操作”我们之前已经定义好了函数,因此可以再写一个函数,传入city得到对应的函数。这就是把函数作为返回值。如下:

getLocationFunction city = case city of
    "wh" -> wuhanOffice
    "sh" -> shanghaiOffice
    "dc" -> dcOffice
    _ -> (\name -> name)

然后我们的addressLetter就可以这样写了:

addressLetter name city = locationFunction name
  where locationFunction = getLocationFunction city
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容