来自地狱的杂技团----函数与函数类型

我在Structral programming and formal method 的前半部分简单的接触了Haskell的最基本的语法。 但是用haskell写出如此丑陋的代码显然不是目标!!
思考如下的愚蠢问题:

把list中的所有值加一

非常快我们可以写出如下的代码:

foo list = map (\x -> x+1) list

这段代码很烦, 但是不得不说比遍历整个list,每一次加1要短多了。 大部分接触过map这类函数的小伙伴们,在python,js中, 这种东西叫做高阶函数,也就是说他可以接受一个函数作为高阶函数的参数来进行运算。就算在c这样不支持闭包,lamba, 高阶函数的语言里,我们也可以通过函数指针的方式来实现。
在编程语言中当我们使用一个变量的时候,这个变量是一定有其类型的, 在Haskell这样类型约束强到几乎是偏执的语言中, 我们代码中的lambda 表达式或者是函数当然也是有类型的。回想一下函数的定义

addOne :: Int -> Int 

没错, 在Haskell中,类型的显示声明操作符正是::, 这里的Int ->Int 就是函数的类型。从之前说的纯函数的概念上来说,函数本质是一种对映射关系的抽象, 所以在haskell中-> 正是一种映射操作符。hhhh是不是确实是很固执的函数式编程。
好的,这是只有一个参数的函数的情况,思考一下稍微复杂一些的加法函数

 (+) :: Int -> Int -> Int

现在的函数有两个参数和一个返回值了,为啥这只有两个->呢,该怎么区分函数的输入和返回值呢?
在haskell中,或者说许多函数式语言中,函数永远只有一个参数和一个返回值
这种处理就是大名鼎鼎的函数的柯里化函数Curried functions, 如果你是一个js开发者,很可能早就已经在某个高深莫测的文章中见过了。
知道了这一概念之后,让我们重新审视一下加法函数+,没错它只有一个输入Int, 它返回了一个Int -> Int。啊!对返回值也可以是函数。

在函数式编程中函数是一等公民

干TvT,这句话真不是白说的。
柯里化以后的函数通过返回函数的方式来实现多参数的调用.现在,有了函数地位和定义方式的概念我们终于可以很自然的写出map的函数定义了。

map :: (a -> b) -> [a] -> [b]

那柯里化有什么好处呢?
现在我们的函数其实变得更加灵活了!无需再定义addOne这种莫名其妙丑陋的东西了直接+1 就可以了,函数+在这里吃进一个Int 1之后返回了一个新函数!这被称为不完全调用函数(Partially Applied Functions)Nice!可以把代码写的更短了!

foo list = map (+1) list
-- wait ! foo和map也可以不完全调用!
foo = map (+1) 

好了好了,真干净,爽爽爽!
不过话说程序员里有一个很有名的笑话:

有一个苏联特工费劲了千辛万苦,终于偷到了阿波罗登月计划代码的最后一页,代码是用lisp写的。但是当他看到内容的感受到了世界的恶意XD,因为最后一页是
))))))))))))))))))))))))))))))))))))))))))))))·········)))))
(╯‵□′)╯︵┻━┻

函数式编程一般都非常依赖与递归,以lisp为代表的这种S Expression-base的语言都会有比较多的括号。相信写过js回调的人也会有这种感觉,漫天的括号非常难受。haskell也很容易写出很多括号。比如当我们联用高阶函数mapfilter(请hoogle之,用来过滤list的高阶函数)

sum (filter (> 10) (map (*2) [2..10]))

好多括号啊!haskell这种杂技语言显然不能接受这样的结果!可以用函数f $ x = f x来实现

sum $ filter (> 10) $ map (*2) [2..10]

效果拔群,函数从左结合变成右结合了,自然就用不到括号了,强迫症爽了嘛?

问题复杂一点

把list中的所有值加一再乘2

作为函数式语言,这种级别的杂技显然不是终点。之前说到了这里的函数和数学里的函数是一样的,所以当两个函数组合的时候有 (f.g)(x) = f(g(x)) ,哇,真是并没有什么卵用的功能啊,我们的函数又能写的更炫酷啦(毫无表情)!

