免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: send_linux
打印 上一主题 下一主题

《ruby元编程》有奖试读中!(获奖名单已公布) [复制链接]

论坛徽章:
0
21 [报告]
发表于 2012-02-27 17:30 |只看该作者
本帖最后由 琳琳的小狗 于 2012-02-27 17:41 编辑
OwnWaterloo 发表于 2012-02-24 11:36
整个章节可以吐槽的地方很多,这里只说一点:这功能又不是什么稀奇的事情,连许多被认为是静态语言并使 ...


作者给出的这个例子,只是为了向读者揭示Ruby的”open class“特性,因为在ruby社区里这种用法实在是太常见了,以至于被很多人滥用,要不怎么叫”Monkey patch“呢:)

时至今日,Rubyist们也逐渐了解到了super男说的那句话”能力越大责任也就越大“,在打猴子补丁的时候都知道要多长个心眼,所以在元编程这本书里面有一整章讲述怎么预防各种陷阱(怎样安全地元编程)。另外,在Ruby的下一个版本也就是2.0中,也倾听了社区的诉求,加入了”选择子“来最大限度的防止猴子补丁带来的问题(也就是你所说的第二点使用using all):
module MonkeyPatch
  refine Integer do
    def +(v) #嘿嘿,ruby也有“重载”哦,所有的操作符只不过是普通的方法加上语法糖而已,正所谓一切皆对象……
      self - v
    end
  end
end

class MonkeyIsland
  using MokeyPath #声明使用MokeyPath之后,在此词法作用于中,猴子补丁都有效

  puts "1 + 1 = #{1 + 1}"  #输出 1 + 1 = 0
end

puts "1 + 1 = #{1 + 1}"  #输出 1 + 1 = 2,因为修补被限制而不会影响标准库的行为

利用选择子,可以不用再担心污染现存对象的行为,而把猴子补丁限制在可控的范围类。

至于全局变量,自古至今都不被认为是最佳实践,这个没有什么好争论的。在Ruby社区中,如何最大限度的消除此依赖,一直都是重构的话题之一。拿Erlang的话来说,连可变“状态”都是罪无可恕的东西,更别说这玩意儿了(并行计算中的状态同步,简直是噩梦)。

另外针对“面向对象”相关叙述,个人决定不予任何置评,保留沉默的权利。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
22 [报告]
发表于 2012-02-27 18:04 |只看该作者
回复 20# 琳琳的小狗

终于来了个能打的了……  激动死我了……

琳琳的小狗 发表于 2012-02-27 16:55
这一节作者只是站在Ruby的角度上来看元编程,因为ruby是解释执行的,其元编程实现手段都是通过运行时修改对象模型来实现,因此他一开始就在这个上下文中,给出跟明确的定义(元编程是编写在运行时操纵语言构件的代码)是应该的,这个结论在后续章节中有所呼应。总不至于说,在一本讲Ruby技术内幕的书中,去讲C++或者其他语言的实现细节吧?

其实就我阅读这本书的过程来看,并没有感受到作者对其他语言,特别是静态语言的贬伐之意。作为一名实在的程序员,对某种语言有特别的嗜好完全可以理解,这就好比你言语之中透露的对C++的喜好。对于Lisp,书中一上来就讲了,Ruby是借鉴了Lisp以及Smalltalk的语法优势,属于后来之辈,怎么好意思说前辈的坏话?只不过对于同一事物的处理方式不同而已了,抬高点说,仅仅是世界观有所区别,正所谓“求同存异”嘛。

我是见过大量的书/文章/博客等等,总喜欢用贬低(也许是无意的)X的方式来抬高Y。
而且确实又存在大量的人学不会全面的分析,真将某种权衡当作了优势。

比如看到javaer说C++不支持interface,不是纯OO,不是……什么的,有闲情就去调戏两把,没逸致就笑笑了之……
我担心的是书中这种说法,会不会又导致大量ruby程序员说C++不支持元编程。

C++不支持元编程这方面是我的错,我最初是在is-programmer上的某个博客看到这书的,那文章只引用了书中的一部分。
书里有一个灰框里面有提到 http://book.douban.com/reading/15986307/  《代码生成器与编译器》。
不过这部分依然有一些问题,这是ruby(或者说这本书)对元编程的定义。
至少在试读部分里介绍的那些技术,只能算是dynamic programming(不是动态规划,也不是动态类型dynamic typed),还没达到code transformation的程度。
不同社区对相同事物有不同的term,同一term描述不同事物其实也很正常,所以我也不打算继续说了……

关于lisp。用贬低C++的方式抬高java至少在两者都是静态类型方面还说得过去。
而用C++来对比ruby,说C++编译后没有类型信息…… 这不是明显的么,否则还叫什么静态类型语言?
所以,要比较元编程,应该与lisp比较。

最后,我以前对C++有喜好,现在没什么特殊感觉了。



琳琳的小狗 发表于 2012-02-27 16:55
1、别说的Ruby多流行似的好吗?

恩,Ruby在国内是非常不受待见,这点从相关的书籍出版物惨淡的销量上就能看出(希望这本元编程能带来点涟漪)。但是,不流行就代表很差劲么?反之,流行就代表高雅、牛逼?恐怕不见得。37things就是那么一个名不经传的小公司,使用如此垃圾的ruby语言开发了rails这么个另类的敏捷开发框架,不也在小众社会活的有滋有味么?如果Ruby真的这么垃圾,我想Uncle bob、Martin叔叔这些老家伙也不会亲言流露对其赞赏有加;另外Ruby社区的活力,恐怕也是有目共睹的,惯例优于配置、BDD实用化……无穷的创造力(可以参见rspec以及cucumber):
http://rspec.info/
http://cukes.info/

如原帖
>> 如果TIOBE具有参考性的话,Ruby也就10x左右徘徊而已,与Lisp是一个水平的好吗?
不仅仅是国内而已。

另外,流行与优秀之间没有直接关系我是赞同的。
这里仅仅是就事论事的说书里提到ruby的流行只是相对的。并没有说因此ruby就不好,也没有说因此ruby就好。



琳琳的小狗 发表于 2012-02-27 16:55
2. 说最友好不害臊吗?

说对程序员“友好”,这个标准是需要实践的,如果没有对ruby语言的实际使用感受,妄谈友好还是憎恶都不妥当,何不尝试一下呢?鄙人作为一名专职Ruby程序员,使用Ruby以及Rails已经有六年之久,深感这个社区的人文风格(如第一条的两个工具,给团队的开发人员实施BDD、ATDD带来了无穷的愉悦),我只想说,自从将项目从Java迁移到Ruby平台之后,加班少了,熬夜少了,一人顶三人,身体倍儿棒……

看吧,与java比了。java有什么好比的? 虽然我对C++无爱了,但我对java一直都是鄙视的。
而且与上面一条相同,说ruby好(暂且不论它是否真的好)并不能反叛ruby不流行,说ruby比java友好也不能证明ruby就是最好的。只能说明它比java好而已,这里我也承认。

能与lisp比较一下么?


琳琳的小狗 发表于 2012-02-27 16:55
3. 只有一个世界并不是优势,能将多个世界融合、一致化才牛b

Ruby并没有排斥多元世界,相反,元编程在Ruby身上反而是通过一种简单的、高度一致的基础原则,也即”对象模型“来实现的,在Ruby中,真正的做到了”一切皆对象“,不相信?且看《Ruby元编程》的精彩剖析:)

排斥了。 排斥了编译这个事情。 lisp/lua/python/js都有编译,就只有ruby是单趟直接解释。
这里不需要与lisp比了,lisp区分了几种time,但又能将其融合。

一切皆对象什么的,正好是我对ruby的资料看不进去的原因之一。
lisp一切都是form,haskell一切都是function,但这两社区的人很理智,不会在无关紧要的地方提到这种哲学上的考虑。
比如haskell就不会说它支持参数多态是因为haskell里一切都是函数的原因。 不将一切都处理为函数同样可以支持前者, 只是haskelll恰好将一切按照函数对待。
同样, 不将一切视为对象同样可以支持(甚至是一致性地)许多优美的表达, 面向对象并不是这些优美表达的原因, 只是ruby恰好将一切设计为对象而已。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
23 [报告]
发表于 2012-02-27 18:58 |只看该作者
回复 21# 琳琳的小狗

open class后除了修改class的定义,还有其他用法么?

修改class的定义,或者修改某个object的定义:
1. 并不只有ruby才可以, 在lisp/lua/python/js里面都可以做到
2. 在上面提到的其他语言里,没有哪一门将这个叫做metaprogramming。
它们,包括ruby,都是dynamic language, 运行时修改class/object是天经地义的,不能修改才叫奇怪了。
3. 即使是在C/C++里, 有意识地将class设计为可以让用户修改也是很容易的事。
难的,但也可以做到的,同时也是不推荐的,是修改那些没有被设计为打算被修改的东西。

我好奇的是ruby能不能做到code transformation。
例如,scheme,写作let:

  1. (let ((x 12) (y 26)) (+ x y))
复制代码
但实际上代码是被转换为一个接受2参数的匿名函数,并以12,26作为参数立即调用

  1. ( (lambda (x y) (+ x y)) 12 26 )
复制代码
也就是前面引用豆瓣试读里面那个灰框里的技术。

为什么lua/python/js不将修改class/prototype/object的技术称作元编程?
也许是因为它们都知道自己不能很好的完成code transformation,所以不敢称自己能元编程。

所以,我好奇的其实是ruby是真正支持lisp里面那种元编程? 只是我还没看到对应的章节?
还是ruby其实做不到, 它只是将元编程的定义给改了。



关于全局量,我也认为它不应该被滥用。但我表达我支持使用全局变量的意思?
全局变量不好的地方只是因为它可变的。
如果是全局常数?全局函数?全局class?全局module? 它们通常被认为是不变的,所以它们出现在全局里其实是很常见的。
而且你也应该清楚这些被认为是不变的东西被不加克制的打补丁不是什么好事。



关于面向对象,我有一个(或者说两个)问题。
ruby是否是函数式语言?
如果是, 那么我用过的语言里面只有common lisp是将oo与functional结合好了的, 只不过common lisp的oo又不被其他oo社区接受就是了。
比如, ruby有map吧? 对集合每一个元素应用一个操作, 将操作的结果作为新的集合?

  1. ; scheme
  2. (map (lambda (x) (+ 1 x)) '(0 1 2))
  3. ; => (1 2 3)

  4. # ruby
  5. [0,1,2].map{|x| x+1 }
  6. # => [1,2,3]
复制代码
ruby的代码是这样么?
如果要求平方根呢? (map sqrt '(0 1 4)) => (0 1 2)


那么,ruby里如何对2个集合执行一个二元操作?
比如加法: (map + '(0 1 2) '(2 1 0)) => (2 2 2)
比如向容器添加元素: (map cons '(0 1 2) '( (a b) (c d) (e f) )) => ((0 a b) (1 c d) (2 e f))
有3个容器(a b) (c d) (e f), 分别添加 —— cons —— 0 1 2, 产生3个新的容器(0 a b) (1 c d) (2 e f)
添加在前在后都无所谓, 因为scheme里的list是单链表, 最方便的是在头添加。

进一步地,如何对n个集合执行n元操作?

论坛徽章:
0
24 [报告]
发表于 2012-02-27 23:38 |只看该作者
本帖最后由 琳琳的小狗 于 2012-02-28 01:04 编辑

首先声明,我不是“能打的”,更不是来打架的。只不过是以一个Ruby程序员的角度,来反驳某些言论,为《Ruby元编程》这本书正名。以下针对上面的疑问,试着逐一解答:

修改class的定义,或者修改某个object的定义:
1. 并不只有ruby才可以, 在lisp/lua/python/js里面都可以做到
2. 在上面提到的其他语言里,没有哪一门将这个叫做metaprogramming。
它们,包括ruby,都是dynamic language, 运行时修改class/object是天经地义的,不能修改才叫奇怪了。


恩,确实如此,如果不能在程序执行过程中随心所欲的修改类定义(这只是对象模型中的一个元素而已)的话,根本不能称之为“动态语言”,这也就是为啥C++他们不能算是一族的根本原因。但是,相比ruby,其他几种动态语言,在语言设计层面,并没有竭尽全力为程序员考虑,比方说,javascript中虽然具备原型链可以耍点小花招,但却不具备method_missing(对象在接收到某个不认识的消息时,会被触发)特性,我不能在一个地方抽象出未知方法调用并做下一步打算,真是遗憾。了解Rails框架的人都知道,这正是ActiveRecord的实现基石(《Ruby元编程》这本书的第二部分有详细、深入的讲解)。例如我数据库中有张users表,其对应的model类定义可以简单到这样:
  1. class User < ActiveRecord
  2.   #类主体可以不写任何代码
  3. end
复制代码
Rails利用元编程,实现了ORM的0配置,你可以这样来使用(假设users表中存在name、sex、age字段):
  1. 
  2. #动态查询方法,根据姓名和年龄查找数据库中对应的记录
  3. u = User.find_by_name_and_age 'metaprogramming', 28
  4. #获取、修改name字段的值
  5. u.name
  6. u.name = 'ooxx'
复制代码
这所有的一切,都源于ActiveRecord在第一次调用某个尚未定义的方法时,通过method_missing机制捕获了它,并利用相关信息查询、修改了数据库中对应的记录,也即书中所说的“幽灵方法”。ActiveRecord的实现远比这个精巧,为了兼顾效率,并不是每次都执行完整的方法查找过程,重复调用的时候,这个方法已经是真正存在于模型类中了。请问其他语言如何实现此功能?同样,实现模型关联,一句类宏就搞定:
  1. class User
  2.   has_many :skills
  3. end

  4. class Skill
  5.   belongs_to :user
  6. end
复制代码
注意到上面类定义中的语句没?其结果是,分别在两个类中定义了两个关联方法(User#skills以及Skill#user)其实,这只是两个类方法调用而已,通过省略方法调用的括号,让其看起来更像是一句声明,所以《元编程》这本书把这种手段叫做“类宏”。

总结一下,Ruby对元编程的定义其实可以这么理解:通过运行时修改对象模型,最大化程序员对语言构件的控制力。如果非要说他不是元编程,那绝对是说得通的,套用本书第一部分的结束语:“根本就没有什么元编程,从来都只是编程而已”——这充分说明了,在Ruby中,你是很难严格界定某段代码是不是元编程,因为构成这些代码的元素,都是纯纯粹粹的ruby语法而已。

3. 即使是在C/C++里, 有意识地将class设计为可以让用户修改也是很容易的事。
难的,但也可以做到的,同时也是不推荐的,是修改那些没有被设计为打算被修改的东西。


在Ruby中,不需程序员下意识的去设计就获得了这种能力,你用不用哪倒是另一回事情。很多情况下,具备灵活的修改、打补丁的特性可以创造出更多有意思的玩意儿,比如单元测试中常见的Mock框架,我拿Rspec来举例,其自带的mock框架可以让你自由的生成mock、stub对象,甚至是stub已有对象的行为,这让程序员编写的测试代码更加健壮灵活(具体原因请参考TDD的相关书籍):

m = mock(:user)
m.should_receive(:sex).and_return(:unknow)

String.stub!(:class).and_return(:OOXX)

具体请参见:http://rubydoc.info/gems/rspec-mocks/frames

我好奇的是ruby能不能做到code transformation。


Ruby吸收了Lisp以及Smalltalk的特性,骨子里自然是具备FP特性的。在Ruby中,对FP的支持主要依靠可调用对象家族来实现,其中最为常见的是代码块(block)以及lambda表达式,代码块可以对应到Lisp中的闭包,在元编程这本书里面,对其进行了详细介绍,包括由它引申而来的技巧——“扁平作用域”。模仿你那个例子:

  1. lambda {|x, y| x + y}.call(12, 26) #一个匿名、立刻执行的lambda对象,返回38
  2. -> (x, y) {x + y}.call(12, 26) #同上,返回38,但使用了ruby1.9新增的“诡异”语法,哈哈
  3. [0,1,4].map {|i| Math.sqrt i} #返回[0, 1, 2]
  4. [0,1,2].zip([2,1,0]).map {|i| i.reduce(&:+)} #返回[2,2,2]
  5. [0, 1, 2].zip([[:a, :b], [:c, :d], [:e, :f]]).map(&:flatten)  #返回[[0, :a, :b], [1, :c, :d], [2, :e, :f]]
复制代码
这里就不再列举了,实在是没意思……Ruby元编程的最大用途,就是用来实现领域特定语言,也就是DSL,而且在Ruby社区比较流行的是“内部DSL”(由合法的ruby语法元素构成)而非“外部DSL”,Rails属于DSL,Rspec同样也是:
  1. describe "语言之争" do
  2.   it "毫无意义"
  3.   it "不需要钻牛角尖"
  4.   it "各自具有擅长的领域"
  5. end
复制代码
运行测试,结果如下(按照TDD的原则,下一步迫切需要要做的,就是实现这些Pending状态的example,所以请停止毫无意义的争论吧):


同理,要实现一个Lisp解释器并非什么难事,已经很多现成的代码了,比如这些个,都是纯ruby实现,git上有源码可供下载观摩,这也是Ruby元编程的绝佳展现(题外话,我本人也是FP的爱好者,不过对Lisp而言,仅了解Scheme方言,只看过过两本书,《How to desgin program》以及《SICP》):
http://thingsaaronmade.com/blog/introducing-flea.html
Flea is a tiny Lisp interpreter implemented in Ruby. Flea is designed to be an example of how simple it can be to bootstrap the core of a small flexible language. Flea essentially defines an informal subset of Scheme, just enough to be fun and interesting.
  1. (define number (+ (rand 9) 1))

  2. (display "\n\nI'm thinking of a number between 1 and 10,\n")
  3. (display "try to guess it!\n\n")

  4. (define user-guess
  5.    (lambda ()
  6.      (display "Take a guess - ")
  7.      (define guess (string-to-num (gets)))
  8.      (if (equal? guess number)
  9.        (display "Good guess!\n")
  10.        (begin
  11.          (if (greater-than? guess number)
  12.            (display "Lower!\n")
  13.            (display "Higher!\n"))
  14.          (user-guess)))))

  15. (user-guess)
复制代码
感觉怎么样?嘿嘿……下面这个是国人作品哦:

http://code.google.com/p/orzlisp/
a lisp interpreter implmention on ruby by ssword , just for study & fun


再来一个:
https://github.com/tmountain/Liby
Liby is a simple Lisp interpreter written in Ruby. It's heavily derived from
slisp which was written by Sandro Sigala. Liby implements a good number of
constructs seen in typical Lisp systems and may be useful for learning the
basis of the Lisp language.


所以,Ruby对FP的支持程度,也就不言自明了,再加上完善的面向对象实现,绝对称得上是Lisp与SmallTalk的结合。广告时间:关于领域特定语言(说实话,我不太喜欢书里面的翻译“领域专属语言”),在《Ruby元编程》一书中对这个主题也有详细介绍,想自己实现DSL的朋友建议认真看看。


最后唠叨几句,我之前的帖子,就语言本身,并没有将Ruby与Java语言本身做任何优劣对比,我只不过是对两者的开发效率有所感触。话说回来,Java作为主流编程语言发展至今,其社区积累了丰富的理论学说及最佳实践,我个人也是通过Java主题的相关书籍来学习那些好东西的,比如设计模式(说起这个,如果OwnWaterloo看了Ruby的另一本叫《Design Patterns in Ruby》的书,估计会吐血的吧,里面有句话是这样说的:“设计模式体现了编程语言的特性匮乏”,在ruby中,很多所谓的模式,都是多此一举)、重构、TDD等,这些都是无关语言、无关平台的通用思想和有价值的知识。对于C++,我没有学习过,也没有任何的概念,并不了解相关特性和具体的细节,故不做任何评价,因为对自己不了解的事物做出任何评判,恐怕都会有失偏颇。倒是蘑菇叔叔的这篇评论比较实在,来看看这位非Ruby程序员是如何感受这本书的:
《Ruby 元编程》——十年来读到的最好的编程技术书籍

不扯远了,希望这些争论不要影响到大家试读本书,Rubyist们,赶紧加入进来呀,所有关于Ruby元编程的一切,都可以贴出来讨论。对了,最后卖下关子,后续鄙人会放出专题screencasts,针对书中的一些知识点进行探讨,并加入部分实战内容,敬请关注:)

论坛徽章:
7
戌狗
日期:2013-12-15 20:43:38技术图书徽章
日期:2014-03-05 01:33:12技术图书徽章
日期:2014-03-15 20:31:17未羊
日期:2014-03-25 23:48:20丑牛
日期:2014-04-07 22:37:44巳蛇
日期:2014-04-11 21:58:0915-16赛季CBA联赛之青岛
日期:2016-03-17 20:36:13
25 [报告]
发表于 2012-02-27 23:59 |只看该作者
试读中!

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
26 [报告]
发表于 2012-02-28 02:11 |只看该作者
本帖最后由 OwnWaterloo 于 2012-02-28 02:13 编辑

回复 24# 琳琳的小狗

先说我最关心的,OO与functional的混合。很多语言都没做好这东西。
许多OO语言是将data(instance)与function(method)绑在了一起。

你的答案其实已经将我的问题了,我的问题是在n个集合上应用n元函数。

  1. [[0,1,2].zip([2,1,0]).map {|i| i.reduce(&:+)} # [2,2,2]
复制代码
map不是一个独立的函数,而是Array的一个instance method,于是只能先将 [0,1,2], [2,1,0] zip 为一个Array, 然后在此Array上map。
注意此处zip依然只是一个instance method, 比较: (zip '(0 1 2) '(2 1 0)) => ( (0 2) (1 1) (2 0) )。
之后显然也不是直接在 0 2, 1 1, 2 0 上使用二元函数, 而是再将先前zip得到的 (0 2) (1 1) (2 0) reduce 到一个值。

  1. [0, 1, 2].zip([[:a, :b], [:c, :d], [:e, :f]]).map(&:flatten)  #[[0, :a, :b], [1, :c, :d], [2, :e, :f]]
复制代码
依然是先zip得到 [0,[:a,:b]], 然后在这个集合上flatten。而不是将0直接加入[:a,:b]。
而且被修改的问题不仅如此, 还包括flatten。 我本意是想问push。

对比:

  1. (map atan '(0.0 -0.0) '(1 1)) ; => (0.0 -0.0)
  2. [0.0,-0.0].zip([1,1]).map(&Math.method(:atan2)) # => [0.0, -0.0]
复制代码
后面那行ruby代码我都不知道该怎么解释……
但至少暴露出了一个问题,Ruby没有first class function,必须用一个Method作为包装,然后call。这问题暂且放下。

functional与OO的一个矛盾的地方是,因为OO将method与object绑定,所以有些函数会少一个元
atan 数学意义上是一个2元函数 —— 有两个集合 (a b) (x y), 通过map得到新集合 (list (atan a x) (atan b y))。
cons 数学意义上也是2元函数 —— 有两个集合 (a b) (xs ys), 通过map得到新集合 (list (cons a xs) (cons b ys))。

而许多OO语言, 比如ruby:
Math的类方法atan2 本意是一个2元函数, 调用时也需要传入2个参数。
如果类方法其实也是class object(我不知道ruby的术语怎么说), 也是会传入一个隐式参数的话,其实那个隐式参数没有被使用, 依然是2元函数。
Array的实例方法 push ,如果有两个集合 [ ["x0","x1"] , ["y0","y1"] ] 与 [0,1], 想构造一个新的集合 [ ["x0","x1",0] , ["y0","y1",1] ], 就会遇到问题。
[ F(["x0","x1"], 0) , F(["y0","y1"],1) ] —— 怎么表示那个F?
(这里修改了一下, 使用: x1 且中间没有空格会被认为是表情……)

如果不使用high order function,不将函数传入传出(函数传出在ruby里面依然有问题,稍后再说), object.method(arg)只是一个语法问题。
但如果想要传入一个F, 它可以 F(object,arg), 各种语言就会想出各种方法, C++里是mem_fun, python里面叫unbounded-method, js里面叫indirection call —— 于是将语言弄得很复杂。
而其目的只有一个 —— 将那个隐式的self/this,重新显式化。 那何不一开始就显式化?
lua就是这样做的, 没有隐式参数, 只有一个语法糖。

  1. local a = {0.0,-0.0}
  2. local b = {1,1}
  3. local c = map(math.atan2 , a , b) # map不是lua的标准库
复制代码
假设lua有一个Array库,就可以:

  1. local xs = Array{1}
  2. Array.push(xs,2) --> 或者
  3. xs.push(xs,3) --> assert(Array.push == xs.push)
  4. xs:push(4) --> 使用 : 语法糖, 与上面一行相同

  5. map(Array.push,{ {"a"}, {"b"}, {"c"},{"d"} },xs) --> 就不需要有unbounded/bounded之分。
复制代码
如果是非OO的functional语言,就只有data与function(甚至只有function)。
而一旦加入OO,事情就会非常复杂,class/prototype/instance,unbounded/bounded-method, normal/method/indirection/construction call……
这是我反感OO的一个地方, 引入了许多繁琐的概念, 而且完全没必要。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
27 [报告]
发表于 2012-02-28 02:28 |只看该作者
回复 24# 琳琳的小狗

关于method_missing。
lua支持,通过metatable的__index/__newindex。

更一般地,method_missing可以通过一个更范化的机制实现,可恢复的异常。
我不知道ruby的异常是不是可恢复的,但基本主流(python/js/C++/Java/C#)语言的异常都是一旦抛出就无法回到抛出点。
common lisp的condition system直接支持这个概念。

而可恢复的异常又是另一个更范化的机制的特殊化 —— coroutine, 被lua支持。
而coroutine又是另一个更范化的机制的特殊化 —— continuation, 被scheme支持。
ruby貌似这两者都支持。

那method_missing根本不需要语言作为特性提供,直接在method_missing的handler里补上然后回到抛出点就完了。
而这种通过很小的核心语言自身来增加新的语意结构,当然也包括DSL,就是lisp的卖点。
所以我很关心的那个问题: ruby是否支持code transformation? 你貌似还没回答呢……

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
28 [报告]
发表于 2012-02-28 02:41 |只看该作者
回复 24# 琳琳的小狗

关于语言之争是否有意义?
只有认为自己所学语言就是最有表达力的,或者干脆很懒的程序员才认为语言之争没有意义。
以你自己的例子来说, 你也能体会到并且承认ruby相对于java能得到巨大的生产力的提高, 但ruby就是生产力最高的语言么? 我看不见的。
或者你就只满足于这样的生产力?

这就是语言之争的意义。


关于ruby实现的lisp。
我不知道你举这个的意图何在?
因为ruby可以实现(或者说很容易实现)lisp,所以就不需要比较ruby与lisp了?
那很多语言都不需要与lisp比较了,都可以实现,
而且,为何不直接用lisp?


关于设计模式。
如果你逛C/C++社区,就知道我是反OO与反模式的。
在我还热衷C++的时候(那时候只用过lua,因为WOW,其他动态语言都没用过),就知道这玩意不行。
学了lisp更是懒得给别人指出设计模式为什么渣了。


关于“《Ruby 元编程》——十年来读到的最好的编程技术书籍”。
这本书的试读章节我没发现任何亮点。都是我在各种语言里早就玩得不想玩的东西 —— 信不信只好由你了。
十年来读到的最好书籍就是这个样子? 我只能说井太小……

跑这里来踢场子(其实发帖时没注意到这是ruby板块,因为它也在C/C++板置顶了),也是为了能让(或者说激将)一些有经验的ruby程序员说出ruby到底好再哪?
至少到目前为止,从你提到的那些ruby的功能里,我还是没发现任何亮点 —— 同样信不信只好由你了。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
29 [报告]
发表于 2012-02-28 03:53 |只看该作者
回复 24# 琳琳的小狗

最后说两点。关于ruby的语法以及新语法,还有ruby与FP。

怎么说呢,这也许不算ruby的缺陷,ruby并不需要是一个核心简单清晰却又支持很强表达能力的语言,也不需要支持FP编程。
但如果与之比较的语言少,就不会将ruby的某些东西理解为权衡,只会当作优势。


先说语法。
lambda {|x, y| x + y}.call(12, 26) #一个匿名、立刻执行的lambda对象,返回38
-> (x, y) {x + y}.call(12, 26) #同上,返回38,但使用了ruby1.9新增的“诡异”语法,哈哈

无节制地引入新语法并不是好事。 为什么在scheme里是( (lambda (x y) (+ x y)) 12 26 ) 而在ruby里要多一个call? 你想过这问题没有?
为什么函数的调用方式有这么多种 —— JS是4种,我看ruby也好不到哪去。

1. f(x)
ruby可以简化语法,写f x。但代价就是它没有语法来区分函数的行为与函数的结果。
其他语言里,f求值就得到函数本身,f(x)就是函数应用得到的结果, ruby里没法区分这两者,不能将f传递给另一个函数。
于是ruby就没有first class function, 于是就需要 method(:f)来做一个包裹传入, 并使用call。

连传入一个函数 —— 高阶函数,函数式编程的重要特性 —— 都这么麻烦,怎么谈FP?

2. f.call(x) 上面说了

3. yield x
我不知道block有什么好的地方,在我看来它是设计缺陷。平白无故地(也许并不是,后面会说我的猜测)增加了Proc这种概念, 以及Proc.new/lambda这样的复杂性。
在FP里,anonymous function是first class的。 而在ruby里, block是语法结构, 不是1st class value, method包裹的那个才是。

而且我现在就可以指出因为block的语法产生的代价, 没法很好的传出一个函数 —— 同样是高阶函数。
以compose作为传出高阶函数的例子, lua里可以 compose(f,g)(x) --> f(g(x))。 注意第2个调用。
而ruby里, f(arg) 这里是为block保留的。 写第2个调用就是错误。


语法很难被编程
lisp的路线是放弃过多的语法,尽量让语言的各个部分都可以被编程
common lisp支持read-macro,scheme有readtable,不过我不知道有没有被标准化。通过它们改变语法是很容易的事情。
比如支持clojure的 #(+ %1 %2) <=> (lambda (x y) (+ x y)。
但通常cl与scheme程序员不会这么做,而且clojure也明确地表示不会支持read-macro。
因为lisp更喜欢增加语意结构而非语法结构,过多地引入语法会增加AST(abstract syntax tree)在大脑中的转换层次,增加语意结构的转换难度。

再举个语法结构与语意结构的例子。 x,y = y,x 貌似ruby管这叫并行赋值? 许多语言 —— 包括lua/python都以此自豪。
而cl(也被emacs lisp偷学)的做法是(rotatef x y):
1. 互换变量的值是更一般化的rotate的一个特例, 还可以

  1. (let ((x 12) (y 26) (z 86))
  2.   (rotatef x y z)
  3.   (list x y z))
  4. ; => (26 86 12)
复制代码
2. 至少它没有违法DRY(Do not Repeat Yourself), x,y只出现了一次
3. rotatef 自己只是一个macro, 你可以看到它是怎样实现的, 而不是听人解释它是什么功能。

而通过增加语法结构的语言,学习成本高。C++在这方面也很恼火。
比如haskell的do notation、list comprehension都是用起来方便,但会影响到更深入地学习, 比如manod。
如果打算专注这语言的话这并不是特别严重的问题。
但至少还有一个潜在地缺陷 —— 你得语言的下一个版本支持它。
因为在很多语言里它都是不能被编程的, 是语言核心的一部分(haskell 语言是例外,可以重定义许多语法,但不是通过read-macro)。
比如你说的那个1.9支持的新诡异语法, cl 想做随时都可以做, 还可以做得更诡异。



最后,再说FP。

>> Ruby吸收了Lisp以及Smalltalk的特性,骨子里自然是具备FP特性的。
这说法你觉得科学么?
我还是很在意ruby是否吸收了lisp的code transformation —— 这与函数式编程无关,稍微又跑题了, 因为我确实关心这个。
只要支持这个, 语言核心就可以很简单, 只要支持if与continuation就是图灵完备了, 而且可以很容易地支持所有语言里的其他控制结构。
不过考虑到continuation实现的复杂性, 可以再加一个lambda。 if+lambda就是图灵完备, continuation只是让非局部跳转更容易实现。
如果使用惰性求值, 连if都可以不用。

>> 同理,要实现一个Lisp解释器并非什么难事,已经很多现成的代码了
>> 所以,Ruby对FP的支持程度,也就不言自明了
这种说法同样不科学, C也可以实现lisp解释器, C是函数式语言么?

functional style programming与functional programming language是两回事。
前者连C语言里都可以做。 除了非结构化的语言, 似乎都可以使用functional style programming。
问题是容易程度。
就像metaprogramming, 许多动态语言(dynamic language)都可以做, 静态语言只能在编译时做。
但只有lisp将这件事变得很容易, 容易到像呼吸那样平常。

其他更高阶的函数式编程支持,比如尾调用优化、纯函数与显式副作用、惰性求值…… 就不说了,支持这些的语言不多。
但ruby不支持1st class function,要用Proc.new或者lambda包裹出一个method, 要用call/yield而不是直接调用……
支持嵌套函数么? 这我不清楚, 貌似不行。
支持闭包, 这我很确定, 也就只支持一个没多少语言不支持的闭包……
这样的语言大量使用functional style programming绝对不会轻松。

另外,不只是语言本身,库是否使用函数式接口也很重要。
这点上C++/lua/python都做得不好,因为它们骨子里其实是命令式语言(我就不说OO了,我认为OO根本排不上programming paradigm)。
比如上面的:

  1. (map cons '(1 2) '( (10 100) (20 200) )) ; => ( (1 10 100) (2 20 200) )
  2. # python使用unbounded-method还是很容易,只是概念很复杂
  3. map(list.append,[ [10,100],[20,200] ] , [1,2])
复制代码
但结果是 [None,None], 因为list.append不返回新list, 而只是in-place更改。

而scheme的库就是一个正面例子。
Ruby,至少Array.push是返回array本身, 但我想这里更多的考虑是为了链式表达而不是函数式接口。
更多的我就不了解了。

因此,我认为Ruby是函数式语言的说法只是相对的。
只是许多人并没有函数式编程的精确定义,只知道笼统的说什么语言是,什么语言不是。
而Ruby支持的函数式语言的构造上面已经列出了, 貌似就只有闭包…… 真的少得可怜……

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
30 [报告]
发表于 2012-02-28 04:10 |只看该作者
回复 29# OwnWaterloo

哦,前面还有一个关于block的猜测忘记说了。
这个猜测就是:松本的在编程语言方面的理论水平,尤其是函数式编程,其实很差,甚至比Python的作者还要差。

http://www.artima.com/intv/closures.html

Yukihiro Matsumoto: Basically, blocks are designed for loop abstraction. The most basic usage of blocks is to let you define your own way for iterating over the items.

这段真把我看吐血了……

其实Python的作者还是懂函数式编程,只是他本人不喜欢。
松本根本就不知道…… 仅仅为了loop abstraction就定义了一种语法结构? 之后才发现block不是1st class, 再搞出method已经晚了。
我不知道ruby的发展历史, 如果block比 Proc/lambda 更先加入语言, 那猜测基本就正确了。


查看编程语言的发展很有意思, 尤其是Python与Lua的比较。
可以很清晰地观察到Python自下而上的设计,case by case: 我觉得那里不方便了, 于是添加一个特性。
而Lua是自上而下的设计: 语言需要支持那些范化且通用的构造? 那么就支持这些, 那些零零碎碎的特性不过是这些范化特性的特殊化而已。
就Python的generator的发展过程, 相比Lua的coroutine, 就看得人吐血了……
而且Python语言是越来越发散, 而Lua是越来越内聚…… 无他,作者的理论水平决定的。

看到上面引用的松本那段话,我相当怀疑他的理论水平。
当然,不被我吐槽的语言也没几个,连C++都经常被我吐槽。
可能只有Lua与各种Lisp在语言的表达能力与简炼方面有最高的比值, haskell这种奇葩我还没能学会…… 也许也能称上鬼斧神工……

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP