免费注册 查看新帖 |

Chinaunix

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

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

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
41 [报告]
发表于 2012-02-29 08:59 |只看该作者
回复 39# OwnWaterloo


    提一点,就历史来看,ruby(严格意义上的)是perl的后继,从名字和各种蛛丝马迹就可以看出来,似乎作者也承认了,不过我找不到引用。ruby最开始的目的是为了写perl代码更方便。所以ruby提供了很多的功能,省略括号是perl的语法,不过perl有必须的分号可以很好的解析;block也是模拟perl的“匿名子过程”(就是如同map $_[1]+$_[2], [1,2,3],[4,5,6]这种,注意第一个参数是表达式,而此表达式是作为匿名的lambda函数存在的),ruby身上可以看出大部分的perl的影子,甚至ruby的网络库就是比照perl的来实现的。其他语言对ruby的影响反而比较少。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
42 [报告]
发表于 2012-02-29 09:19 |只看该作者
回复 40# lcqtdwj


    lisp复杂程度一点都不高。我一句话就能解释明白,所谓“lisp复杂程度高”是因为lisp太灵活,你想做啥就做啥,而对于你想做的,有各种各样已经规定好的实现(包括if和lambda),而这些东西太多了,你才会觉得复杂。

lisp的本质,一句话就能说明白:程序是以S-exp表示的广义表,对程序的求值(运算、执行),本质上是对广义表的求值,这是一个递归过程:对广义表的第一个元素求值,并决定如何对其他元素进行求值。完了。

比如(display (if (< a 1) 'a 'b)),先求值(display ...),首先根据描述对display求值:他是个函数,那么函数的求值方式是对其所有后继子项求值,那么开始对(if ...)求值,if是个special-form,他的求值方式是对第二项进行求值,如果是真就对第三项进行求值,否则就对第四项进行求值………………

这个过程说起来复杂,实际上就是一个多叉树的遍历而已,任何语言都可以很简单地做到(所以说ruby能简单实现scheme真不是什么很自豪的事情),只是lisp特别灵活,它通过第一个元素对这个遍历过程进行了控制。你可以认为lisp特别适合于dfs搜索功能,当然这就是“lisp适合人工智能”这种论断的原因。

那lisp的问题在哪儿呢?在于效率。如上所说,要根据定义去执行lisp程序出乎人意料的简单,问题是性能就不好说了。ruby本来就慢,用这种方式求值lisp没什么问题,但是如果你是正儿八经去考虑高效地执行lisp程序,你不得不考虑很多问题:lisp到字节码甚至机器码的翻译(为了jit或者效率)、cons的高效存储、轻量级编译器的实现、垃圾回收的效率等等等等,这才是lisp的难点所在,为了克服这些难点,实现要么减少了lisp的灵活性(不允许修改+-*/神码的),要么体积会很大(以同时支持灵活性和效率),这才是lisp不容易实用的原因。

但是你必须承认,真正做到了这点的lisp效率是ruby的300多倍(参看programming language benchmark game),而做不到这点的lisp(如用ruby实现的lisp)本质上就是个toy而已。

最后说说ruby吧。这是我第一门脚本语言。最开始对它是很有好感的,但是因为了解到它是perl的后继,我就开始学习perl,感觉perl太难学就转了python,后来我常用perl(工作中)和python(私下里),就对ruby生疏了。作为一门perl的后继语言,ruby无疑是目标明确的——它拉低了编程的门槛(至于这个目标是不是有益去问OwnWaterloo吧),然而ruby自身也有很多问题,第一个就是翻译的问题了,第二个就是效率问题。ruby的效率低一方面用的是yacc而不是自行实现的翻译器,另一方面它的执行方式和朴素的lisp非常像——它也是执行语法树的!而且是以一种写死的遍历方式进行执行,这种方式我看是扬短避长了。它没得到lisp的灵活性,反而损失了性能,这是得不偿失的。

