显示标签为“programming”的博文。显示所有博文
显示标签为“programming”的博文。显示所有博文

2008年3月20日星期四

JavaScript上的闭包(closure): 第二种说法

而Martin Fowler对闭包的介绍却是另外一种解释: 闭包就是一段可作为参数传递的代码。从他提供的示例来看只是个匿名函数,跟嵌套函数没有什么关系,其他人也给出了C#PythonJavaScript上的例子,却跟上面的不一样。
  1. Array.prototype.select = function(func)
  2. {
  3. var ary = [];
  4. for (var i = 0; i < this.length; ++i)
  5. {
  6. if (func(this[i]))
  7. ary[ary.length] = this[i];
  8. }
  9. return ary;
  10. };
  11. function managers_noclosure(emps) {
  12. var result = [];
  13. for (var i = 0; i < class="delimiters1">)
  14. {
  15. var e = emps[i];
  16. if (e.isManager) result[result.length] = e;
  17. }
  18. return result;
  19. }
  20. function managers(emps)
  21. {
  22. return emps.select(function (e){ return e.isManager; });
  23. }

而现在嚷嚷得比较厉害的Java支持也是依照这种概念: http://www.infoq.com/cn/news/2007/09/catching-up-with-closures

按照这种定义,闭包非常类似于C语言中的函数指针、C++里面的模板函数、Java中的内嵌类,虽然Martin Fowler在他文章里也在说明闭包跟这些概念的不同时提到了运行环境的问题,但似乎没有什么说服力,至少从例子上是看不出来的。

个人感觉,前面一种说法是正确的,这种说法不大对。虽然Martin Fowler的名气很大。

JavaScript上的闭包(closure)

闭包这个概念随着并发、函数式编程的复兴,也热乎了起来。”跟上Java闭包(Closure)的步伐“一文中说:
Mark Jason Dominus在Perl概述中的话语作为开场:在未来的三十年中,人们将会取笑那些发明没有闭包特性的语言的人,如同他们现在正取笑那些发明没有递归这种特性的语言的人是一个道理。
  • 定义
那么这是个啥东西? Wikipedia上对closure的解释是:
In computer science, a closure is a function that is evaluated in an environment containing one or more bound variables. When called, the function can access these variables. The explicit use of closures is associated with functional programming and with languages such as ML and Lisp. Constructs such as objects in other languages can also be modeled with closures.

In some languages, a closure may occur when a function is defined within another function, and the inner function refers to local variables of the outer function. At runtime, when the outer function executes, a closure is formed, consisting of the inner function’s code and references to any variables of the outer function required by the closure.
这个说法比较抽象。还是来看看实际的例子吧。
  • 实例说明
JavaScript中,简单的说法是: In JavaScript, if you use the function keyword inside another function, you are creating a closure.(摘自Morris Johns的JavaScript Closures for Dummies, 中文翻译可看这里: 在JavaScript中,什么是闭包)。
  1. function sayHello(name) {
  2. var text = 'Hello ' + name; // local variable
  3. var sayAlert = function() { alert(text); }
  4. return sayAlert;
  5. }
  6. var say = sayHello('Bob');
  7. say();

注 意sayAlert是sayHello的一个嵌套函数,当var say = sayHello('Bob')这行执行完之后,因为say是对内部函数sayAlert的一个引用,所以这个函数对象不会被释放,而sayAlert的 执行环境(execution context)也不会被释放,一个函数与它的数据、执行环境绑在一起,这个sayHello就是一个闭包了。

这里有好几个概念: 嵌套函数,函数引用,作用域链(scope chain),执行环境。从目前看见的资料来看,闭包的构成必须有这几个要素。
  • 作用
这个东西有什么作用呢,先看如下一个例子:
  1. var f, g;
  2. function foo() {
  3. var x = 0;
  4. f = function() { return ++x; };
  5. g = function() { return --x; };
  6. x = 1;
  7. print(f()); // "2"
  8. }
  9. foo();
  10. print(g()); // "1"
  11. print(f()); // "2"
  • 两个不同功能、相互独立的函数只能通过全局变量交换数据么(这里说语言本身提供的能力,而不是平台提供的能力)? 上面例子中两个函数都操作了变量x,但x并不是全局变量,但对于其他函数也是不可见的。可见第一个作用是提供另一种手段来供两段程序交换数据,同时保护变 量安全
  • 两个闭包建立之后,只需要根据两个引用就可以调用了,这两个引用相互独立,但又是完全自包含的,——这显然是函数式编程的风格了

2007年9月10日星期一

回应: Linux之父炮轰C++:"糟糕程序员的垃圾语言"

涉及到编程语言之争的帖子,在CSDN上一定会吵得不可开交,这不:
"Linux之父Linus Torvalds为了Linux内核开发而专门打造的版本控制软件Git已经引起了业界的广泛关注。昨天,有一位Dmitry Kakurin老兄在查看了源代码之后,发现使用的是纯C而非C++,表示不可理解,他直言:“别拿可移植性说事儿,这是屁话(BS,bullshit)。“(此外,他还批评Git蛮力地直接操作文本,既啰嗦又易错,而且很难跟上高层代码逻辑。)这个BS引起了Torvalds的强烈反应,他用*YOU* are full of bullshit.“(你才满嘴屁话呢)作为自己反驳的开场白。接着,他先转向了对C++的罕见的火药味十足的炮轰..."
从几年的实践来看,我觉得从面向对象这个角度来看,C++很好,但C++这个东西太复杂,太容易导致错误,用上几个模版后,编译时搞出一大堆不知所云的信息也是常事。俺是觉得C++写的东西很容易变得不可维护,尤其是有C++"高手"加入以后。而且印象中用C++做出来的可重用的库比较少。

大 家都说“我们在没有掌握得好的时候,不妨只用我们了解的那些特性。那些高级特性并不妨碍,你可以不用啊”,但这都是从单独一个人的角度来说的。放到一个团 队来说,你的队伍中有C++高手,有新手,还有更多的半生不熟的人,怎么办?Linus的考虑也是从这个角度出发的,就算你Dmitry Kakurin很懂C++,不会出什么问题,能写出漂亮的代码,但其他人怎么维护你的代码?


不过俺没有帖子里面各位这么确信,对很多事物都抱着一种敬畏的态度,因为我不知道是不是本人不够聪明,没有认识到C++的各个方面。



刚 才我统计了一下sourceforge.net上用C++和C的项目数,以及各自的处于stable和mature阶段的项目数。


































total stable mature
C++ 22462 4326(19.26%) 427(1.90%)
C 19623 4792(24.42%) 615(3.13%)
Java 26454 5097(19.27%) 353(1.33%)
Python 7224 1287(17.81%) 109(1.5%)

从总数上看似乎C++更适合大家写东西,但从stable和mature项目比率上看似乎C项目更容易稳定和成熟,可维护性高些。

不过也许这不能说明什么,因为sf.net上只是些开源软件,上面各位又说C++在windows上应用普遍,而windows上素来没有多少开源传统。又或者商业软件又有不同。各位自己乱炖吧。

参考:

2006年10月1日星期日

C++类继承中的几个小问题

是的,我一直是个Delphi的粉丝。虽然现在工作上用的是C++(而且是在用VC/MFC),但一直都没有认真去学习它。前两天听俺们项目组的C ++高手讲解了一些“基本知识”,我还是觉得这个语言太复杂了,陷阱也太多。以下有几个例子(鄙人C++确很粗浅,如有不对的地方,敬请指正)。

[@more@]

第一个例子:

class Base
{
public:
virtual void MethodFoo(std::string s);
}
class Child: public Base
{
public:
virtual void MethodFoo(std::string s);
}
Base *b = new Child;
std::string s("something");
b.MethodFoo(str);

我 们会利用多态性来调用不同的MethodFoo实现,但突然有一天你觉得MethodFoo中间s是不应该发生变化的,于是加了一个const。问题来 了,很多编译器不会告诉你任何告警,但b.MethodFoo再也调用不到派生类的MethodFoo了,除非你把它们一个个都改过来。

第二个例子:

class Base
{
public:
virtual void MethodFoo(std::string s);
}

class Child: public Base
{
public:
virtual void MethodFoo(std::string s);
{
DoSometingMyself();
Base::MethodFoo(s);
}
}

突然有一天,你觉得这个设计不太合理,需要在Base和Child之间再插入一层(class Middle),这就意味着你得修改Child所有函数中类似上面黑体的部分,把它们改成Middle::MethodFoo什么的。

第三个例子:

我 们知道C++中支持多继承,但它对于多个基类中有同名同类型函数的问题没有提供解决方法(也许你会说,我们不应该写这样的代码,但仔细想想X, Y都是接口(C++用纯虚类来做)的情况下,难免会有重名的)。对于X, Y均派生自B,而C又牌证自X+Y这种”恐怖的菱形,它有一个"workaround"是用虚基类,但这要求更改X, Y的代码,使得它们虚拟继承自B。

-----------

是的,这三个问题在Delphi中均可以避免:

1. 要重新实现虚方法必须用override关键字;而带有override声明的方法如果参数不一致,编译器会报错;如果基类中不存在叫这个名字的方法,也会报错。而你确实要用同样的函数名做别的事情,得用reintroduce或者overload关键字。

2. Child::MethodFoo中调用其父类的MethodFoo时不用写Base::MethodFoo,直接写inherited MethodFoo就行了。

3. 采用方法分辨子句即可:

TChild = class(TParent, Interface1, Interface2)
procedure Interface1.MethodFoo = MethodFoo1;
procedure Interface2.MethodFoo = MethodFoo2;
procedure MethodFoo1(s: String);
procedure MethodFoo2(s: String);
end;

2006年8月6日星期日

Inno setup, 不错

俺现在用Delphi的时候少了,业余也不怎么玩它了,有时候有点手痒。

俺在公司赖以混饭吃的那个产品原来是用 InstallShield做安装程序的,一直用的是个老版本6.22,都好几年了,有很多功能做起来相当麻烦,很多新特性也不支持;加上IS公司又查的 紧,搞得想升级到新版本又被公司卡得紧;俺又不喜欢它一个工程要一大堆目录的风格(而且还不能随便挪目录)。于是就琢磨着换个免费的工具。

NSISInno Setup之间比较了半天之后,选择了Inno Setup,最开始的原因很简单: Inno是用Delphi写的,而且居然支持用Pascal写安装脚本(好像用的是RemObjects Pascal Script引擎)。

用 了一阵下来,感觉不错:

  • 用InstallShield没法自行调用函数注册COM组件(只好调用外部regsvr32:-(),这个问题在InnoSetup根本就不存在;
  • 用TStrings类可 以很容易地操作非ini类的文本文件,这样原来很多配置文件里面的选项都可以用安装程序来调整了;
  • 同样可以用CreateOLEObject来创建COM对象,并直接调用,俺们的XML配置文件就可以很好解决了;
  • 整个工程设置都在一个文件里面,用版本配置工具管理起来 也方便
  • ......

最重要的是,用Pascal写起代码来让我感觉得心应手,干嘛还要另外学习一个语法古怪的语言:-).

2006年6月11日星期日

C/C++的字符串处理效率...咳咳

Shlemiel得到一??在路上?T油漆的工作,他要漆在路中殓的殓?喾指艟?。第一天他拿了一罐油漆去漆好了300瘁的路。「做得真好!」他的老??真「你手倪真快啊!」然後就斤他一??泾板。
第二天Shlemiel只漆了150瘁。「呃?影。??]有昨天好,不咿也??是很快。150瘁也很了不起。」也斤他一??泾板。
第三天Shlemiel只漆了30瘁。「只有30瘁而已!」老??就哇哇大叫了。「呃??在是?o法接受!第一天你漆了十倍的樘度耶!究竟怎?N回事啊?」
「我也?]揠法啊,」Shlemiel真「我每隔一天就滕油漆罐愈?碛?哞啊!」

这人很傻是吧?但C/C++里面字符串处理函数strlen, strcat是怎样实现的呢?

Joel说(英文原文),这就是为什么一些系统一个目录下有上千个文件时就效率急剧下降的原因,也是为什么Excel后来采用了Pascal的字符串处理方式效率就提高了很多的原因。

2006年1月31日星期二

GUI程序的RAD和脚本化?

在我赖之混饭吃的那个项目上,这个大版本碰到一个比较棘手的问题, 那就是: 业务功能太多,虽然都不是很复杂,但总工作量却相当之多,加上项目组有2/3是新入职不到一年的同事,所以我们一直想把核心功能和业务功能分开来,后者用脚本来写。

但UI怎么办呢? 当然是要求有一些常用控件的,而且有事件响应能力,脚本中也要求能够操纵这些UI元素。基于俺个人多年的爱好,最好是能够同时编辑UI和事件处理代码(我认为没有这个功能就不能叫RAD:-)。


比较理想的是像Excel/Word那样用VBA(该项目是在Windows上用MFC开发的),但似乎这方面的例子和文档都比较少,也拿不到VBA的试用版来评估一下。

从目前看到的一些东西来看,Windows下比较接近我们的需求的是Script Builder, 它是基于Delphi的,可以直接使用ActiveX或者Delphi编写的package,支持ActiveScript(也就是说可以使用 VBScript, JScript, Python和Perl了)和DelphiScript。如果我们自己的项目是用Delphi开发的,把这个集成进去应该是件很酷的事情。(这个东西实际 是基于以前见过的Dream Scripter,只是UI表现上做得更加接近了我们“用脚本写程序”的想法而已。)

再看一些其他的GUI toolkit吧:

  • gtk有一个glade来编辑界面,然后我们可以在pygtk/python-glade或者gtk-perl/glade-perl来写响应代码,没法直接编辑界面代码(毕竟glade不是针对具体哪种语言的) P.S 刚刚在Planet Debian TW上看见yungyuc介绍一个新东东Gazpacho,说是"看起來是 Python+GTK 的 IDE",我还高兴了一阵,拉下来试试却发现只是一个改为完全用python写的一个glade而已。当然,我们可以以此为基础再来扩展。
  • wxWidgets有个wxGlade, 是模仿Glade的,不用多说。
  • Qt有Qt-Designer来编辑UI, 但这个东西也是跟glade差不多;同时它还有个Qt Script for Application,使得可以通过Qt Script(基于JavaScript)来操纵UI,不知道这两个东西是否可以接合,不过这已经很不错了。
  • SpecTcl是一个tk的界面设计器,不过它可以直接生成脚本(tcl甚至perl, python, ruby)
  • Java方面我用过JBuilder, 对于Borland的这种方式,用最近最红火的郭德纲的话说:“ 这个杭子,我做兴!”,简直" 要了亲命了"。只是因为java的烦琐,一个用JBuilder编写的略微复杂的界面,其创建UI部分的代码,基本上是不可手工维护的。至于Eclipse上UI设计器,还没玩过,不瞎说了。

说了这么些,有朋友会问,到底你们用的哪种?呵呵,记住我们的程序是用MFC写的,也没有买Qt这样的界面套件。最后我们用了用友华表的 Cell控件做模板来模拟UI,上面也可以放些按钮、下拉框什么的,虽然显得很不专业,但总算能对付过去。

想 想也知道为什么这种东西没有基于MFC的: 要在脚本中操纵UI控件(当然是说比较自然的方式,比如MainForm.Statubar1.SimpleText = "hello, world", 不是我见一些高手在tcl里面用c代码封装SendMessage来操纵的那种),这显然要求界面库有良好的RTTI,以便宿主程序能够很方便地将界面控 件对象加到脚本解释器当中去,否则脚本中怎么操纵它们。MFC有这些个嘛? ──除非完全自己手工来折腾。

补充:

1. 忘了gambas了,这个接近于Visual Basic,UI是用Qt写的。

2. mozilla的XUL应该是一个很不错的选择,而且传言很快将加入python支持,只是比较耗内存(似乎也没界面编辑器)。

2005年7月18日星期一

高版本pkg-config的问题

Briefly, on my cygwin, `pkg-config --libs gtk+-2.0' would output:

0.17: -Wl,--export-dynamic -lgtk-x11-2.0

0.15: -Wl,--export-dynamic -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangoxft-1.0 -lpangox-1.0 -lpango-1.0 -lgobject-2.0 -lgmodule-2.0 -lglib-2.0 -lintl -liconv

I think the latter is right. Perhaps cygwin port forgot to specify '--enable-indirect-deps' option while building.




这 几日在公司做设计,需要划UML图,于是就想用dia,装了个windows版本,谁知道老崩溃,于是想念起cygwin版本来。原来在cygnome2 已经做过移植,但跟现在cygwin自己的gtk2用了不同的prefix,就重新编译一下吧。 因为在公司空闲时间不多,于是就在家里先搞好了build script和cygwin patch打算然后重新在公司直接编译一遍。

谁 知道在家里弄好的patch到了公司就不用了,configure的时候报告gtk+-2.0不存在,检查config.log,发现是测试gtk+- 2.0时没有g_xxx等函数的定义,也就是说缺少-lglib-2.0。手工添加了一把,到了生成lib/libdia.la时还是出错,这回说是没有 gdk_xxx的定义。

觉得有问题,回头再查看config.log, 看到pkgconfig --libs gtk+-2.0的时候就只输出了-Wl,--export-dynamic -lgtk-x11-2.0. 怎么回事?gtk+-2.0依赖于gdk,glib等,它应该自动将它们的libs也提取出来啊,总不能让我在应用程序中自己来调用吧(比如我怎么知道该 添加gdk-x11还是gdk-win32,我怎么知道它有没有使用libintl?)

回到家一试验,果然家里的pkgconfig输出的 是正确的(有-lpango-1.0 -lglib-2.0.....)。但pkgconfig是个小程序,好像没有什么配置上的不同导致这个问题啊?难道是版本问题?一检查,家里用的还是 0.15版本,而公司里用的是较新的0.17。奇怪,老版本居然没问题,而新版本有。

2005年4月14日星期四

终极编程语言: C-omega (玩笑)

http://www.csdn.net/news/newstopic/20/20503.shtml:

Cω自去年7月份就要微软研究院的网页上公布出来,但C#的光芒完全把这门语言掩盖住了,近期在微软的官方站点上又刊登了一些有关Cω的文章,不知它是否会如当年C#的出现一样再次吸引住开发者的眼球,再次带来生产力的突破?
......

DarkSchneider:
其实,微软的“下一代”语言真正的含义是:“C oh my god!“ (2005.04.13)