Roar for mootools 1.4

早在天下大势还处在分久必合的时候,那时候mootools还有不少简单实用的小库,比如我今天搜索”mootools notification”就找到这个08年的库叫做Roar。不过遗憾的是从那以后,这个库就再也没有更新过了。

Mootools本身也沉寂了很久,这个项目恐怕也要思考自己未来的发展方向了。今年9月Mootools迈进了1.4,API上有一些变化。现在的下载页也能看到with/without backward compatibility的版本分开下载。为了用上Roar,我尝试了这两个版本发现都不能使用。最后downgrade到1.2可以确定Roar本身在当时是没有什么问题。

这么多年对mootools痴心不改,所以顺手维护了一下Roar,现在可以在1.4 without compatibility的发布下运行了。主要是几个小修改,大多是一些多年deprecated函数被正式删除:

  • Type常量,原先的String.type,Object.type现在统一到一个Type对象下,变成Type.isString和Type.isObject
  • $empty 常量被删除了,现在直接用function()或Function.from()代替
  • $pick 方法被Array.pick取代,参数现在也必须接受数组类型了
  • $merge 方法被Object.merge取代
  • $type 被typeOf取代
  • 函数对象的create方法被删除了,现在可以用函数对象的bind方法替代
  • Browser.Engine 被删除了,需要用其他Browser的API替代

修改后的Roar,放在这个gist里,测试过可以在firefox和chromium上健康使用。IE没有做测试。这个08年的库,眼看四年过去了,用起来依然不错。

作为mootools的铁杆,我还是会一直专一地坚守下去的。(于是,我也已经变成了多年前那些我眼中为旧事物顽抗到底的老家伙了)

TF101 101

上周末决定不再忍耐,又入了一个大件:华硕的平板,变形金刚。TF101上市已经半年了,而且现在TF102号称四核的版本已经开始接受预订了,所以差不多也到了应该出手的时候了。因为我知道如果去等102的话,我还会像现在这样地去和103做比较。

选择这款的主要原因即他的键盘配置,平板加键盘的组合彻底把上网本推进深渊。对于我这种还算是制造内容比率比较高的人来说,有个强有力的输入设备是必要的。目前市面上有这种搭配的只有transformer和think的,而且think似乎又只有配图和说明,没见真正卖那款键盘的。更严重的是,当然,think太贵了。

接下来开始说问题:
第一关叫做充电。TF101在充电方面有严重的缺陷。当电池电量极低时,会出现无法充电的情况。明明接着电源,可是电量提示一直是0%。如此情况下我整整充了一天拔下电源依然无法开机。最后看了网上的说法,在充电自动开机后关机了半个小时终于起死回生。

第二个小问题,键盘底座的平板本身电源是分离的,二者各自充电。所以在前面〝整整充了一天〞之前还有整整充了一晚上键盘。早晨起来键盘电满了,平板没充进去。

系统出厂是Android 3.0,这个系统的伟大之处在于从他一启动开始,就开始不断有程序报出错退出,不断有应用停止响应。他简直都对不起这个版本号。直到后来充上电升级到3.2之后才可以用。但是,还是有浏览器突然僵死然后突然从眼前消失的场面。再有就是机捆绑的什么人人网,电子书,开心网什么的,让你想不root都不行。

再有andrid 3.x上应用可能本来就不多,随机捆的又是一个流氓市场,除了满眼的流氓软件和山寨以外,就是版已经过时的软件。可怜这挺好一机器都不知道该装点什么。要说TF101硬件已经很可以了,只是配上这么个系统,用范伟的话说,白瞎你这个人了。手放键盘上,真恨不得能打开个终端来挡住这个浅薄的外观。等有时间我一定要尝试在这台机器上装个正经系统。也算是对得起这硬件了。

一句话概括一下的话,硬件还好,软件拉倒。

Finished my machine learning courses

