Ruby 里的 block一般翻译成代码块,block 刚开始看上去有点奇怪,因为很多语言里面没有这样的东西。事实上它还不错。
First-class function and Higher-order function
First-class function 和 Higher-order function 是函数式编程语言里面的概念,听起来好像很高端的样子,其实很很简单的。
First-class functions 是指在某些语言里,函数是一等公民,可以把函数当做参数传递,
可以返回一个函数,可以把函数赋值个一个变量等等,反正就是正常值能做的事函数都能做。JavaScript 就是这样的。举个例子(下面的所有例子里,当我提到
JavaScript 时,示例代码都用的 CoffeeScript):
greet = (name) -> return -> console.log "Hello, #{name}" greetToMike = greet("Mike") greetToMike() # => 输出 "Hello, Mike" a = greetToMike a() # => 输出 "Hello, Mike"
在上面的第四行里,greet("Mike") 返回了一个函数,所以第五行里才可以调用 greetToMike()输出"Hello, Mike"。第六行把一个函数赋值给了a,所以第七行就可以调用这个函数了。
higher-order function 一般翻译成高阶函数,是指接受函数做参数或者返回函数的函数。
举个非常常用的例子(用 JavaScript):
a = [ "a", "b", "c", "d" ] a.map((x) -> x + '!') #=> ["a!", "b!", "c!", "d!"]
上面例子里 map 就接受了一个匿名函数作为参数。Array.prototype里的很多方法,比如reduce, filter,every, some 等等都是高阶函数,因为他们都接受函数作为参数。
高阶函数非常强大,表达力很强,可以避免大量重复代码。总的来说,它就是个好东西。
Block 的本质
先来看一组 Ruby 和 CoffeeScript 代码的对比。
a = [ "a", "b", "c", "d" ] a.map { |x| x + "!" } # => ["a!", "b!", "c!", "d!"] a.reduce { |acc, x| acc + x} # => "abcd" a = [ "a", "b", "c", "d" ] a.map((x) -> x + '!') # => ["a!", "b!", "c!", "d!"] a.reduce((acc, x) -> acc + x) # => "abcd"
这两组代码真的看起来超级像。我觉得这也暴露了 Ruby 的 block 的本质:高阶函数的函数参数的变体。
JavaScript 里面的map 函数接受一个函数作为参数,但是 Ruby 里的 map 却接受一个
block 作为参数。
其实 matz 早在一本书里《松本行弘的程序世界》里说了:
复制代码 代码如下: 最终来看,块到底是什么?
...
块也可以看作只是高阶函数的一种特殊形式的语法。
...
高阶函数和块的本质一样
...
在 Ruby 里,函数不是一等公民,没有 first-class functions。但是在 Ruby
里怎样使用高阶函数呢?答案就是使用 block。可以直接用 block,也可以用 lambda
或者 proc 把 block 转换成 Proc 类的实例用。
我发现在 Ruby 里使用 block 时,几乎所有的情况下都可以用 JavaScript
的高阶函数替代。
Enumerable 模块里的所有方法都是典型的例子。事实上确实存在 JavaScript 版
的 Enumerable,比如 Prototype.js 就有个 Enumerable,用起来跟 Ruby版的几乎一样的。当然它是通过高阶函数实现的。
与高阶函数有何不同
除了语法上看上去有点不同外,有非常重要的两点。
控制流操作
在 block 里面可以用 break, next 等等这些在一般的循环里才有的控制流操作,这些
在高阶函数里是用不了的。比如你可以试试在 JavaScript 里用 forEach 而不用循环
实现个take_while 函数,真是相当别扭的。比如之前 cnode 上就有人发帖问:nodejs的forEach不支持break吗"htmlcode">
name = "mike" def greet puts "hello, #{name}" end hello # => in `greet': undefined local variable or method `name' for main:Object (NameError)
但是用 block 就可以了
name = "mike" define_method(:greet) do puts "hello, #{name}" end greet # => "hello, mike"
用 JavaScript 就根本不存在问题。
name = "mike" greet = -> console.log "hello, #{name}" greet() # => "hello, mike"
同理还有class 和 module 关键字都会创建新的作用域而在里面接触不到外面的变量,
也可以用 block 解决。
还有那个 proc 和 lambda 的区别。其实我一直不理解为什么会有人不用lambda
而跑去用 proc,明显 proc 的 return 行为太不符合常识了。但是到头来却发现
block 的行为跟 proc 创建的对象的行为是一样的,比如
def hello (1..10).each { |e| return e} return "hello" end hello # => 1
这感觉真是有点悲催。
结语
说了这么多,就是因为在 Ruby 里面函数不是一等公民,又想获得函数式编程的便利。
Ruby
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]