Nirvana Studio » 2006 » 四月 :: 分享知识,传播技术

Archive for 四月, 2006

关注 JBossWeb

Posted by Nicholas Ding on 29th 四月 2006

介绍

JBossWeb 是 JBoss 2006 年新产品线中的一个非常重要的部分。他基于 Apache Tomcat 开发而成,但是 JBoss 对 Tomcat 进行了额外的开发。Tomcat 是一个纯 Java 实现的 Web 服务器,想必大家在实际部署的时候不单单是只部署 Tomcat,还需要同时部署 Apache Web Server,通过mod_jk 来整合 Tomcat,这个配置相当繁琐,而且性能未必优越,jk、jk2 的站点似乎很长时间没有再更新了。JBoss 对 Tomcat 的调整力度非常大,JBossWeb 已经不是简单的包含了 Tomcat,而是对 Tomcat 做了本地化,使用了Apache APR,即Apache Portable Runtime 对 Tomcat 底层 API 做了一个本地化实现,下面一副图引子 JBossWeb 的站点,对 JBossWeb 和 Tomcat 做了一个性能比较。

JBossWeb

架构

Tomcat 以内嵌的方式集成到 JBoss 中,基于 JBoss Microkernel 架构,这样以来,在 Tomcat 上就可以使用到更全面的功能,譬如JBoss提供的数据库连接池(感觉比Tomcat配置方便多了),真正的 JNDI 支持,当然还有 JTA 等以来 JNDI 基础设施的服务。

JBossWebArch

JBossWeb 不仅仅支持 JSP 等 Java 技术,同时他的架构还支持其他 Web 技术的集成,譬如 PHP、.NET 两大阵营的正和。到目前为止,JBossWeb 提供了一个与 PHP 集成的例子,通过 JNI 库调用 PHP,可以通过 servlet-mapping 调用 PHP 处理引擎处理 PHP,在此之前,要做到这一点需要配置 Apache 服务器,但现在,JBoss 做到了这一点,而且非常方便。

JBossWebBlock

下面的图介绍了Tomcat Native的基础架构,依仗于 APR 的支持,可以像 Eclipse 一样运行于各种平台,同时又可以提升性能。

TNative

URL 重写

URL 重写是 Apache 一个非常重要的部分,mod_rewrite 模块起到了非常大的作用。现在 JBossWeb 同样支持了这个功能,基本配置和 mod_rewrite 几乎一样,而且同样支持全局配置和针对每个Context进行配置。

Posted in Java | 2 Comments »

理解 ANTLR 语法文件

Posted by Nicholas Ding on 26th 四月 2006

理解 ANTLR 语法文件

译者:Nicholas @ NirvanaStudio

原文出处:http://www.placidsystems.com/articles/article-grammarlayout/grammarLayout.htm

你是否被Antlr语法文件的不同部分搞的晕头转向呢,你是否很想知道这些部分的含义呢?我们在这里将从另一个方面来了解它们,这里我们使用Antlr Studio来演示。

不管你信不信,Antlr语法文件在某种程度上和Java源代码很类似。什么,你看不出任何与Java代码相似的地方?好,就让我给你展示……

语法(Grammar)

1

有放在header部分的内容将会出现在Antlr生成的Java代码的头部。站在Java文件的层次上思考,你通常在这个部分里面放置包定义。你还可以把一部分import的内容放在这里。

2这个部分的代码对于文件中的每个语法来说是唯一的。这个部分会被放在类声明之前。以上的例子将只对CalcParser引入ArrayList和MyClass这两个类。

3 width="20" />然后我们开始定义语法,这看起来像是在声明一个类。

4 width="20" />在这个options部分,你可以制定语法生成的一些参数。在Antlr Studio中你可以使用Ctrl+Space看看哪些选项可用。

5 width="20" />Token部分用来制定“假想的”记号,那些通常没有声明在lexer中。这些是在TreeParsers中使用的“假想的”记号。

6 width="20" />另一个行为部分。这个部分的内容被放在类内部。你可以为你的解析器定义一些自定义方法。

规则(RULES)

7 width="746" />

Antlr语法文件中的规则定义对应生成的Java代码中的一个方法定义。

1 width="20" />2 width="20" />3 width="20" />4 width="20" />正如你所看到的,这里我们可以在规则中做任何事,这些可以通过一个函数完成。我们可以为规则指定参数,上面的例子显示了这一点,甚至可以指定返回值和异常。

5 width="20" />这个options部分允许你指定一些可选参数。

7 width="20" />我们可以指定自定义异常处理机。

Java and all Java-based trademarks and logos are
trademarks or registered trademarks of Sun Microsystems, Inc. in the United
States, other countries, or both.

Posted in 编译器 | 1 Comment »

Smalltalk不好吗?

Posted by ShiningRay on 22nd 四月 2006

原文:http://www.cincomsmalltalk.com/blog/blogView?showComments=true&entry=3295067130
by James Robertson

翻译:ShiningRay

我收到了一封邮件,看到了一个语言趋势的帖子的链接——这个帖子说大约在1995年,Smalltalk有一个很好的及正派的用途,但是在2005却被大家扔进了墙角。他问到:

那么为什么会导致这种情况呢?这个更大的故事的背后:对于Smalltalk的否定。我很感兴趣的是,为什么Smalltalk会从他的顶点——1995年OO语言的首选落到到它现在作为一个强大却没人用的平台的地步。这是否也是Java的未来呢?如果我能更好的理解Smalltalk的衰落(更不用提Objective C了),我可以更好地预期Java和其他语言的未来。

好吧,有两件事情影响了Smalltalk的使用——ParcPlace-Digitalk(也就是后来的ObjectShare)的行为和IBM的行为。先让我说一下PPD/OBJS。ParcPlace在1995年是一个快速成长的公司,但那在与Digitalk合并之前。之后这个公司就逐渐显现出一些组织上的劳损——管理小组充其量只是次好的(他们所做的与Digitalk合并的决定就很好的说明了这个问题)。

那次合并消耗了接下去的18个月和大量资金。公司不仅仅花了18个月尝试将VisualWorks和VSE合并,他还:

  • 烧掉了很多钱却带来了几个没有效果的新东西
  • 在尝试代码合并的过程中没有发布任何VW或者是VSE的新版本
  • 由于没有去管理合并本身的事宜,结果导致了在工程和咨询小组之间致命的冲突。

不用说了,这种事情根本不能给客户群带来任何信息。这已经够坏了——然后财政问题的谣言就传开了,然后再1997年的动态年管理层发布了VSE的EOL(不包括任何迁移策略)的声明,VW/VSE的代码合并终止,以及一个新的Java策略。然后VSE的客户群就乱套了,已经对不存在的新发布很紧张,质问在新的Java的关注下,到底这个产品还能获得多少关注。

一切就这样进行直到新的管理团队进入并把公司名称更改为了ObjectShare(在原来管理层的疯狂的收购行为中一个被培养出来的公司)。事情一团糟,Java产品砰一下砸了出来,VW也爬了出来。新的管理层尝试扶持股票价格但是未能成功,情况又继续下滑(这一次又没有对顾客和前景带来任何信心)。最后,Cincom在1999年收购了Smalltalk业务(从此事情在我们这边开始有了转机)。

。下面,你要看一下Java的介绍和IBM的举动。在90年代中期的时候,IBM有一个很成功的Smalltalk产品,而且他们所有的开发环境都是基于它的——从VisualAge Smalltalk产生了一个C++、Cobol、同时最后,一个Java工具集。IBM的这些东西做的都很好,并且已经设法(由于PPD刚刚抛弃了他们)。然而,Java得到了很多绯闻,而且IBM对于构建VA架构基础花了很多他们自己的钱。提醒一下,这部分是我的推测,不过我认为发生了这些事情:

  • IBM说Java在快速成长IBM saw the Java buzz growing
  • 他们认识到直接骑在Sun的Java上要比维护他们自己的一套基于VAST的开发工具要便宜得多

后一个观点最后因为Sun免费发布Java而成真。同时这两个举动都帮助了Java惊人地增长,我一点也不确定他们是否帮助了Sun一点点。这是另外一个故事了——在Smalltalk的世界中,IBM逐渐降低了他们对Smalltalk的投资(最后以他们把这个技术推给Instantiations而告终,今年),同时开始快速书写他们的Java故事。这也没有给Smalltalk用户或者是对Smalltalk的期望带来任何信息——特别是当IBM的销售人员们开始干劲十足地推进他们的Java生产线和驳倒Smalltalk的时候。

总之——ParcPlace(以及他后来的公司,PPD/OBJS),。同时,IBM开始从Smalltalk转移至Java上——也产生了同样的效果。在90年代中期其他的Smalltalk提供商还过于微小无法影响这种不断成长的观点病毒——Smalltalk正在消亡。

而现在不同了——从1999末开始,Cincom勤奋地研究Smalltalk,我们也拥有一个快速增长的、并且盈利的业务。同时还有很多其他的Smalltalk方言,包括现在要称为VA Smalltalk的。Smalltalk在当前并不是主流,但是它还是被很多期望一个有生产力的候选工具的公司所使用。

Posted in Smalltalk | No Comments »

Ma client server简明教程

Posted by ShiningRay on 22nd 四月 2006

翻译:ShiningRay @ NirvanaStudio

原文地址:http://minnow.cc.gatech.edu/squeak/2978

{译者:Ma client server是针对Smalltalk的一个网络应用框架,由于十分好用,介绍到这里来}

简介

Ma client server 可以帮助你非常方便地书写客户端/服务器端(Client/Server,以下简称C/S)程序。事实上,它容易到了服务器段程序只要用一行代码:

MaServerSocket new listenOn: 12345 answer: [ :requestByteArray | 'Hello world!' asByteArray ]


这个框架实际上是基于一个稳定可靠的、多线程的Socket模型,是由John McIntosh分享给我的。

在C/S模型中,一个客户端向服务器段发送一个请求,然后服务器处理请求并返回一个应答。

这个框架提供了相互配合的两层结构。你选择其中你想使用的一层然后写两个程序。一个客户端程序会给远程服务器发送请求,服务器上则运行着另一个程序处理请求。

如果你选择较低的一层,“套接字层”(Socket Layer),你发送的是ByteArray(字节数组,一个基本类)的请求,接收的是ByteArray的应答。在较高一层,“对象请求层”(Object Rquest Layer),你可以发送任意的请求对象,获取任意的对象作为应答。较高层上使用了“Ma对象序列化”(Ma Object Serialization)框架来将对象转换成ByteArray,然后让较低的一层为你把这个ByteArray发送出去。

使用套接字层

建立服务器

如果你只是要发送和接收ByteArray的数据,你可以随便写你的服务器程序。但记住,不管在哪里,你要接收ByteArray作为输入,你就必须应答一个ByteArray

然后,要启动服务器:

myServerSocket _ MaServerSocket new
	listenOn: 12345
	answer:
		[ :requestByteArray |
		... 将 requestByteArray 传给你的服务器端程序 ... ]

MaServerSocket 利用了 SharedQueue和后台进程来确保处理过程不会因在网络上发送和接收ByteArray数据而阻塞,现在发送和接收完全在后台进行。“answer”块一次对一个客户端请求进行执行。

当你完成之后,一定要关闭套接字这样资源才能正确被释放:

myServerSocket shutdown

在客户端获得应答

下面客户端程序建立了一个MaClientSocket对象,要将IP地址作为ByteArray中的数字指明:

mySocket _ MaClientSocket hostAddress: #(192 168 1 1) asByteArray port: 12345.

建立了套接字之后还没有进行任何连接。只有在你发送请求的时候才会发生:

responseByteArray _ mySocket sendData: myRequestByteArray

同时具有一个sendData:startingAt:count:waitForReplyIn:方法可以达到最大的效率。如果你提供作为返回的ByteArray 不够大,你就会得到一个更大的。

可选的套接字事件

MaServerSocket 要用到一个 “server”, 默认是一个MaServer对象不过它什么也不做。但如果你给MaServer派生子类并且重写其中一些方法,这些方法会在如下时候调用套接字加入队列、一个ByteArray请求完全抵达,或者是发送一个响应等等。详细信息见MaServer

使用对象请求层

如果你选择使用对象请求层,你发送“请求”对象并获得某种应答对象。这样你可以专心写客户端和服务器程序,而不用关系如何解析ByteArray了。

建立服务器

这里当你准备开始写你的服务器段程序时,不是建立一个MaServerSocket对象,而是建立一个MaTcpRequestServer对象:

myServer _ MaTcpRequestServer protocol:
		{ MyRequestClass1. 
		MyRequestClass2.  
		MyResponseClass1.  
		MyResponseClass2.
		OrderedCollection.
		Dictionary. "etc." }.

这是什么?是的,你必须告诉她你要发送和接收的所有类,来记录你请求对象的形式。例如,如果你有一个 SubmitArticleRequest类,里面有一个Article对象,你不仅要列出SubmitAritcleRequest类,还要给出Article和所有其他Article引用的类和这些类他们引用的类,如此继续。然而,你不需要给出String, ByteArray, Set, Symbol, SmallInteger, Float, True, False或者UndefinedObject等类。这些类已经自动成为协议的一部分了。

也可以指明一个MaServerConsole来接收事件。派生MaServerConole并改写他的一个通知方法,象下面这样,他就会被自动调用。

myServer _ console: myConsole

当你准备启动服务器时,你要使用 processOn:do:

myServer
		processOn: 12345 ;
		using:
			[ :requestObject |
			... 将 requestObject 传递给服务器程序,可以应答响应 ... ]

当完成之后,你需要关闭并释放系统资源:

myServer shutdown

建立客户端

你的客户端程序建立一个 MaTcpRequestServerLink 并指向服务器所监听的地址 hostAddress 和端口 port

myLinkToServer _ MaTcpRequestServerLink hostAddress: #(192 168 1 1) asByteArray port: 12345

在你要做任何事之前,你必须先建立连接。这确保你在客户端的镜像中包含了所有必须的协议 (如果没有就会抛出异常)。

myLinkToServer connect

如果要在网络状况和你的耐性做出平衡,你可能想改写默认的超时期限为30秒:

myLinkToServer timeoutSeconds: 30

在客户端获得应答

现在你已经连上了,你的客户端程序可以如下从服务器端获得应答:

myLinkToServer submit: someRequestObject

记住, someRequestObject 必须是协议的成员之一,同样你从submit:取回的应答对象也必须是协议之一:

好了!当你完成之后,别忘了断开连接这样你可以将网络资源还给系统

myLinkToServer disconnect

Posted in Smalltalk | No Comments »

有趣的Morphic图形系统

Posted by ShiningRay on 22nd 四月 2006

教程: 有趣的Morphic图形系统

作者:John Maloney
翻译: @ NirvanaStudio
原文地址:http://static.squeak.org/tutorials/morphic-tutorial-1.html

这篇教程要用在Morphic工程中。要建立一个Morphic工程,只要在Squeak窗口的背景上点击鼠标,不要再任何其他窗口上,出现了屏幕菜单。选择“open….”并选择“project(mophic)”。一个小的桔色的“Unnamed”Morphic工程窗口就出现了。点击窗口中间可以放大到全屏幕。(如果你不确定你在什么地方,只要探出屏幕菜单——如果标题是“World”,那你已经在Morphic工程中了)

首先我们需要建立一个浏览器“Browser”和一个工作区“Workspace”。先复习一下,在Squeak屏幕的空白处按下鼠标左键。在出现的屏幕菜单上选择 ”open…”。点击“browser”。一个绿色的浏览器就出现了。再次利用这个屏幕菜单,选择 ”open…” ,并选择“workspace”。

如果你是一个新手,刚刚看完前面的教程,你应该已经有一个叫“My Stuff”的分类。把浏览器最左边的列表拉到最底端,应该就放在那里。

如果没有,我们需要新建一个地方来添加你的东西。在浏览器的左上面板中,点击右键呼出菜单[Option-click] (在苹果机上 – 教程以下都将Mac苹果的指令以这种方式显示)选择“add item…”并输入“My Stuff”并按回车。把列表拖到最下面,你会发现它已经被选中了。

我们再次白手起家,建立一个程序。我们先从一个空的类开始,这里我们要让他成为Morph的一个子类。在Morphic对象系统中,Morph是一个通用的图形类。我们现在看一下建立新类的一个模版。修改成下面这样:

Morph subclass: #TestMorph 
    instanceVariableNames: '' 
    classVariableNames: '' 
    poolDictionaries: '' 
    category: 'My Stuff'

首先选择“Object”并输入单词“Morph”(M一定要大写)。选择“NameOfClass”并输入“TestMorph”。新建立的类就会成为Morph的一个子类。删除实例变量名和类变量名的文字内容。按Alt+s接受。[Command-s]

现在我们来到工作区并新建一个我们刚刚建立的类的实例。输入:

TestMorph new openInWorld.

选择这行并按下Alt-d [Command-d]或者从右键菜单中选择“do It” [Option-click]

一个新的图形对象——一个蓝色的方块,现在出现在左上角。这样你就搞定了一个图形对象,虽然几乎没写什么代码。你可以拾起这个对象并移动它。现在先用鼠标把它抓起来。注意了,当你拾起这个Morphic对象的时候,他下面投射出了一个阴影。这个可以帮助你确定你已经确切的把他抓起来了,而且在其他任何物体之上。如果有对象覆盖在上面,抓起这个对象会自动地把他拉到最前端,这时投影就会显示它现在在其他任何物体之上了。

你可能注意到奇怪的大写方式,但是在Smalltalk中,一个选择器只能是中间没有任何空格的一个字符串。所以我们约定在每个英文单词的第一个字母大写,然后去掉中间的空格连起来。其他语言也常在单词之间使用下划线或者破折号。

halo labels.gif
现在按住Alt并单击 [Command-click]这个蓝色的东西,这时你会看见一组不同颜色的小圈环绕在周围,我们称之为“光环”。这些光环每一个都是向这个Morph对象发送指令的一种快捷的途径。如果我的鼠标停在任意一个圆点上,一个小小的帮助气球会弹出来告诉我这个圆点能做什么。这个黑色的点上的气球说:“Pick up”。也就是,如果我拖动这个黑色的把手,他可以像我直接拖动这个蓝色方框一样。这样可能看起来是一个多余的功能,但是因为可能有时候Morph对象会有交互的行为,比如按钮,这个把手让你可以直接操作一些直接和鼠标动作相关的对象。

这里一个基本的想法是,大多数系统,如HyperCard,依靠模式来改变对象的大小等等。也就是编辑一个对象针对使用它是分离的。在Mophics系统中,你可以在任何东西上面使用光环不用考虑他是如何活动的。光环是在它运行时的一种安全的处理方法。你不需要中止他的按钮功能或者是其他的东西。如果这个东西是一个按钮,你可以无需激活就可以编辑它,也不需要关闭它。

让我们来看看光环到底能做什么。比如这个绿色的,它会产生一个副本。粉红的光环中的X会将它删除(移动到垃圾桶中)。另一个常用的功能是改变它的尺寸,由这个黄色的实现。现在暂时不要对它做其他事情了,尤其不要使用蓝色的按钮(在对象的左下角)。

现在让我们开始制作我们自己的对象吧。我们先写一些方法来自定义它。

我要做的第一件事情是让我们的对象在你用鼠标点击他的时候做点什么。这里我们要实现两个方法。点击浏览器,选中第三个面板中的 “no message”。把下面的代码粘贴到底部的面板并接受,Alt-s[Command-s]

handlesMouseDown: evt
    ^ true

如果这是我第一次在这个映像中接受一个方法,我必须先输入初始值,这个值将会跟着我修改方法一直记录。这个方法,handlesMouseDown,告诉Morphic对象有一个对鼠标敏感的东西。

下一个方法我们要添加的是当鼠标按下的时候实际要做的事情。继续,把下面的代码粘贴进去成为另一个方法。

mouseDown: evt
    self position: self position + (10 @ 0).

将在你点击它的时候,这个代码会让对象向右边移动10个像素。试着点击这个对象看看会发生什么。

在这时我们已经有了两个方法。一个返回“true”说我们需要响应mouseDown:事件。第二个是对鼠标按下事件做出的响应,移动了对象的位置。

现在我想给这个Morph对象一些行为来重新绘制它自己。把这个方法粘贴到浏览器中并用Alt-s接受 [Command-s].

drawOn: aCanvas 
     | colors | 
     colors := Color wheel: 10. 
     colors withIndexDo: [:c :i | 
         aCanvas fillOval: (self bounds insetBy: self width // 25 * i + 1) 
                color: c].

现在,当你点击对象的时候,你会看到对象的完全不同的外观。让我们回头看看这个方法究竟做了什么。

这个方法的第三行(代码的第一行)是给临时变量‘colors’赋值。如果你看到两个竖线和一列变量名,那这些变量就是这个方法的局部变量,通常可以叫做临时变量。在Smalltalk和Squeak中,你可以不用自己声明变量——如果确实有系统会自动询问你。而且我们也没有任何“类型”所以你不用声明任何类型。任何变量都可以存放任何对象。所以我们把表达式(Color wheel: 10)的值赋给‘colors’。要显示这个方法作了什么,选择‘wheel:’并按下Alt-m查看实现器。你会看到有两个实现——选择Color class的那个。

我们在Squeak中经常做的一件事情是在方法之前放上一小段注释。第一行说了‘wheel:’是干什么的。他返回一个thisMany colors的一个集合,thisMany是一个参数。这些颜色会均匀分散在色环上。这是颜色的光谱。

如果很方便可以给出这个方法的一个例子,这也是我们经常做的。这里第二个注释是一个表达式你可以实际运行的。选中引号中的内容并按下Alt-d。在屏幕的最上方出现了一条展开的色环。这时一个在光谱上间隔的一些颜色的集合。(但不是最大饱和也是最大亮度,所以他们不会太刺眼。

在这个世界里面我们可以直接在屏幕上涂鸦,但他之后不会自动清除。所以我使用了屏幕菜单的“restore diplay”菜单项来去掉那些画在屏幕顶部的东西。

了解“wheel:”的含义在这里有点偏离主题了。我们知道现在这是一些颜色的集合。点击标题栏左边的 “X”关闭窗口。

这个方法的下一部分遍历这个颜色集合。

"下面是drawOn方法的一部分:"
     colors withIndexDo: [:c :i | 
         aCanvas fillOval: (selfbounds insetBy: self width // 25 * i + 1) 
                color: c].

withIndexDo: 消息同时提供了一个颜色元素“c”和一个索引“i”,让我们能跟踪我们在列表中的位置。第一次循环索引会变成1,第二次是2……如此继续,直到10。我们只需要知道我们在列表中的哪个位置并增加缩进,让椭圆每次更小。我们发出消息”fillOval:color:“并在带上加上了括号的参数——一个矩形,比外部的矩形尺寸上缩小了 (((self width)// 25) * i + 1)像素,由(self bounds)返回——第二个参数是一个颜色。作为索引,i增加,缩进也增加,每次返回一个更小的嵌入矩形,每个矩形里面我们都会画一个椭圆并用颜色c填充。

当我们再看这个对象时,我们看见了10条颜色。这个看起来很不错啊,同时由于使用了发送给自身的消息“width”,作为他的一个参数,它就可以知道它实际上有多大。用Alt-点击[Command-click] 呼出光环并拖动黄颜色的点。你就可以改变它的大小了,不管变成什么尺寸,他都会一直重新绘制自身。

我下一个想做的事情是点击他并调用一些动画。既然这样那么第一件要做的事情是建立一系列的点。当我点击这个对象的时候,我希望他会沿着每个点来回移动直到所有的点都走过了。为了这个想法,我需要做些工作

Morph subclass: #TestMorph 
     instanceVariableNames: 'path' 
     classVariableNames: '' 
     poolDictionaries: '' 
     category: 'My Stuff'

回到浏览器,并点击“instance”按钮。然后会出现TestMorph的类定义。然后点击实例变量名的位置,在单引号中间,并输入“path”。这个会成为一个新的实例变量来保存一个显示我们对象位置的点的列表。现在我要点击名称为 “as yet unclassified”的消息分类。

当我添加了一个实例变量,我要知道它应该初始化为什么内容。当我们给TestMorph定义这个消息:

initialize
     super initialize. 
     path := OrderedCollection new.

这一行代码“super initialize”为什么出现在这里?虽然类TestMorph的定义看起来十分简单,但path并不是他唯一的实例变量。在浏览器的第二个面板中,右键点击出现一个菜单,选择“inst var refs”,你会看见一个列表,里边列出了这个对象从超类继承的其他变量。我这里不想选择其中任何一个。如果我做了,他会把所有用到这个实例变量的方法列出来,不过,你可以看到这里有自类Morph继承的6个实例变量。

“super initialize”给我的超类,Morph类发送了初始化消息。当你给接收器“super”发送消息“initialize”的时候,他其实是发送给“self”,但是要保证是被继承的initialize的版本被调用。

我们的初始化的中心是:“ path := OrderedCollection new”。它将一个空的集合赋值给path。现在当我的TestMorph接收了发送的intialize消息,他首先完成Morph要做的初始化,然后,他再完成它自己的初始化。结果是所有从Morph继承的实例变量包括我新添加的都被初始化了。

我们实际上在这里又有一个约定,一般不会自动给一个新的对象发送“initialize”消息。但是,Morph对象总是给每一个新的Morph实例发送初始化消息。

这个TestMorph对象,现在就在屏幕上,并没有对我们新的代码获得初始化。我们在定义了新的 initialize 之前就创建了他。让我们先把它删除并做一个新的。用Alt-点击 [Command-click]呼出对象的光环。点击里面带个 X 的粉红色圆圈来删除这个对象。他现在被扔进了垃圾筒了。现在我们执行下面的代码来搞一个新的TestMorph(也许这次就可以初始化好了):

TestMorph new openInWorld.

我们现在要当你在我们的对象上点击鼠标的时候,让他做点什么事儿。我们现在已经有了一个“path”集合。把下面的方法复制进去并且接受:

startAnimation
     path := OrderedCollection new. 
     0 to: 9 do: [:i | path add: self position + (0@(10* i))]. 
     path := path, path reversed. 
     self startStepping.

我们在第一行创建了一个新的 OrderedCollection。在下一行,我们作了10次计算并把表达式的结果加到变量path上。我们保存了相对于目前位置的一些点,”self position”,加上一个我建立的点。建立的点的X坐标是0,“@”(可以建立一个点)后面的,Y左边是i的10倍,“i”是循环的变量,从0到9每次增加1。所以,最终的效果是建立的10个点,Y坐标是从当前坐标加上(0的10倍)到(9的10倍)并把这些新的点添加到path集合中。

接下来一行,我们把path扩展为(path, path reverse)。“reverse”会将任何集合用相反的顺序放置。逗号(“,”)操作符是另一个消息选择器用来连接两个集合。

我们所做的是把一个10个点的列表,从0到90,并且附加上一个列表,从90返回到0。这样我们现在就有20个点,出发并最终回到原来的位置。

该方法的最后一行把我们的Morph对象登记在一个可以持续发送“step”消息的一个引擎中。这是动画引擎的秒针,是动画的心跳。现在我们必须让我们的Morph对象理解step方法:

step
    path size > 0 ifTrue: [self position: path removeFirst].

这个方法很容易理解。它说只要在点的列表中还有东西,也就是只要path集合的长度大于零,就移动自己。给自己发送消息“position”,并将从点集合中删除的第一个点作为参数传送进去。他在做每一步的之前检查并确保表中确实有东西可以删除。

从图象上看,它会立刻跳到下一个点。

现在这些东西都已经设置好要准备运行了,但是,他并没有开始动画。好吧,那是因为我们并没有真正通过向他发送开始动画的消息,所以我们才没有把这个球踢出去。我非得要做的一件事就是要试着找出发送消息给这个对象的途径,其实有一个最方便的方法就是让他自己给自己发消息,当鼠标按下的时候。

mouseDown: evt
    self startAnimation.

这里有两种途径可以定义这个方法。你可以在任何现有的方法上粘贴(在浏览器下面的面板中),或者你可以点击“mouseDown”在右边的面板中,并修改我们现有的mouseDown。其实当你接受的时候都是一样的。

当你完成了上面的,再去点击你的Morph对象,你应该能看到发生了一些东西,它动了,太好了~,但是,他动得十分十分十分得慢。哎~,我失望了因为我以为Sqeak是很快的。其实呢,是默认仅仅每秒钟才给对象发送一次“step”消息.但是对象可以决定它自己需要多久被通知要执行“step”,我们只要实现一个消息“stepTime”:

stepTime
    ^ 50

它返回对象要求的 step 消息的时间间隔(以毫秒计)。现在,明显你可以要求两次 step 消息之间是 0 秒,但是你不会成功。这里存在一个基本的判断。

现在如果我们再次点击我们的对象,我们就能看到一个快得多的动画。我现在把它改成 10 毫秒,看看能不能达到每秒 100 帧。让我们看看能不能成功。好的,我不知道我是不是真的达到了 100 帧每秒,不过他看上去确实很快。

你知道,我是给“动画公司”工作的。这个动画起始并立刻停止,同时每个人都知道还有“渐进”和“慢出”来让他看上去更优雅。这里有一个小小的改进,使用了“i”的平方来让动画开始的时候比较慢。

startAnimation
    path := OrderedCollection new. 
    0 to: 29 do: [:i | path add: self position + (0@(isquared / 5.0))]. 
    path := path, path reversed. 
    self startStepping.

我只更改了这个方法里面的一行。我们现在准备在path中建立30个点,然后附加上反转的,一共60个点,另一个需要更改的是替代10次。最后,表达式将会是“841 / 5”。

现在动画就是开始下落时比较慢并且加速然后在他回来时又慢下来。事实上,他看上去像某种球在弹跳。

现在是一个很好机会让你自己和预先制作的Morphic对象玩玩乐。我们先拿一个装满对象的容器。如果你的系统中启用了标签,点击屏幕底部的 “Supplies”标签。如果底部没有标签页,使用屏幕菜单并选择“authoring tools…”。选择“standard parts bin”。无论什么情况你都能获得一大帮对象你可以拖到屏幕上,在你拖出来一个之后,Alt-点击[Command-click] 呼出光环,并按住红色圆圈呼出对象的内容菜单(contents menu)。这里是一些Morph对象:

  • A RectangleMorph (内容菜单中, 试试 “raised bevel” 和 “insetbevel”)

  • An EllipseMorph

  • A StarMorph

  • A CurveMorph (内容菜单中, 试试 “show handles”)

  • A PolygonMorph (f内容菜单中 , 试试”show handles”)

  • A TextMorph (可以用黄色的把手把文字块加宽)

  • [ignore the gradient filled Sketch Morph for now]

  • [ignore the green PasteUpMorph]

  • 画家的调色板可以在你丢下的位置开始新绘制一幅图。画完后,点击“Keep”按钮把图案变成Morph对象

  • [ignore the StringMorph]

Posted in Smalltalk | No Comments »

ANTLR 介绍

Posted by Nicholas Ding on 17th 四月 2006

ANTLR 介绍

作者: Terence
Parr

译者:Nicholas @ NirvanaStudio


原文出处:http://www.cs.usfca.edu/~parrt/course/652/lectures/antlr.html

介绍

自1980年以来我手工编写了很多识别程序(recognizer)和翻译程序(translator)但最终我感到很恶心并且尝试将这个过程自动化:来源于我的座右铭:


Why program by hand in five days what you can spend five years of your life automating.

手工编写过很多程序之后你就可以发现一些共性,并且这些共性可以合理地格式化并且自动生成。我当时对yacc不是很熟悉但是想要一些东西去代替我原本需要手工

编码的工作。ANTLR就是这个最终的结果(实际上原来它叫做PCCTS)。我现在已经为之工作了十年了。

ANTLR, ANother Tool for Language Recognition, 是一个可以接受含有语法描述的语言描述符并且生成程序能够识别这些语言所产生的句子。作为一个翻译程序的

一部分,你可以给你的语法附上简单的操作符和行为并且告诉ANTLR如何构造AST并且如何输出它们。ANTLR知道如何使用Java,C++,C#或者Python来生成它们。

ANTLR知道如何构造识别程序并且将语法结构应用到三种不同的输入上:(i) 字符流,
(ii) 标记(token)流,(iii) 二维树结构。本质上这些对应了词法分析器(Lexer),解析器(Parser)和Tree Walker。用于制定这些语法(Grammar)的句法(Syntax),被称

meta-language,在所有情况下是独立的。

一旦你适应了ANTLR或者相应的工具,你将会以另一种眼光来看待编程。很多任务期待一种不同于传统编程语言流派的语言解决方案。举个例子,这些课程的笔记就是用TML编写

的,Terence’s Markup Language。我讨厌输入HTML所以我用ANTLR编写了一个简单的翻译程序来转换文本成为HTML或者PDF或者其他我讨厌直接编写的东西。

最后,情让我指出ANTLR仅仅是一个工具!它帮你通过自动生成单调乏味的组件来构造程序,但并不试图让你创造一个完整的编译器,举个例子,单行的描述。

在2003年以前,ANTLR的下载量一度达到5000每月。当时ANTLR直接暴露在公共域而且没有一个明确的版权但是附带了完整的源代码。

这些笔记假设你已经熟悉了基本的语言识别和翻译概念。那么现在你就需要熟悉ANTLR的元语言以及如何生成它。之后,我们将把焦点集中在构造复杂的翻译器上。

一个对 ANTLR 句法的简单介绍

了解ANTLR最好的办法就是通过例子。一个简单的计算器经常被用来作为起步教程,并且有一个很好的理由支持这么做:它容易理解且实现简单。还有一些ANTLR例子和教程,但

是在这里我将会用自己的语言来描述一个计算器。首先我们将要做一些东西可以直接计算这些简单的表达式。然后我们将会生成树并且计算这课树得到相同的结果。

当你知道最后需要将输入的字符流断开成为一个个的记号(token),思考表达式的语法结构是一个良好的开端。

直接执行句法

识别程序

让我们编写一个程序来接受一个带有加、减、乘,例如3+4*5-1的表达式,或者带有括号的,用来强行限制计算顺序的如(3+4)*5的表达式。

所有ANTLR语法都是LexerParser或者TreeParser的子类,因此你需要从语法的层次上来思考这些东西,你将会构造一个Parser的子类

。在类声明之后,内需要用EBNF符号来制定规则:


class ExprParser extends Parser;

expr: mexpr ((PLUS|MINUS) mexpr)*
;

mexpr
: atom (STAR atom)*
;

atom: INT
| LPAREN expr RPAREN
;

这个词法分析器遵循了一个相似的模式并且只需要定义一些操作符和空格。将这些词法放入一个文件中,expr.g,是最简单的方法:


class ExprLexer extends Lexer;

options {
k=2; // needed for newline junk
charVocabulary=’\u0000′..’\u007F’; // allow ascii
}

LPAREN: ‘(’ ;
RPAREN: ‘)’ ;
PLUS : ‘+’ ;
MINUS : ‘-’ ;
STAR : ‘*’ ;
INT : (’0′..’9′)+ ;
WS : ( ‘ ‘
| ‘\r’ ‘\n’
| ‘\n’
| ‘\t’
)
{$setType(Token.SKIP);}
;

要生成一个采用Java解释的语法,用如下方式:


$ java antlr.Tool expr.g
ANTLR Parser Generator Version 2.7.2 1989-2003 jGuru.com
$

ANTLR 生成了什么?

ANTLR生成的识别程序模仿了你需要手工编写的递归的解析器;yacc和它的朋友,另一方面,生成了一个满是整数的表来模拟有限状态机的行为。

ANTLR 将会生成以下文件:


ExprLexer.java
ExprParser.java
ExprParserTokenTypes.java
ExprParserTokenTypes.txt

如果你看下生成的代码,举个例子,ExprParser.java,你将会看到对语法解析文件expr.g中的每个规则生成了一个函数。举个例子,mexpr

atom的代码应该是这样:


public void mexpr() {
atom();
while ( LA(1)==STAR ) {
match(STAR);
atom();
}
}

public void atom() {
switch ( LA(1) ) { // switch on lookahead token type
case INT :
match(INT);
break;
case LPAREN :
match(LPAREN);
expr();
match(RPAREN);
break;
default :
// error
}
}

注意这些规则引用被翻译成了函数,记号引用被翻译成了match(TOKEN)调用。从一个语法文件构造解析器的唯一难点就是计算lookahead信息。

记号类型这个类定义了记号类型常量,以便于词法分析和语法分析之用:


// $ANTLR 2.7.2: “expr.g” -> “ExprParser.java”$

public interface ExprParserTokenTypes {
int EOF = 1;
int NULL_TREE_LOOKAHEAD = 3;
int PLUS = 4;
int MINUS = 5;
int STAR = 6;
int INT = 7;
int LPAREN = 8;
int RPAREN = 9;
int WS = 10;
}

测试词法分析和语法分析

要使用生成的解析器,在ExprParser.java中,使用如下main()函数:


import antlr.*;
public class Main {
public static void main(String[] args) throws Exception {
ExprLexer lexer = new ExprLexer(System.in);
ExprParser parser = new ExprParser(lexer);
parser.expr();
}
}

$ java Main
3+(4*5)
$

或者针对无效输入:


$ java Main
3++
line 1:3: unexpected token: +
$

或者


$ java Main
3+(4
line 1:6: expecting RPAREN, found ‘null’
$

表达式计算

要实际计算表达式,只需要给解析器增加行为:


class ExprParser extends Parser;

expr returns [int value=0]
{int x;}
: value=mexpr
( PLUS x=mexpr {value += x;}
| MINUS x=mexpr {value -= x;}
)*
;

mexpr returns [int value=0]
{int x;}
: value=atom ( STAR x=atom {value *= x;} )*
;

atom returns [int value=0]
: i:INT {value=Integer.parseInt(i.getText());}
| LPAREN value=expr RPAREN
;

词法分析也是一样,除了增加一个print语句在主函数中:


import antlr.*;

public class Main {
public static void main(String[] args) throws Exception {
ExprLexer lexer = new ExprLexer(System.in);
ExprParser parser = new ExprParser(lexer);
int x = parser.expr();
System.out.println(x);
}
}

现在,当你运行程序,你会得到一下结果:


$ java Main
3+4*5
23
$ java Main
(3+4)*5
35
$

ANTLR 如何翻译动作

动作通常会被放入生成的解析器的代码中:

像下面的return规则


mexpr returns [int value=0]
: …
;

被翻译为


public int mexpr() {
int value=0;

return value;
}

如果你增加一个参数,这个参数同样复制到了方法的定义:


mexpr[int x] returns [int value=0]
: … {value = x;}
;

生成


public int mexpr(int x) {
int value=0;

value = x;
return value;
}

所以,完整的mexpratom翻译规则看起来像下面的代码:


public int mexpr() {
int value=0;
int x; // local variable def from rule mexpr
value = atom();
while ( LA(1)==STAR ) {
match(STAR);
x = atom();
value *= x;
}
return value;
}

public int atom() {
int value=0;
switch ( LA(1) ) { // switch on lookahead token type
case INT :
Token i = LT(1); // make label i point to next lookahead token object
match(INT);
value=Integer.parseInt(i.getText()); // compute int value of token
break;
case LPAREN :
match(LPAREN);
value = expr(); // return whatever expr() computes
match(RPAREN);
break;
default :
// error
}
return value;
}

通过AST中间形式计算结果

现在你看到了一个基本的直接基于句法的翻译/计算描述,里面的语法/句法制定了何时执行动作
一个强有力的策略就是构造一个间接的表示形式持有所有或者大多数编码过的输入符号,在数据结构中,包含这些记号的关系。
举个例子,输入“3+4”可以通过一个抽象语法树(AST)来表示:


+
/ \
3 4

针对这种树,内需要一个TreeWalker(由ANTLR从一个树语法中生成)来计算之前的相同值,但是采用了一个不同的方式。

AST的用法变得很清晰,就是当你需要从多次遍历这棵树来指出什么需要计算或者重写,或将树转变为另一种语言的时候就需要用AST。

构造AST

用ANTLR来生成一个有用的AST非常容易。在我们的例子中,打开buildAST选项并且增加一些后缀操作符告诉ANTLR何种记号需要构成子树的根节点。


class ExprParser extends Parser;

options {
buildAST=true;
}

expr: mexpr ((PLUS^|MINUS^) mexpr)*
;

mexpr
: atom (STAR^ atom)*
;

atom: INT
| LPAREN! expr RPAREN!
;

然后,词法不需要改变,用来计算树结果的主程序如下:


import antlr.*;
import antlr.collections.*;

public class Main {
public static void main(String[] args) throws Exception {
ExprLexer lexer = new ExprLexer(System.in);
ExprParser parser = new ExprParser(lexer);
parser.expr();
AST t = parser.getAST();
System.out.println(t.toStringTree());
}
}


$ java Main
3+4
( + 3 4 )
$ java Main
3+4*5
( + 3 ( * 4 5 ) )
$ java Main
(3+4)*5
( * ( + 3 4 ) 5 )
$

AST 解析与计算

通过以上的解析器构造的树非常简单。在TreeParser中一条规则就够了。


class ExprTreeParser extends TreeParser;

options {
importVocab=ExprParser;
}

expr returns [int r=0]
{ int a,b; }
: #(PLUS a=expr b=expr) {r = a+b;}
| #(MINUS a=expr b=expr) {r = a-b;}
| #(STAR a=expr b=expr) {r = a*b;}
| i:INT {r = (int)Integer.parseInt(i.getText());}
;

主程序被修改成了使用新的TreeParser来实现计算功能:


import antlr.*;
import antlr.collections.*;

public class Main {
public static void main(String[] args) throws Exception {
ExprLexer lexer = new ExprLexer(System.in);
ExprParser parser = new ExprParser(lexer);
parser.expr();
AST t = parser.getAST();
System.out.println(t.toStringTree());
ExprTreeParser treeParser = new ExprTreeParser();
int x = treeParser.expr(t);
System.out.println(x);
}
}

现在你得到了树结构以及计算结果。


$ java Main
3+4
( + 3 4 )
7
$ java Main
3+(4*5)+10
( + ( + 3 ( * 4 5 ) ) 10 )
33
$

Posted in 编译器 | No Comments »

初识 ANTLR

Posted by Nicholas Ding on 17th 四月 2006

由于并非科班出生,所以对于喜欢计算机程序设计的我来说,尽管通过自己的努力学习了相当多的实用技术,但是让我总觉得遗憾的除了高等数学之外就是汇编和编译原理了。从C到Java的过渡,让我从指针和数据结构中脱离出来,长时间的使用Java让我很少去接触系统底层,开发中几乎不会遇到任何汇编代码,除了能够理解基本的汇编指令之外,对于汇编,我已经没有多少感情了。除了汇编之外、数学和编译原理一直是我最想去学习的,但是自学编译原理让人很难上手,虽然可以大体上理解书上的理论,可是依然感觉要自己亲手写个解释器则是难上加难,直到遇到了ANTLR,我才渐渐开始理解《编译原理》这本书的真正内涵。

在《编译原理》这本书上的介绍非常理论性,对于一个非科班出生人来说很难简单的读懂,他在一个很高的高度上涵盖了编译器(Complier)和解释器(Interpreters)的内容。编译器与解释器的界限是非常模糊的,ANTLR的作者Terence Parr在它的文章An Overview of Language Implementation上给出了一个比较浅显的解释。

Anything that executes natively via machine instructions you can safely say is compiled; everything else is interpreted. A more useful way to describe the difference is that compilers translate source to an intermediate representation (IR) and then translate that to machine code usually by making several passes over the IR. An interpreter on the other hand stops the compilation process before machine code generation at the IR–it “executes” or interprets the IR, emulating an abstract high-level machine architecture.

总体来说,要实现一个Compiler的难度要比Interpreter大的多,虽然经过编译的代码在执行效率上要比动态解释的代码高,但是解释型的语言具有更快的加载速度和扩展性,况且基于虚拟机的动态语言的盛行也证实了解释型语言的强大生命力。

下面主要讨论一个解释器的原理。一般来说,解释器接受一段文本,对文本进行词法分析,经过词法分析之后在进行语法分析,在检测无误后根据语法规则开始解释。

下面看一个最基本的词法分析的例子。这个例子是一个最简单的词法分析器,来匹配文本中的字符串和数字。这个文件采用EBNF描述。例子取自于Terence Parr所写的关于ANTLR的例子。

class SimpleLexer extends Lexer

options { k=1; filter=true; }

ALPHA
    : ('a'..'z'|'A'..'Z')+
    {System.out.println(“Found alpha: “ + getText());}
    ;

NUMERIC
    : ('0'..'9')+
    {System.out.println(“Found numeric: “ + getText());}
    ;

EXIT
    : '.' {System.exit(0);} ;

将这个文件保存为simple.g,然后使用ANTLR生成相应的词法分析器,java antlr.Tool simple.g,并且使用以下代码可以观察结果。

import java.io.*;
public class Main {
    public static void main(String[] args) {
        SimpleLexer simpleLexer = new SimpleLexer(System.in);
        while(true) {
            try {
                simpleLexer.nextToken();
            } catch(Exception e) {}
        }
    }
}

如果你输入:This Lexer recognises strings and numbers: hello 22 goodbye 33

那么程序将会输出:

Found alpha: This
Found alpha: Lexer
Found alpha: recognises
Found alpha: strings
Found alpha: and
Found alpha: numbers
Found alpha: hello
Found numeric: 22
Found alpha: goodbye
Found numeric: 33
It ignores everything else: -=+/#
Found alpha: It
Found alpha: ignores
Found alpha: everything
Found alpha: else
.

这个例子演示了使用ANTLR如何编写一个简单词法分析器,一般来说词法分析是配合语法分析一起使用的,在ANTLR的站点上有很多例子可以参考。

ANTLR : http://www.antlr.org/

 

Posted in 编译器 | No Comments »

FIT 简介

Posted by ShiningRay on 11th 四月 2006

Fit: Framework for Integrated Test

优秀的软件需要协作和沟通,在开发过程中,客户如何才能知道他们的程序员如何正确处理了事情呢?而程序员又如何知道客户究竟想的是什么呢?测试人员如何才能知道什么是正确的而什么又是错误的?让这些小组能有效和精确地交流应该是团队创建伟大软件的一个目标。

FIT就是一个用于增强交流和协作的工具。FIT创建了一个在客户和程序员之间的反馈循环。FIT让客户和测试人员可以使用诸如Microsoft Office之类的工具来给出程序应当如何表现的例子——而无需成为直接编码的程序员。FIT自动针对实际的程序检测那些例子,这样就在业务世界和软件工程世界之间建立了一个简单而且有效的桥梁。

有了FIT,客户可以通过将他们的主题相关的专业知识和想象引入实际的工作中,给开发过程直接提供更多的指导。客户所获得的更多关于目前产品开发中正在发生的东西的可见性,能让他们随时控制项目避免偏离目标。

==FIT如何工作==

FIT通过读取由类似MicrosoftWord之类的工具生成的HTML文件中的表格来进行工作。每一个表格通过一个程序员写的“装置”来进行解释。装置将通过运行实际的程序来检测表格中的例子。

在这个例子中,团队要建立一个产品用于计算职工的薪水。团队已经一起创建了一个包含一些如何计算小时薪水例子的FIT文档。

表格包含了例子。第一行告诉了FIT如何读取表格。第二行给出了例子的头,剩下的行给出了例子。例如,在第一个表格中的例子说:“如果某人工作了40个工时同时没有休息时,那么就付给$20每小时的薪水,然后他的总薪水是$800。”

(虽然这个例子只使用了一个简单的表格格式来展示计算的结果,其实还有很多不同的表格格式可以使用。)

FIT自动针对软件检验表格中的例子。在我们这个例子中,对前两个测试案例,软件给出了正确的解,所以FIT把表格单元标示为绿色。在最后一个测试案例中,软件给出了错误的解,所以FIT把这个表格单元标记为红色。这个软件说职员的总薪酬是$1,040,而期望的值是$1,360。


为了能让FIT能使用这个表格工作,团队的程序员创建了一个“装置”来告诉FIT如何和他们的软件交流。情况类似下面:

程序员使用了一个ColumnFixture来将表格中的列映射到装置中的变量和方法上。前三列,提供了信息,对应于装置中的变量。最后一列,包含期望的值,对应于装置中的Pay()方法。要计算答案,程序员使用他们程序中的WeeklyTimesheet类。

==总结==

FIT给予了客户和程序员一个关于软件的精确交流的方法。客户所给的具体的例子让程序员能深刻理解将要构建的产品。程序员的对于装置的工作和软件可以让客户给出不同的例子进行试验来获取对于软件如何真正工作更深入的了解。这样通过一起工作,整个团队可以学会更多关于产品的内容并产生更好的结果。

Posted in 测试 | 1 Comment »

Lua 5.0 参考手册

Posted by ShiningRay on 5th 四月 2006

Lua logo Lua 5.0 参考手册

作者: Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes

Copyright © 2003 Tecgraf, PUC-Rio. All rights reserved.

译者:ShiningRay Nicholas @ NirvanaStudio


由于参考手册较大,请访问:Lua 5.0 参考手册

Posted in Lua | 4 Comments »

PHP 对比 PERL

Posted by ShiningRay on 5th 四月 2006

本文是 http://tnx.nl/php - 如果你要复制他,请保持这个链接。

翻译:ShiningRay @
Nirvana Studio

目录

参数和返回值极其矛盾

要展示这个问题,下面有一个函数列表,里面的函数用来匹配用户定义的内容:(也许甚至那些用PHP的人才会使用这个文档,只是用来查看该用哪个函数:P)

  匹配 替换使用 大小写敏感 返回的数字 数组参数 返回匹配 s/m/x标志 偏移(-1=结尾)
ereg ereg   所有 数组 0
ereg_replace ereg 字符串 所有 0
eregi ereg   所有 数组 0
eregi_replace ereg 字符串 所有 0
mb_ereg ereg[1]   所有 数组 0
mb_ereg_replace ereg[1] 字符串/表达式 所有 0
mb_eregi ereg[1]   所有 数组 0
mb_eregi_replace ereg[1] 字符串 所有 0
preg_match preg[2]   皆可 一个 数组 0
preg_match_all preg   皆可 所有 数组 0
preg_replace preg 字符串/表达式 皆可 无/所有 0
str_replace str 字符串 所有 数字 0
str_ireplace str 字符串 所有 数字 0
strstr, strchr str   一个 子串 0
stristr str   一个 子串 0
strrchr str   一个 子串 -1
strpos str   一个 索引 n
stripos str   一个 索引 n
strrpos char[3]   一个 索引 n
strripos str   一个 索引 -1
mb_strpos str[1]   一个 索引 n
mb_strrpos str[1]   一个 索引 -1

这种问题还存在在其他的函数组里,不仅仅是匹配的这部分而已。

(在Perl中,所有这些功能都可以通过四个简单的操作符来完成。)

[1] 用于处理多字节字符

[2] PCRE regex: 所谓的“Perl兼容”的正则表达式。

[3] 在PHP5中也是字符串str

PHP对大小写不敏感的操作使用不同的函数

(这个可能会有两方面的争论。有些人认为提供不同的函数更好,即使这意味着又要记很多名词了)

在Perl中,你可以使用两个lc() 或者是 /i 标志,而PHP通常会提供一个大小写敏感的变量。
而且,大小写不敏感的那些版本的函数名的命名方式也不一致。

Perl: $foo cmp $bar lc $foo cmp lc $bar
PHP: strcmp($foo, $bar) strcasecmp($foo, $bar)
     
Perl: index($foo, $bar) index(lc $foo, lc $bar)
PHP: strpos($foo, $bar) stripos($foo, $bar)
     
Perl: $foo =~ s/foo/bar/ $foo =~ s/foo/bar/i
PHP: $foo = str_replace('foo', 'bar', $foo) $foo = str_ireplace(...)
PHP: $foo = ereg_replace('foo', 'bar' ,$foo) $foo = eregi_replace(...)

PHP的函数命名方式的不一致

  • 大小写不敏感的函数有一个’i'或者’case’在函数名的不同的位置。
  • 毫无表现上的规律 有下划线的 和 没下划线的:
    下划线                   无下划线:
    stream_get_line          readline
    disk_free_space          diskfreespace
    is_object                isset
    mcal_day_of_week         jddayofweek
    set_error_handler        setlocale
    snmp_get_quick_print     snmpget
    get_browser              getallheaders
    base64_encode            urlencode
    image_type_to_mime_type  imagetypes
    msql_num_fields          mysql_numfields
    php_uname                phpversion
    strip_tags               stripslashes
    bind_textdomain_codeset  bindtextdomain
    cal_to_jd                gregoriantojd
    str_rot13                strpos
    

    Perl的核心函数名则没有含有下划线的。

  • PHP 有 unlink 、link 和 rename 和系统调用一致,但是 touch 的系统调用是 utime, 不是 touch。
  • 同时你也无法确定单词的顺序:
    • 宾语 动词:base64_decode, iptcparse, str_shuffle, var_dump
    • 动词 宾语:create_function, recode_string

    Perl的核心函数都是“动词 宾语”结构的除了替代的 dbm* 函数。(注意里面的 sys 是一个前缀,而不是一个宾语。同时 flock 和 lstat
    是根据系统调用命名的。shm* 和 msg* 是库函数调用)

  • “to” 还是 “2″?

    ascii2ebcdic, bin2hex, deg2rad, ip2long, cal_to_jd (jdto*, *tojd), strtolower,
    strtotime,

PHP没有词法范围

Perl 有词法范围和动态范围。PHP则没有。

对于为什么词法范围很重要的解释,可以参考 Coping with Scoping.

  PHP Perl
超全局(Superglobal) [1]
全局(global)
函数局部
词法域(块局部)
动态域

[1] Perl有一些变量总是在main:: 命名空间中。这些类似于PHP的超全局变量。

[2] 在子过程的块中使用一个词法变量,就可以作为一个函数的局部变量。

PHP的主名空间中函数太多

(使用编译了所有核心分发包中的可用扩展的核心库,我们使用了2003年11月发布的版本)

PHP 主要函数的数量:3079 [1]
Perl主要函数的数量:206 [2]
中值PHP 函数名长度:13
平均PHP 函数名长度:13.67
中值Perl函数名长度:6
平均Perl函数名长度:6.22

注意,Perl的一些函数有简短的等价语法:

readpipe('ls -l') ==> `ls -l`
glob('*.txt')     ==> <*.txt>
readline($fh)     ==> <$fh>
quotemeta($foo)   ==> "\Q$foo"
lcfirst($foo)     ==> "\l$foo"  (lc is \L)
ucfirst($foo)     ==> "\u$foo"  (uc is \U)

[1] 来源:PHP Quick Reference

[2] 来源:perldoc perlfunc

PHP缺少抽象令 TIMTOWTDI* 走向糟糕的极端

*(There Is More Than One Way To Do It,有不止一种方式来完成它)

为什么PHP有3079个函数但是Perl却只有206个?在PHP中,常常有好几个十分相似的函数。在Perl中,你要了解和记住的要少很多。

另外一个重要的因素是模块的使用,尤其是DBI模块——它用来提供数据库支持,而不是把很多特性塞进内核,占用了空间却很少用到。

(不常用的模块不计算在内(所以这里排除了PHP的PEAR和Perl的IO::File)).
如果核心没有提供类似的功能,那么这些模块也会算在里面。为了简便起见,内部的工作方式将会忽略。)

  • 转义:
    • PHP: (14)
          dbx_escape_string, escapeshellarg, escapeshellcmd, pg_escape_bytea,
          pg_escape_string, pg_unescape_bytea, addslashes, addcslashes, preg_quote,
          quotemeta, mysql_escape_string, mysql_real_escape_string,
          mysqli_real_escape_string, sqlite_escape_string
    • Perl: (2) [1]
          quotemeta, $dbh->quote
      
  • 排序:
    • PHP: (16)
          sort, arsort, asort, krsort, ksort, natsort, natcasesort, rsort, usort,
          array_multisort, uasort, uksort, dbx_sort, imap_sort, ldap_sort, yaz_sort
    • Perl: (1)
          sort
  • 遍历列表:
    • PHP: (10)
      array_filter, preg_grep, array_search, array_unique, in_array, array_map,
      array_walk, array_count_values, array_change_key_case, array_sum
    • Perl: (2)
      map, grep
  • 分割:
    • PHP: (8)
      split, explode, strtok, spliti, chunk_split, mb_split, preg_split,
      str_split
    • Perl: (1)
      split
  • 匹配:
    • 字符串:
      • PHP: (11)
        strstr, strchr, stristr, strpos, strrchr, stripos, mb_strpos,
        mb_strrpos, strrpos, strripos, substr
      • Perl: (3)
        index, rindex, substr
    • 正则表达式:
      • PHP: (6)
        ereg, eregi, mb_ereg, mb_eregi, preg_match, preg_match_all
      • Perl: (1)
        m//
  • 替换匹配部分:
    
        * PHP: (12)
        ereg_replace, eregi_replace, mb_ereg_replace, mb_eregi_replace,
        preg_replace, str_ireplace, str_replace, ltrim, rtrim, trim, nl2br
    
        * Perl: (1)
        s///
  • 连接到数据库:
    • PHP: (17 或更多)
          dbx_connect, fbsql_connect, ibase_connect, msql_connect, msql_pconnect,
          mssql_connect, mysql_connect, odbc_connect, pg_connect, pg_pconnect,
          sesam_connect, ifx_pconnect, ifx_connect, sqlite_open, sqlite_popen,
          mysqli_connect, mysqli_pconnect
    • Perl: (2)
          DBI->connect, DBI->connect_cached
  • 打开(文件、进程等):
    • PHP: (5)
          dio_open, fopen, proc_open, popen, gzopen[2]
    • Perl: (2)
          open, sysopen
  • 读取/接收:
    • PHP: (12)
          dio_read, fread, gzread[2], socket_read, socket_recv, socket_recvfrom,
          socket_recvmsg, readline, fgetc, fgets, stream_get_line, file
    • Perl: (5)
          read, readline, sysread, recv, getc
  • 打印/输出/写入:
    • PHP: (14)
      print, echo, printf, fprintf, vprintf, dio_write, fwrite, fputs,
      gzwrite[2], socket_send, socket_sendmsg, socket_sendto, socket_write,
      socket_writev
    • Perl: (5)
      print, printf, syswrite, send, write
  • 关闭:
    • PHP: (7)
      closelog, dio_close, fclose, gzclose[2], pclose, socket_close,
      proc_close
    • Perl: (1)
      close
  • 常用/工具:
    • PHP:
      array_combine, array_fill, array_flip, array_merge, list, range, count,
      create_function, strtr, pow, putenv, getenv, getmygid, getmypid, getmyuid
    • Perl:
      使用语法或者魔术变量

[1] 因为系统的LIST语法和DBI的占位符,显式转义常常是不需要的。

[2] 在Perl中是由PerlIO层来处理的。


  • Re^2: Is Perl a good career move?
    by Juerd, 2005

    • 依然没有命名空间
    • 没有闭包,甚至没有匿名函数
    • 没有良好的HTML分析器
    • 没有简单的MIME构建工具
    • 没有良好的WWW库
    • 没有 CPAN
    • 没有数组
    • 没什么用的逻辑操作符

  • Yaywoo!
    by Dave Brown, 2004

    • 使用system()无法避免(不安全的)shell
    • XY-problem
    • 大量的不同程序,但是,很多只是做了差不多的事情的变体
    • 第二参数和返回值毫无意义
    • 函数名的差劲拼写方式

  • Why PHP sucks
    by Edwin Martin, 2004
    ,中文翻译
    为什么PHP令人不爽(对于大型系统)

    • 不良的递归支持
    • PHP 不是线程安全的
    • PHP 由于商业原因而不健全
    • 没有命名空间
    • 非标准的日期格式化字符
    • 混乱的许可证
    • 不一致的函数命名规则
    • 魔法引用地狱

  • Perl vs. PHP - octo’s subjektiver Vergleich
    by Florian Forster, 2003 (German)

    • Perl 比 PHP 快很多
    • Perl 比 PHP 更丰富
    • Perl 比 PHP 有更好的文档
    • PHP 缺乏模块化支持
    • PHP的here-docs对Windows用户毫无用途
    • PHP 缺少一致的数据库API
    • PHP 缓存数据库查询结果很危险
    • 图形上,PHP实际上被限制在了 GD 中

  • I hate PHP
    by Keith Devens, 2003

    • 白痴似的——调用时不推荐引用传递

  • Experiences of Using PHP in Large Websites
    by Aaron Crane, 2002

    • PHP 是推荐把表现和业务逻辑结合起来的
    • 没有命名空间造成很多问题
    • php.ini 的全局配置
    • 过分简单化导致了额外的复杂度

  • PHP Annoyances
    by Neil de Carteret, 2002

    • 没有真正的引用或者指针
    • 毫无命名空间的概念
    • 毫不组件化
    • 想变成Perl,但事实上也没想变成Perl
    • 没有标准的DB接口
    • 所有的PHP社区都是针对非程序员的
    • 不支持链式方法调用 (现在已经不是了 –tnx.nl)
    • 没有全局变量除非通过导入
    • register_globals 和 $_REQUEST 都让人痛心
    • 数组都是哈西表
    • PEAR 并不是 CPAN
    • Arrays 不能内插值替换成字符串(如$a=array();$b="$a";是错误的)
    • 没有类似 “use strict” 用来检验变量名的功能

  • PHP: A love and hate relationship
    by Ivan Ristic, 2002

    • 社区令我不安
    • 知识渊博的人少之又少
    • Zend 发布的文章还建议不安全的实践方式

  • My list of PHP shortcomings
    by Nathan Torkington, 2001

    • 没有命名空间
    • 所有的函数都是全局的
    • 没有真正的引用
    • 没有真实的数据结构 (现在已经不是了 –tnx.nl)
    • 没有匿名函数

引用

EFnet #php:
19:45 <+Dragnslcr> Comparing PHP to Perl is like comparing pears to newspapers

Perl Monks: PHP - it’s “training wheels without the bike” — Randal L. Schwartz

Posted in PHP | No Comments »