瞎扯淡 对 ruby 了解的太深入并不是好事

gaicitadie · 2014年02月04日 · 最后由 pepsin 回复于 2014年02月07日 · 4018 次阅读

以前用 block 的时候非常爽 users.select { |user| user.id > 33 } users.sort_by { |user| user.post_count }

虽然不知道其背后的原理,但知道怎么用,并且对这些语法有一见如故相见恨晚的感觉。忍不住手痒痒看了 ruby 元编程,才知道原来这叫 block,并且知道了它本质上也是个参数,并且知道了它在函数内部的实现原理,给我带来了两个坏处: 1、原来这么漂亮的语法只是个障眼法,自从知道它本质上是个参数以后就觉得不好玩了 2、实现的原理有些绕,每次使用 block 的时候忍不住脑补它在函数内部是怎么 yield 的,累!

有一个故事不知道真假,一个男妇科医生,每天面对女人的下体,对女人的下体甚至比女人自己还了解,后来他阳痿了。出于职业习惯,每次他 XX 的时候忍不住去思考女人的生殖系统,构造、神经、内分泌、高潮原理,思考到头疼

听说有的人学会看见一句话瞬间知道多少字以后就再也没法好好读书了...

知识越多越反动?只安心做拖拽工程师,然后加班挣点钱买 ide,这也是天朝的技术只能追着美国佬和日本鬼子屁股后面跑的原因吧

人可以选择不去考虑些什么,但这样做很难。。。

楼主又来散布歪理了。 👎

#5 楼 @Rei 但凡像这样的理论,只要是发到这个节点的,我们应该鼓励。

貌似 1.8.5 之后多了很多 xx_by 对方法,据说能提高效率。。。

