2006年8月31日星期四

Ruby的特性之三,Block

另外一个不得不说的特性应该是Block,Block是一段代码,可以看作是一种简洁明了的回调函数的实现方式。
比如下面这段代码:
def do_it_three_times ()
yield
yield
yield
end

do_it_three_times { puts "hello" }

上面这段代码定义了一个函数do_it_three_times,它的功能是调用传给它的block三次。所以这段代码的输出是:
hello
hello
hello

跟iterator配合起来使用也非常方便,比如:
["cat", "dog", "pig"].each {|pet| puts "I love "+pet}
它的输出是:
I love cat
I love dog
I love pig

Block在实现某些功能时异常方便,比如对于一个GUI程序中的所有按钮,点击后程序做相应动作。最愚蠢的办法是为每个按钮定义Button的子类,重载on_button_click成员函数;还可以用add_listener或者signal_connect之类的办法,将按钮点击事件与某个action关联起来。这种方法在大部分时候有些overkill。如果用block则可以这样写:
start_button = Button.new { player.start; }
stop_button = Button.new { player.stop; }
这是不是更加简单清晰呢!?

Block还可以用来实现Transaction,看这个例子:
class File
def File.my_open(*args)
result = file = File.new(*args)
# If there's a block, pass in the file and close
# the file when it returns
if block_given?
result = yield file
file.close
end
return result
end
end

这里定义的my_open除了可以像普通的open函数那样打开文件并返回打开文件对应的对象外,如果调用时提供了Block,它会调用这个block来处理该文件,然后将文件关闭,实现了
事务的完整性(打开的文件被确保关闭)。两种调用方式如下:

# 普通的调用方式,得到打开文件对应的对象
my_file = File.my_open("/path/to/somefile");

# Transaction的调用方式,得到处理结果
res = File.my_open("/path/to/somefile") do |file|
#some code to process the file
...
end

说明:以上例子来自Programming Ruby: The Pragmatic Programmer's Guide。这本书写得非常好。

2006年8月19日星期六

Ruby的特性之二,module_eval

利用module_eval,Ruby可以读入一段代码,把它加入到正在运行的系统之中。这简直太dynamic了。如下面的例子:

#定义一个类
class TestClass
end
#定义一字符串
code = %q{ def hello() " Cool, isn't?" end }
#让code立刻生效
TestClass.module_eval(code)
TestClass.new.hello()

这段代码将输出“Cool, isn't?”。module_eval在RoR中得到广泛的运用,比如下面这段在RoR中常见的代码

class WeblogController

Ruby的特性之一,类的修改

Ruby语言本身有很多有趣的特性,比如可以将类重新打开进行修改,对类的修改会立刻反映到该类已经生成的对象上。举个例子:

#定义一个类
class TestClass
def initialize()
puts "initialize Ver 1"
end
end

#初始化一个对象,输出是:initialize Ver 1
a = TestClass.new

#打开TestClass,进行修改:重新定义构造函数initialize,增加一个函数fun1
class TestClass
def initialize()
puts "initialize Ver 2"
end

def fun1()
puts "fun1 invoked"
end
end

#初始化一个对象,输出是:initialize Ver 2
b = TestClass.new

#对a和b调用fun1,得到同样的输出:fun1 invoked
a.fun1
b.fun1

借助这种动态特性,可以实现很多有趣的功能。比如实现ORmapping时,在运行中为一个表生成一个类,并且生成了若干对象。如果表发生了变化,只需要利用这个特性对类进行修改,就可以确保对象与表的同步。

开始学习Ruby和Ruby on Rails

前几天在一个J2EE的项目中打下手。简简单单一个网站,却需要用到Hibernate,Spring,Struts等一堆框架,写无数配置文件,到头来还是需要写很多冗余的代码,做很多本可以让计算机自动完成的维护工作。很不以为然。
之所以出现这种原因,我以一个无知者的身份妄加揣度,是因为J2EE在一开始设计的时候,就走了学院派那套大而空的路子,这跟Sun的JXTA如出一辙。
不过J2EE比JXTA要好一些,它得到了广泛的运用。在这其中,大部分人痛并快乐着的学习使用复杂的J2EE,并以征服各种不易用性而颇有成就 感(在J2EE里面找这类东西真是太多了);另有些聪明人不堪忍受J2EE拿牛刀杀鸡(且杀不死)的缺点,整出一堆框架来解决J2EE的各种实际问题。
然而,即使在这么多人做了这么多的努力之后,J2EE离一个完美的架构还差得很远。这个差距在Ruby on Rails出现之后就显得更加明显(我想,RoR的作者一定没有学过J2EE,否则就被毒害了),也许J2EE永远也追不上RoR。
我花了些时间看了http://www.rubyonrails.org/docs 上的Tutorials,就立刻决定摒弃以前对ruby的成见和对J2EE诚惶诚恐的敬畏,投身Ruby和RoR的怀抱。

2006年8月18日星期五

kde下自动mount usb硬盘

我用的是debian unstable。需要安装usbmount这个包,并对它的配置文件稍作修改:

