KEEP K.I.S.S.

tk's blog

我要学的最后一门编程语言:LISP (Clojure)

转载

来源:外刊IT评论

我开发软件。事实上,我的整个职业生涯都是从事软件开发。早在互联网时代之前,在那次泡沫之前,那次泡沫之前的泡沫之前,我就在波士顿的一家小公司里的一份软件开发的岗位上苦苦奋斗,我们做多协议路由器。在那份工作中,我从做测试干起,是一个“质管员”,感谢这个公司的技术创始人的榜样,他努力的工作,在Cayman Systems中作为一个特殊的开发人员给予我们指导,使我找到了驱动我之后二十年的人生招唤:软件开发。

 

这第一份工作不仅仅塑造了我的职业内容,同时,在Cayman,我遭遇了可怕的创业诱惑的冲击。第一次,我感觉到了一个公司创业的成功带来的直接、美好、让人紧张颤栗的强烈感觉。我感觉到了那种自己掌舵的愉悦,尽管只是部分的。这次跟自由与责任的第一次的亲密接触让我胃口大开,我从此走上了创业之路。

我在Cayman Systems的导师给了我两条谏言,它们一直陪伴我至今:

  • 当你想开办一个公司时,先从顾问做起。(看,他已经知道我入魔了。)从你的客户那里学习业务和开发,获得报酬的同时,利用他们的专家和资金,最终将其全部转化到你的产品里。
  • 学习LISP。

我很不擅长听取建议,所以这两样我一个都没做。

十年后(几乎也是这段时间),我开始研究Ruby。当我发现Ruby时,一种高兴的,一种长出一口气,一种兴奋的感觉充满着我。它俘获了我,不仅仅从技术上,在之后的一年的学习这种语言中,我编写软件的方式、我解决问题的模式改变了。我可以用一种最简单的,但毫无疑问的语句描述这种奇异的感觉:“编程太有意思了!”。

经过了多年对这种体验的深刻的反思,我对Paul Graham的话更深信不疑了:

“编程语言不仅仅是一种技术,它更是一种思维习惯。” – http://www.paulgraham.com/avg.html

通过对其它语言的学习,我无意中学会了新的、更高效的思维习惯。Ruby使我成为一个更优秀的程序员。

回顾往事,我发现有些地方做的很英明,并不是有意识的,就像是我早就知道了那句名言——早在它清晰的指导我行为处事之前。因为,你看,我第一眼看到Java,我就知道我讨厌它。我从来没有用Java写过一行代码,不会拿它来挣钱,更不会把它当作兴趣。我想我清楚的知道,深深的知道,它会改变我,使我成为一个“编码器”。甚至更糟。

纽约时报推荐了一篇Guy Deutscher写的标题为“语言如何改变你的思维”的文章,文章先从回顾开始,然后谈到了在这个问题上的最新研究发现。

“50多年前,著名的语言学家Roman Jakobson用一句简单的名言指明了语言之间最关键的不同之处:‘语言之间的不同本质在于它们能够传达的信息,而不是它们不能传达的信息。’这句名言告诉了我们解放我们母语潜能的关键所在:不同的语言用不同的方式影响我们的大脑思维,这并不是因为这种语言可以让我们这样思考问题,而是它在强迫你这样思考。”

“当你的语言日常的强迫你去使用某些类型的信息进行说明,这会迫使你去留意那些使用其它种语言的人平时不会注意的某些细节和体验。因为这种说法的习惯是从小养成的,这种习惯已经成为大脑思维的习惯,超越了语言本身,直接影响了你的言行,感知,联想,感觉,记忆,以及世界观。”

基本上我不是一个办事果断的人,但年初我决定要给自己充充电,我誓言要再学一门新的语言。我知道,我现在是CTO,这样做会使我“失身份”,但我认为,我的能力就体现在能在技术界和商务界的贯通,我必须坚定的深度的同时浸润着这两个领域。我听说越来越多的语言开始利用JVM,所以我想Scala也许会是一个不错的选择。我不想用无聊的细节打搅你,但这种语言确实没有符合我的预期。

六个星期之前,我发现了Cloure。现在,二十年来的今天,我懊悔不已,我发现导师的建议不该被当作耳边风,那是个极其重要的建议。

我们知道,Clojure是LISP语言的一种方言。而事实证明,LISP并不是一种语言。LISP是一种思维方式。

Yield in Ruby

# 这是一篇转载的文章,From http://fairleads.blogspot.com/2007/06/ruby-yield.html

Yield 是 Ruby 里面一个很独特的关键字,这篇文章可以帮助你很好的理解她。


 

TUESDAY, JUNE 5, 2007


Ruby Yield

 

 

Yield is one of the most powerful concepts implemented in the Ruby programming language. Yield lets you branch from within a method and execute some external code, then return to the original code in the first method.
 

What's so special about yield in Ruby? You could get the same branch and return flow by just executing a method call in java, or a function call in C. 
 

Heck, COBOL's PERFORM verb implements branch and return behavior. So what's up with yield?
 

Well, the behavior of what you branch to is set in stone with all those other techniques, but is undetermined until coded in RubyYield is kind of like a place holder that says "I'm going to branch off and do something here, but I don't know what I'm going to do yet".
 

Maybe some examples will help make it more clear. Here is a Ruby method with some yields coded in it, then the code to invoke the method and the block of code to invoke when the yield is encountered:

 

def block1
 puts "Start of block1..."
 yield
 yield
 puts "End of block1."
end

block1 {puts "yielding..."}

Running this code would produce the following output:

 

Start of block1...
yielding...
yielding...
End of block1.

So block1 executes it's first line and prints "Start of block1...", then it executes the yields which branch to the block of code outside block1 but associated with it at run time, then comes back and prints "End of block1."

OK that's, fine but isn't that functionally the same as the following code:

 

def block2
 puts "Start of block2..."
 put_it("in put_it called method...")
 put_it("still in put_it called method...")
 puts "End of block2."
end

def put_it(text)
 puts text
end

block2

Running this second set of code produces the following:

 

Start of block2...
in put_it called method...
still in put_it called method...
End of block2.

For the most part, these are pretty much the same. However without having to redefine any methods I could immediately execute the block1 method with a different block associated with it. The yield would branch and execute this newly associated logic:

 

block1 {require 'time' 
 puts Time.now.httpdate}

This produces the output:

 

Start of block1...
Tue, 05 Jun 2007 19:20:35 GMT
Tue, 05 Jun 2007 19:20:35 GMT
End of block1.

The hard coded function call in block2 forever ties the branching logic to the put_it function. To get different behavior you would need to either change the function call in block2 to call a different function, or change the behavior of the function put_it. This second way is brittle because it could break code elsewhere that calls put_it. The first is cumbersome and possibly brittle.

Ruby's yield is a wonderful construct.

What about when you don't want to branch, but want the other functionality in the block1 code to be executed? Can you just run the code without the associated block?

Let's see what happens. Running block1 without an attached block for the yield to associate to:

block1

This produces the result:

Start of block1...
LocalJumpError: no block given
from :3:in 'block1'
from :7
from :0

We get a 'no block given' error at line 3 in method 'block1'. Line 3 is our first yield in the block1 method. 

What to do?

We can wrap the yield in an if statement and check for the presence of a block using the block_given? conditional. It returns true if a block was given with the method call, or false if not.

For this example I'll use a slightly more idiomatic way of checking for the presence of a block in ruby:

 

def block3
 puts "Start of block3..."
 yield if block_given?
 puts "End of block3."
end

now if we run the code with a block:

 

block3 { puts "yielding" }

or without:

block3

It works both times producing:

Start of block3...
yielding...
End of block3.

and

Start of block3...
End of block3.

This will make your methods less brittle and more agile. 

Ruby's yield is worth getting to know. You can learn more here and here. Many of Ruby's built in methods contain yields that allow you to associate blocks with them producing added functionality at run time.

 

 

如奏钢琴

前几天看了一部日剧,《交响情人梦》,算是很长时间没看到这么精彩的日剧了。上一部应该是《父女七日变》,都在此小小的推荐一下。

看完之后的明显感受是想去摸钢琴,虽然自己对钢琴和音律是一窍不通,音乐的感染力真是非凡。反观自己现在的人生,能跟钢琴有点关系的应该就是手下这键盘了。

但是代码似乎没有琴声那么直观,可共鸣的也少,能在代码里看到美的就更少了,感觉上,写代码的大部分是痛苦的,看代码的大多会比写代码的更痛苦吧,尤其是在维护别人代码的时候。

这些天也在学习Ruby,主要是体会一下快乐编程之道,Ruby写起来还是很舒服的,加上自己用 Emacs,感受上很不错。果然写代码不仅仅只去体会完成后的快感,最好还是能在“创作”中也体会到美的感觉,而不只是枯燥,这样的人生,才有意思。

写代码,也应该如奏钢琴。

Qt Creator 2.1.0

之前一直都是直接下 Qt windows sdk 集成开发环境,这个最新的是带着 Qt 4.7.0 和 Qt Cretor 2.0.1。

最新的Qt 库是4.7.2,Qt Cretor 是 2.1.0,但是没有提供组合的 sdk集成环境包下载。

新的 2.1.0的Qt Creator 更新比较大,界面支持全中文显示,插件也有新的,支持类视图浏览了,不用再单独去下第三方的cppsupport了,代码显示上也有更新,我发现的就是虚函数显示会是斜体,具体应该是高亮显示更加细化了。

Qt Creator 真是越用越好用了。

Qt编程中的一个注意事项

Qt GUI编程里,默认根据ui文件生成的窗口部件的构造函数里,大体是这样的:

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
}

ui对应的就是主窗口容器(个人理解),如果在这个容器之中还有其他元素的话,比如说 tableView、editBox,如果也在构造函数里对它们进行初始化,就应该注意到,初始化的操作代码应该需要写到 ui->setupUi(this) 这一句之后,个人理解是,在这句之前窗口对象上的部件还没有成型,因此不能对它们进行相应操作。给个样例代码:

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this); // ui初始化

    model = new QSqlTableModel(this);
    model->setTable("student");
    model->setEditStrategy(QSqlTableModel::OnManualSubmit);
    model->select();

    ui->tableView->setModel(model); // 这一句是对ui上部件tableView的操作
}

如果将 ui->tableView->setModel(model) 这一句放到 ui->setupUi(this) 之前的话,程序运行就会出错,调试会显示 Segmentation Fault, 段错误,就是内存访问错误。因为在ui->setupUi(this) 之前, tableView 这个控件还没有得到初始化,还不存在,而 ui->tableView 这样的操作就没有意义了,自然会发生运行时错误。

所以,有关窗口部件的初始化操作如果要放到主窗口的构造函数里的话,一定要把代码放到 setupUi(this) 之后。