map (\x -> (x+1)*2) [2..10]  
消灭lambda表达式!!
map ((*2) . (+1)) [2..10]  

ok,杂技表演完毕。等下我之前犯了一个错误, 谁说加法只能用来加Int的!所有数应该都可以被加法应用吧!

(+) :: (Num a) => a -> a -> a 

像Num这样的约束在haskell中被称为typeclass, 它代表了一种类型可以被某一簇的函数所应用,而可以被应用的函数就定义在typeclass的定义中,这很有用,因为已经有人帮我们写好了很多的轮子,我们只需要让自己的类符合这些typeclass的规范,就可以得到很多可以用的函数啦!比如我希望之前写的child类可以被打印出来,可以被比较,我只需要

data Child = AA | BB | CC | DD deriving (Show, Eq)

easy, 不过这不是今天的重点,后面在介绍更多fancy的东西的时候你将感受到haskell神奇的类型系统。


坑,在搞了这么多杂技之后让我们来康康haskell的函数本身到底有啥不一样的地方吧。
其实确实,由于在函数式编程中,函数的纯度一旦被保证,有很多语言实现上的方式就变得完全不一样了。

  1. 引用透明Referential transparency 纯函数带来的状态不可变,让我们的编程更加透明了,而且函数的正确性就可以通过类似单元测试的的quickcheck方式来实现了。嗯,对于测试来说是真的友好。
  2. 天生无锁并发 并发编程和并行编程在现在的业务场景下面确实是越来越多了, 函数式因为函数没有副作用可以毫无顾虑的往多核上扔,但是这确实对于程序员本身有了更加高的要求,生产环境上毕竟哪来的这么多简简单单就能用纯函数来表示的场景呢,而且并发的时候说白了很多时候都是为了处理I/O,又要引入一些比较难理解的工具了。但是在spark, GPU等这些并行应用场景下确实好用。
  3. 惰性求值lazy evaluation 因为haskell之类的函数式编程是不可变状态的变量,很多时候都是需要重新返回一个新的值而不是去修改那个值,这时候如果是earger的策略显然是不合适的,特别是函数那么多,考虑到栈深度等很多原因,所以在haskell中函数是默认lazy的,只有真实的call了那个值,才会去计算这个值。说白了这里的等号操作符本质就是个绑定,根本没有计算上的特性,根本没有显示的赋值操作。从表达能力上来说其实也有一些考虑,比如可以很简单的表达一个无穷列表([1..])
  4. 尾递归优化tail recursion optimition 因为递归那么多为了不stack over flow这个显然是必须的。这个在后面应该会单独开文章讲,还有tail call的优化问题。

这只是一些, 这些变化的影响其实导致了编程思路的彻底不一样,原来的算法和数据结构在函数式的情况下都必须做一些不小的改动,还有非常重要的CPS,这感觉也是我这过去半年最大的收获吧,在结束了这两篇对彻底初心者教学的尝试之后也希望自己能真正把后面的内容写好。

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

推荐阅读更多精彩内容

  • 我想这个问题大概很多人讨论过,也见到过,根据自己亲眼目睹和亲身经历说说我的感想。 一次是我在回家的...
    levi韦阅读 120评论 1 1
  • 新的一天都是从无聊开始的… 登录QQ、微信。 张阿姨的微信信息狂轰乱炸而来。 …………张阿姨的审美一直很迷,毕...
    土狗土阅读 306评论 0 1
  • 此刻的我面临绝境,前有悬崖后有虎狼,不禁泪流满面,大喝一声:我的命好苦呀! 正准备纵身一跃跳下悬崖时,看见远方...
    神经质的摩羯座阅读 344评论 0 0
  • (一) 初遇 我是一只游荡在阳间的鬼,没有记忆,不知自己从哪来,要到哪去。一直在洛阳城里打着转,不能出城...
    溶解阅读 220评论 0 0