打开/etc/usbmount/usbmount.conf,在"FILESYSTEMS"这个选项里增加vfat(如果你的u盘是ntfs格式,你自然还要加入ntfs),在FS_MOUNTOPTIONS这个选项里加入""-fstype=vfat,iocharset=utf8,codepage=936,umask=000,users"。其中iocharset要根据你的locale具体定。

随后重新启动udev:#/etc/init.d/udev restart

这样就OK了,插入U盘试试吧。

2006年8月7日星期一

ISP提供Cache支持,p2p的新方向?

周末跟一个做p2p的朋友聊天,他提到一个新的市场机会。
最近几年,类似BT、电驴这样的P2P应用在互联网流量中所占的比例越来越高,这让电信运营商非常头疼。以往普遍的做法是封杀,但是对于P2P这样有生命 力的应用,封杀无法取得好的效果。实际上,bt已经被用来发布正版软件、电影,基于P2P的语音软件skype正大行其道,P2P已经成为主流的互联网应 用。封杀不行了,那是不是可以采用疏导的方式呢?
朋友描述的就是这样一种电信设备,它大量部署在运营商的网络中,缓存热门的内容。当一个计算机要通过p2p获取某个内容时,它可以从距它最近的缓存有该内容的设备中取得,这样就降低了对核心网的带宽占用。当然要实现这种效果,需要P2P协议的支持。
我对此持怀疑态度,对于解决P2P占用过多网络资源的问题,我始终觉得应该从改进协议着手,实现对运营网络友好的P2P协议,而不是由运营商来提出一种标准,让强制推行;并且这种机制不应当由路由器来完成,不应当要求替换或升级现有网络设备。
这可能就是长期存在的两类观点,互联网的观点和电信的观点,的区别:)
但是不管怎么样,从技术的角度来说,这样一种机制总是会对网络的性能有所提升。可巧的是,今天一大早就看到slashdot的新闻:BitTorrent的开发者和CacheLogic公司合作,提供这样一种解决方案。有兴趣的朋友可以看看:

http://slashdot.org/article.pl?sid=06/08/07/214259&from=rss
http://www.cachelogic.com/home/pages/news/pr070806.php

Tomboy,支持wiki的便箴

又到了新软件推介时间,这次带给大家的是tomboy, 一个支持wiki的便箴软件。以前跟大家介绍过zim,也是一个用wiki的方式记录note的软件,不过那个软件相当的糙,用了一段时间我就放弃了。而 这次的tomboy却是非常的好用。实际上,前一段时间我打算把zim改进一下,基本上tomboy就是我想要的样子(还缺一些feature),甚至是 用gtk#写的,跟我的想法一致。大家可以试试能不能在windows上运行,应该问题不大。
前几天,tomboy成为gnome的一个标准模块,这标志着mono/.net技术将在gnome的未来占有一席之地,赞!

用yacc(bison)分析c代码

这些日子花了不少时间在一个c语言的语法分析程序上,这个程序要能识别变量、函数的定义、声明和引用,简而言之就是找出sourceinsight所能提供的信息(比si还要精确)。
分析代码和编译代码一样,都要做语法分析。但是因为分析代码不做预处理,所以就会遇到一些麻烦,比如处理一些定义的稀奇古怪的宏,无法判断一个identifier是变量还是类型。这么一来,严格的用C语言语法来分析代码就没法通过。
考虑到这个问题,一开始决定用lex作词法分析,生写语法分析。进行到中间,代码复杂度越来越高,感觉力不从心。又决定通过修改标准的C语言语法规则,用bison(yacc的GNU实现)来完成语法分析。
所谓“工欲善其事,必先利其器”,bison在做语法分析上,果然很方便。但是很快就遇到上面所说的问题:不做预处理,无法得到一个token的类型。标准的yacc支持的是LALR语法,LALR语法只预读一个token,无法解决这个问题。
好在GNU的Yacc实现bison支持GLR语法,GLR没有只预读一个token的限制,它用状态分裂来解决无法判断token类型的问题。采用GLR,写语法规则就基本上没有什么限制了。
但是GLR有个问题,它用状态分裂解决问题,如果所有分裂的状态只有一个能分析成功,那自然OK;如果不能,就叫产生了ambiguity(不确定),分析就失败了。
解决ambiguity,可以用%dprec来指定规则的优先级。当两个状态发生ambiguity时,根据它们对应的reduce规则的优先级,选择优先级高的那个状态。
但还有一类ambiguity的情况,用%dprec也没发解决。那就是两个产生ambiguity的状态它们对应的reduce规则是同一条。这类问题 很棘手,我只好采用重写规则来避免。但这就导致规则的可读性很差,而且往往是解决一个ambiguity又引入另外一个。
无奈,往bison的mail list里发了一封信求救,第二天收到一哥们回信,信中说:
“用merge啊。我也遇到过类似的问题。”
merge是啥呢?merge就正好是处理上面这类问题的,相当于把多个状态合并。因为是比较新的特性,在bison的文档里并没有很明显的提及。
这样,用bison分析c代码就基本没有问题了;)