由此可以看出ruby对真正元编程的支持了,目前是很弱的,然而应该有足够的潜力达到很好的支持水平。而这本书里面提到的“元编程”,我觉得实在是不能被称为元编程——这种元编程不是通过生成代码来实现的,对效率没有任何额外的好处,只是“运行时获取程序所有信息”而已,最多算是“元执行”或者“元计算”。所谓元编程,即“生成程序的程序”,最初是为了可维护性(如检查)和效率(如直接生成复杂代码的执行结果),而这些在单趟的ruby模型下是做不到的,除非你用ruby去生成ruby代码:用ruby写DSL来描述需求、DSL产生数据结构、数据结构再产生ruby代码,再执行产生的ruby代码。如果是这样那还有点看头,不过ruby做到这个也就平淡无奇了,看看lua的一个绑定库的DSL吧:

  1. require 'lbind'.export(_ENV)
  2. require 'lbind.types'.export(_ENV)

  3. module 'gd' {
  4.     export = true,
  5.     include "gd.h";
  6.     subfiles {
  7.         --"gdImage.bind.lua";
  8.     };

  9.     object "gdImage" {
  10.         method "new" :cname "gdImageCreate"
  11.             (int "sx", int "sy") :rets(selfType:ptr());
  12.         method "newTrueColor" :cname "gdImageCreateTrueColor"
  13.             (int "sx", int "sy") :rets(selfType:ptr());
  14.         method "delete" () :alias "close" :cname "gdImageDestroy";
  15.         method "color_allocate" :cname "gdImageColorAllocate"
  16.             (int "r", int "g", int "b");
  17.         method "line" :cname "gdImageLine"
  18.             (int "x1", int "y1", int "x2", int "y2", int "color");

  19.         include "errno.h";
  20.         include "string.h";
  21.         method "toPNG" (char:const():ptr "name") :body [[
  22.             FILE *pngout = fopen(name, "wb");
  23.             if (pngout == NULL) {
  24.                 lua_pushnil(L);
  25.                 lua_pushstring(L, strerror(errno));
  26.                 return 2;
  27.             }
  28.             gdImagePng(self, pngout);
  29.             fclose(pngout);
  30.             lua_pushboolean(L, 1);
  31.             return 1;
  32.         ]];
  33.     };
  34. };

复制代码
ruby能产生这么紧凑自然的DSL么?

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

松本与Larry的基情貌似听说过……
但block一开始似乎真的只是loop construct……  稍后说……

1. 语法设计很难……  一个地方小赢可以导致其他地方大输……
2. 我不知道smalltalk是不是这样,但ruby也许就类似你说的那种纯粹的OO……
只有消息,木有函数……  想传入一个可调用的东西必须创建一个对象,产生者与使用者约定使用call消息……


关于block与%_[1]+$_[2]……  我问你,后者里面可不可以有return?
如果我告诉你前者不但可以有return,而且return不但可以跳出block,还可以跳出block的调用者…… 你会不会惊掉下巴……

  1. n.times do |i|
  2.   ...
  3.   return
  4. end
复制代码
或者

  1. n.times { |i|
  2.   ...
  3.   return
  4. }
复制代码
不仅仅是跳出block,还可以跳出times……   所以我说block一开始似乎真的只是为了loop……

do |i| ... end, {|i| ... } 我不知道谁先谁后……

同时,上面说了,block是一种语法:
1. 所以compose(f,g)(x)不行…… 因为后面是为block预留的……
2. block是一个隐式"参数"……  又隐式…… 咋就这么喜欢隐式……  一旦隐式就丢失了范性好吗……
3. 不是1st class…… 如果不是传递给那个隐式参数…… 需要某种东西将其转为1st……
于是就有Proc.new/lambda, 前者return跳出block的调用者, 后者仅跳出block……  还有个proc, 在一些版本里是lambda,在后续版本里是Proc.new……

lambda {...} 在1.9里有一种新语法……

故事没完……  proc是将block包裹为一个1st class value…… 可以传递给显式参数。
还有一种语法向那个隐式参数传递……  map(&...)

我还没有研究嵌套函数……  变量作用域等东西……  不知道会不会闹出更多喜剧……



我宁愿相信发展过程是这样……
1. do |i| ... end 为了将循环解耦合,也就是松本自己提到的loop abstraction
2. {|i| ... } 作为上面的语法糖
3. 需要不止一个可调用体了, 于是出现proc与它产生的包裹对象
4. "借鉴"lisp, 让lambda变成proc的同义词……
5. 为了面向对象…… 于是出现了 Proc class, 让Proc.new 完成原始的loop return, 让lambda完成匿名函数, 将proc从lambda改为Proc.new并废弃之……
6. 不知道1.9引入lambda的新语法后, 会不会再将 lambda给废弃掉……  因为, 这可不是面向对象的东西……
7. map(&...) 这个我也不知道应该插入到上面哪个地方……