我告你啊 LZ,无论什么语言其实最终 CPU 跑的都是机器码(好了,LZ 这下不会编程了

咋我读完了之后就惊叹 Ruby 的 block 实现竟然如此高效,像 lamdba 却远远没有 lambda 那样费内存,以后一定要多多用。

#9 楼 @iBachue 幸好还没读到 lambda,不再往下读了,不然对 lambda 的感觉也会变

大多数人都没熟悉 Ruby 到需要在意这个问题对吧...

晕,还有这种理论。那写编译器的同学不是完蛋了?呵呵

了解实现原理是基础吧。一行代码后面发生了什么不清楚怎么能够放心。靠谱的程序员应该看到语法就应该猜测到有什么方式实现,然后去差查一下验证自己的猜测对不对,然后放掉就可以了。

话说回来“对 ruby 了解的太深入并不是好事”另外一个意思:重心不应该放在 ruby 语言本身上面,而是用 ruby 来做自己想要的东西,这个才是难的地方。

#10 楼 @gaicitadie 如果你因此感觉就发生变化了 那我劝你还是继续念下去吧。。现实本来就是残酷的 233

#13 楼 @linjunhalida 就是 咱们应该做到看见 Ruby 代码就想到编译之后的 YARV 指令,一条条是怎么被虚拟机逐句执行的。。

#15 楼 @iBachue 恩,然后再忘掉它。因为对于上层应用来说,这些底层细节不重要。出了问题再去想起来。

《七周七語言》裏訪談了 Matz,Matz 被問到最喜歡 Ruby 什麼的時候,就說最喜歡 block。既然這裏是瞎扯淡,我就把當時的讀書筆記抄一下:


Matz: 我喜歡它寓編程於樂的方式。說到某個具體的技術點,我最喜歡的是“代碼塊”(block)。代碼塊即是一種易於控制的高階函數,也爲 DSL 及其他特性的實現提供了極大的靈活性。(p. 9) 這裏 Matz 說謊了。block 很高階函數,但不是函數。

->(x){x+3}.call 3 

返回 6. 而

{|x| x + 3}.call 3 

將導致syntax error. Ruby 的祖先 Smalltalk 中,block倒是可以接受消息的:

[:x | x + 3] value: 3 

返回 6. 所以 Ruby 的 block 是個奇怪的東西。縱向來說,它很像 Smalltalk 的 block,橫向來說,它很像匿名函數。但是事實上,它和兩者都不一樣。Ruby 的口號是「Principle of Least Surprise」,但是這個 block 卻讓我吃驚。 語意上塊讓人迷惑,語法上也不好。塊有兩種表達法:大括號或者 do...end,問題在於優先級是不同的。例如f x {|x| puts x}f x do |x| puts x end是不一樣的,前者等於f(x {...}),後者等於f(x) do...end。初學的時候很容易搞混。 Matz 在他寫的《まつもとゆきひろコードの世界 : スーパー・プログラマになる 14 の思考法》一書中提到了設計塊的緣由:

  1. 減少對象的生成數,因爲早期 Ruby 生成閉包對象的代價很高。
  2. 外觀上看起來像控制結構。 Matz 還提到,傾向於使用高階函數的 OCaml 的 2239 個庫函數,沒用函數參數的佔 87.2%,用一個函數參數的佔 12.1%,也就是有兩個以上的不到 1%。因此,大多數情況下,只能有一個參數的塊也夠用了。 所以說,塊就是一個語法糖,讓習慣過程式編程的程序員可以使用類似高階函數的東西(同時讓 Smalltalk 和 Lisp 的來客大吃一驚)。

原来这么漂亮的语法只是个障眼法,自从知道它本质上是个参数以后就觉得不好玩了

这个逻辑无法理解。知道本质后,应该惊叹于 Ruby 的 一致性, 统一 才对。其实 Ruby 的很多魔法就是没有魔法。

实现的原理有些绕,每次使用 block 的时候忍不住脑补它在函数内部是怎么 yield 的,累!

那是因为你还没有洗脑成功,等你洗脑成功了,就不会去想了。

至于楼主最后给的例子,不具有类比性,因为 Ruby 本来就很 简单, 可能你从 Python 的角度,来看 Ruby 有点过于 魔幻化 了。那个医生错了,错在他不该了解那么多... 而你对了,因此这才算开始正确的认识 Ruby 了。

#17 楼 @weakish

大多數情況下,只能有一個參數的塊也夠用了。

你这话容易让人引起歧义。我想你的意思是: 一个函数,仅仅存在一个关联的块,就够用了。

@zw963 這一句是 Matz 書裏的,我自己的從下一段纔開始。只能有一個參數,結合上下文應該不會誤解吧。例如樓主的例子, { |user| user.id > 33 },塊只能收user一個參數,不能另外傳參數給它。

楼主已经是 Ruby 大神了。我还没法体会到“太深入”是有多么不靠谱,继续愤斗中。

#21 楼 @ruby_sky 不带这么埋汰人的

#22 楼 @gaicitadie 确实,春节认真看了很多开源项目,发现 Ruby 博大精深,魔法好多,确实好多没有学会。

#20 楼 @weakish

例如樓主的例子, { |user| user.id > 33 },塊只能收 user 一個參數,不能另外傳參數給它。

好吧,如果你这个 例如 是在解释下面的话,

Matz還提到,傾向於使用高階函數的OCaml的2239個庫函數,沒用函數參數的佔87.2%,用一個函數參數的佔12.1%,也就是有兩個以上的不到1%。因此,大多數情況下,只能有一個參數的塊也夠用了。 

我可以很肯定,你完全错会松本的意思了。这个所谓的 参数, 指的是:楼主所谓的那个 本质上是个参数, 也就是说; { |user| user.id > 33 } 这是一个参数。而所谓的两个参数,如果用 Ruby 写,也许是这个样子:

meth {|x| x > 33 } {|y| y > 44 }

很明显,这是有点丑陋的。同样的需求,在 Ruby 下当然也可以实现,是这样做:

proc = proc {|x| x > 33 }

meth(proc) {|y| y > 44 }

而这样的需求,在 Rails 里面,还有很多 gem 里面,也是有的。只不过,这就是松本所说的 不到1%.

果然是瞎扯淡

#24 楼 @zw963 感谢指出。我原先的理解是错误的,Matz 的意思是不能绑两个或两个以上的块。

users.select { |user| user.id > 33 }中,Block 设计的本质是代表了了什么?这里的 select 虽然只是一个普通的方法,但它的目的却与普通意义上的 OOP 里的方法不同(普通的 OOP 不会这么设计,因为 OOP 里方法调用通常代表了向对象发送消息,改变或查询对象的状态),但它的语义实际上类似于 for 循环这样的语言基本语义,可以被视为一种自定义的基本语义。从一定程度上跳出了普通方法的框框,向问题域迈进了一步。

为什么要这么使用 Block 而违反普通的 OOP 做法。它有两个好处: 1,第一个好处很明显,可读性大大增强。 2,第二个原因是:普通的 API 设计方法存在一种天然的陷阱,那就是不管怎样封装,大过程虽然比小过程抽象层次更高,但本质上还是过程,受到过程语义的制约。也就是说,通过基本元素/语义构造更高级抽象元素/语义的时候,语言的构造规则很大程度上限制了抽象的维度。

但 Block 的抽象维度既不同于过程式语言的过程抽象,也不同于 OOP 的数据抽象,而是《SICP》中所说的“元语言抽象”。不同于传统的 API 设计,它根据问题域选取适当的抽象维度,利用语言的基本语法构造领域特定的语义和语法。

这也是为什么 Ruby 的语法这么强大的本质原因之一。理解 Block 这么设计和使用的深层原因,理解元语言抽象对 Ruby 和 Rails 的重要意义,岂不对 Rails 编程本身会有更深的体会吗?

屁股后面站一个一天提出 10 个需求更改的产品经理能治好楼主的 bug

需要 登录 后方可回复, 如果你还没有账号请 注册新账号