经过三个月的时间,终于看完了ml-class的所有视频课程,完成了所有review questions,提交了所有programming exercises.感觉不错,之前一直对数据挖掘相关的方面感兴趣,回想一下大学时候一些地统计分析甚至遥感图像数据处理的课都跟机器学习相关,但是毕竟不是这方面的课程,所以介绍的不是很系统。今年秋天斯坦福推出这个在线课程,机器学习作为其中之一真算是弥补了我们民间科学爱好者的遗憾了。

这个课程在有限的篇幅里涵盖了linear regression, logistic regression, ANN, SVM, PCA, K-Means, Anomaly Detection等等知识,基本上算是一个完整实用的导论。Andrew Ng教授的讲解也算是通俗易懂深入浅出,完全感觉不到什么门槛。

对于online course这种形式,今年秋天斯坦福的人工智能、数据库、机器学习也算是首开先河,目前这三门课程都已经结束,网上的反响非常强烈。好消息是明年Q1斯坦福还有更多数量更多方向的课程。今天MIT也宣布了明年的online course计划,他们也将加入提供在线课程的行列。而且,MIT的在线课程还会颁发一个名叫MITx的certification。开放式课程已经成为大势所趋,信息本应自由传播。

对于对机器学习感兴趣的朋友,除了ml-class.org上的资源,你还可以在academic earth上找到ANG教授的授课视频。这套视频涵盖的内容比ml-class上的更详细完整:
http://academicearth.org/courses/machine-learning

课程结束,我在ml-class上所有的编程作业都已经放在bitbucket上,如果有兴趣可以参考这些octave程序:
https://bitbucket.org/sunng/ml-class

明年一月斯坦福还会开放更多跟机器学习相关的课程,包括:

Thank you, Professor Ng and your team for this well-prepared, high-quality online course.

Extend slacker server with interceptors

An interceptor framework was introduced in slacker 0.3.0. It’s designed to allow user to add custom functionality without hacking into the internal of slacker.

Like many server frameworks, slacker abstracts the request processing as a pipeline. The request object is modified by adding or updating attributes through each node of the pipeline. So it’s easy to add your interceptor into the pipeline, with which you can get the data before and after function executed.

To create such an interceptor, you should use the slacker.interceptor/definterceptor macro and slacker.interceptor/definterceptor+ macro:

(definterceptor name
:before interceptor-function
:after interceptor-function)

(definterceptor+ name [arguments]
:before interceptor-function
:after interceptor-function)

definterceptor+ can accept arguments so you can configure the interceptor when you use it.

See a simple example:

(definterceptor log-function-call
  :before (fn [req] (println (str "calling " (:fname req))) req))

(definterceptor+ log-function-call-prefixed [prefix]
  :before (fn [req] (println (str
                               (if (fn? prefix) (prefix) prefix)
                               " calling "
                               (:fname req)))
                    req))

Then, add it to your slacker server by

(use '[slacker.interceptor])
(import '[java.util Date])
(start-slacker (the-ns 'slapi) 2104
  :interceptors (interceptors log-function-call
                              (log-function-call-prefixed
                                (fn [] (.toString (Date.)))))

Now you can log every function call of your slacker server.

For more detail about the interceptor framework, especially the request data, please check the wiki page.

In slacker 0.3.0, there is a built-in interceptor to stats function calls. You can find it at slacker.interceptors.stats. The stats data is expose via JMX. You can also write monitoring application to retrieve the data.

And there will be more built-in interceptors in 0.4.0, includes function call time stats and logging.

使用Clojure Thread Macro的心得

Thread Macro是clojure里一个很强大的宏,他帮助你简化嵌套函数的调用,比如

(str (inc (count [:a :b])))

就可以利用thread macro简写成

(-> [:a :b] count inc str)

->>和->类似,区别在于->>把值作为函数的最后一个参数传入。

简单的功能介绍完了,接下来就遇到问题了。我需要功能,能够接受一个或多个函数,然后把这些函数组成一个pipeline。这时很自然想到->是一个好帮手。也许只需要一个类似这样的form就可以了: #(apply -> % [funcs])。结果失败了,因为->是个宏,所以根本不能用apply。于是想到有apply-macro吗?有,或者说曾经有过。在contrib中曾经有一个apply-macro,不过被强烈不推荐使用。到这里,这条路堵死了,惟一的办法就是把->放到API之外,放到用户代码里去。

放到用户代码里,你需要写一个详细的说明文档并且告诉用户他必须这么做。然而在clojure世界里有一个更好的办法就是再写一个宏把->包装起来。这么做看似多此一举,其实是保持了API的一致性。通过宏,我们可以把自己的API延伸到用户代码中去。或者说,通过一个类似DSL的宏,给一些并不优雅的API一个缓冲,也为API日后的演化留下空间。

这里还要扯开一句关于宏的开发。clojure中所谓code is data,主要就是体现在宏里。原本在多数其他语言里,宏是不能求值的。但是在clojure里,由于code is data的缘故,宏是可以求值的。所有的输入数据都是list,你可以做first/reverse这样的操作。但是有一点要注意的是,宏中求得的值和代码里的值是不一样的。例如{:a inc}这样一个字面量,在宏里是可以通过:a做求值的,然后这里得到的并非一个函数(function),而是一个符号(symbol)。再者,调试宏的时候你可能会被这样的结果困惑:

(defmacro a [f] (println (:a f)))
(a {:a 1}) ; ==> prints 1
(def b {:a 1})
(a b) ; ==> prints nil

字面量可以,同值的变量就不行了。原因还是那句,宏里不能求值。

继续谈->。这个宏其实远没有你想象的那么驯服。遇到复杂一点的情况:

(def m {:a inc})
(-> 2 (get m :a) str)

这个写法对吗?str是个函数,(get m :a)返回的是inc也是个函数,貌似正确。运行之后却报错get的参数数量错误。所以千万不要忘了->是个宏,(get m :a)这里是不会求值到inc的,直接作为一个list被宏吞下去。在宏里只能通过符号的组合变化来生成代码,那么一不小心,就没有inc什么事了。

于是,你可能想到这里需要一个确切的函数,就好比这样:

(def m {:a inc})
(-> 2 (fn [x] ((get m :a) x)))

也许这样就好多了,我们放了一个匿名函数,并不要求宏去求值,因为这个匿名函数会被宏生成到新的代码里。里面的get也会在运行时求值。看似没什么问题,可是一运行还是没有期待的结果,居然返回了一个匿名函数!而对这个匿名函数求值得到的也是一个错误的结果!简直有点无厘头了。

呵呵,不故弄玄虚了。我们用macroexpand看看发生了什么。

这是用匿名函数包装以前

(macroexpand-1 '(-> 2 (get m :a)))
(get 2 m :a)

->居然只是简单地把2放到了get这个form里面!

再看看用匿名函数包装后的结果

(macroexpand-1 '(-> 2 (fn [x] ((get m :a) x))))
(fn 2 [x] ((get m :a) x))

和刚才一样,2被放到了第一个form的第一个参数位置!得到的是一个非法的form。

那么既然->只是简单地把第一个参数放到后面form的首个参数的位置,那么这个宏正确的使用方法其实是

(def m {:a inc})
(-> 2 ((fn [x] ((get m :a) x))))

再加一层括号!

(macroexpand-1 '(-> 2 ((fn [x] ((get m :a) x)))))
((fn [x] ((get m :a) x)) 2)

可见,->虽然是个功能强大的宏,但宏终归只是宏,和函数的区别是明显的。在使用的时候,不能完全按照函数的习惯。

如果你想了解实际的代码,可以参考slacker 0.3.0里的这个interceptor框架:
https://github.com/sunng87/slacker/blob/master/src/slacker/interceptor.clj
上面提到的难处,多半也都是在开发这个框架时亲身经历的。