你能体会我看到松本的那个interview中那段话的心情吗……  basically, block is ... loop abstraction……

宁愿相信这是因为松本没能一次性地发觉1st class function是更范化的抽象机制,配合exception也可以完成 loop return。
这最多只能说他笨。

而不是因为这就是松本真实的意图……  就是要让1st class function+exception可以完成的事变得这么复杂…… 因为这样才语法"自然", 才user friendly……
我能说他是变态吗……

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

语言能改变程序员的思想……  学了lisp后我发现一种新的改变…… lisp能改变程序员对语法的态度……
我现在看到各种纷乱的语法就%@#$@#$……  对C++11兴趣不大也有这个原因……
我直到现在都没去研究haskell的do notation,打算对monad有更好的理解之后再说……
而且haskell社区也有人认为do notation对理解monad是有害的。因为它让描述逻辑上是sequential的东西看上去像命令式语言……

lambda {|a,b| a+b} vs ->(a, b){a + b} 语意一点没变…… 连typing都不能节省多少…… 仅仅是因为它看上去好一些……


重度依赖语法的语言会让程序员想要更多的语法,为了让代码看上去更"natural",却不能简化任何语意……
但要解释、理解这些natural代码时,终结还是得回到语意上去……

语法繁多的代价前面已经指出不少了,只是受重语法语言影响的程序员不会察觉、承认,或者根本不在乎这点,甚至引以为豪……

嗯,其实我想说,你那段lua的DSL也许根本就不会被ruby程序员接受…… 因为这些代码看上去不像一些熟悉的东西……

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
45 [报告]
发表于 2012-02-29 14:15 |只看该作者
回复 44# OwnWaterloo


    那你觉得有语法好不好呢?反正我看见一堆括号就头疼

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

你知道的,我就是因为lisp语法怪,才选emacs而非vim的……  被人捧上天了也许有其原因……
于是我得到了……
haskell我也是去找自虐的……

ruby也被人追捧…… 但我还没得到……

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

>> 实现要么减少了lisp的灵活性(不允许修改+-*/神码的),要么体积会很大(以同时支持灵活性和效率),这才是lisp不容易实用的原因。

no。 需要某种灵活性就必须得承受相应的代价,时间或空间效率。

只要允许修改+,-,那所有语言在这里都会有效率损失。
只是一些语言不允许。
cl语言允许,但cl实现可以不允许。 以前给你的那个add反汇编, 确实只有4-5条机器指令, 一些操作调用栈, 还剩一条addl, 但肯定没有call/jmp。
就像其他语言里inline的效率与灵活性一样, g中的f调用被inline, f的改变就无法影响到g, 除非重新编译g。


同样,体积大也是实现的事而不是语言的事。
cl是将编程分为develop与deliver,前者为了开发方便会在程序中加入很多很多东西。
有些实现不关注体积(比如sbcl),其他也许默认是插入了全功能的开发用的信息,你得找到将这些信息去掉的编译参数。
你想要的是默认行为是让开发功能尽可能少,让这些功能显示传入?  目前没找到这样的实现, 不能证明这样的实现没有对不对?


BTW: lua里,你以前说 a+b 就会翻译为number +? 编译器怎么知道a,b没有元表?

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
48 [报告]
发表于 2012-02-29 17:08 |只看该作者
回复 47# OwnWaterloo


    lua如果发现+-×/的两个操作数如果全都是number,则执行默认操作,不管有没有__add神马的。也就是说,数字的+-×/不允许替换掉。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
49 [报告]
发表于 2012-02-29 17:09 |只看该作者
回复 47# OwnWaterloo


    我就有个小问题:luaJIT的效率已经堪比大多数的lisp实现了,而且相差不大,但是luaJIT的大小是500K,你找个跟luaJIT一样大一样快的实现?我就是在说lisp这种概念阻止了很好的优化。

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

a+b, 编译 —— 而且你说是一趟编译,一边parse一边就compile了 —— 这行代码时,该产生怎样的代码?
怎么知道a,b一定是number?

或者这么说: function(a,b) return a+b end,编译后是否仅支持传入number? 是否支持传入带元表的对象?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP