<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6121741363511450985</id><updated>2024-08-28T18:43:33.631-07:00</updated><category term="Clojure"/><category term="Java"/><category term="functional programming"/><title type='text'>YD Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>15</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-3710373212684244708</id><published>2010-11-18T06:58:00.000-08:00</published><updated>2010-11-18T06:59:42.811-08:00</updated><title type='text'>blogger 不靠谱，页面直接乱掉了</title><content type='html'>喵的，这上面的博文全部废了……杀回到 javaeye 吧，喵的……</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/3710373212684244708/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/3710373212684244708' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/3710373212684244708'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/3710373212684244708'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/11/blogger.html' title='blogger 不靠谱，页面直接乱掉了'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-7673337722990772034</id><published>2010-11-03T05:03:00.000-07:00</published><updated>2010-11-03T08:01:02.984-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Clojure"/><title type='text'>Clojure Stream</title><content type='html'>&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(define fibs&lt;br /&gt;  (stream-cons 0&lt;br /&gt;    (stream-cons 1&lt;br /&gt;      (stream-add (stream-cdr fibs) fibs))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The code above is an excerpt from &lt;i&gt;SICP&lt;/i&gt; that generates an infinite Fibonacci sequence. I&#39;ve tried to migrate it to Clojure.&lt;br /&gt;&lt;br /&gt;The following code is the implementations of the stream concept mentioned in &lt;i&gt;SICP&lt;/i&gt; and the the Fibonacci function. The essential part is this: &lt;span class=&quot;code&quot;&gt;(defmacro stream-cons [e s] ...)&lt;/span&gt;. Because Clojure&#39;s &lt;span class=&quot;code&quot;&gt;cons&lt;/span&gt; requires that &lt;span class=&quot;code&quot;&gt;s&lt;/span&gt; to be a &lt;span class=&quot;code&quot;&gt;ISeq&lt;/span&gt; or &lt;span class=&quot;code&quot;&gt;Seqable&lt;/span&gt;, that makes the &quot;&lt;span class=&quot;code&quot;&gt;(stream-cdr fibs)&lt;/span&gt;&quot; part always fails. Because this piece of code is executed before the outer &lt;span class=&quot;code&quot;&gt;stream-cons&lt;/span&gt; ever happened. By the time we execute &quot;&lt;span class=&quot;code&quot;&gt;(stream-cdr fibs)&lt;/span&gt;&quot;, we haven&#39;t had the value of &quot;fibs&quot; yet.&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn pair [x y] (cons x [y]))&lt;br /&gt;  &lt;br /&gt;(defn pair-car [p] (first p))&lt;br /&gt;&lt;br /&gt;(defn pair-cdr [p] (second p))&lt;br /&gt;&lt;br /&gt;(defmacro stream-cons [e s]&lt;br /&gt;  `(pair ~e (delay ~s)))&lt;br /&gt;  &lt;br /&gt;(defn stream-car [s]&lt;br /&gt;  (pair-car s))&lt;br /&gt;  &lt;br /&gt;(defn stream-cdr [s]&lt;br /&gt;  (force (pair-cdr s)))&lt;br /&gt;  &lt;br /&gt;(defn stream-map&lt;br /&gt;  ([f s]&lt;br /&gt;    (stream-cons&lt;br /&gt;      (f (stream-car s))&lt;br /&gt;      (stream-map f (stream-cdr s))))&lt;br /&gt;  ([f s &amp; ss]&lt;br /&gt;    (let [step (fn step [ss]&lt;br /&gt;                  (when (not (empty? ss))&lt;br /&gt;                    (stream-cons (map stream-car ss) (step (map stream-cdr ss)))))]&lt;br /&gt;      (stream-map #(apply f %) (step (cons s ss))))))&lt;br /&gt;    &lt;br /&gt;(defn stream-add [&amp; ss]&lt;br /&gt;  (apply stream-map + ss))&lt;br /&gt;  &lt;br /&gt;(defn take-stream [s count]&lt;br /&gt;  (when (&gt; count 0)&lt;br /&gt;    (stream-cons&lt;br /&gt;      (stream-car s)&lt;br /&gt;      (take-stream (stream-cdr s) (dec count)))))&lt;br /&gt;      &lt;br /&gt;(defn print-stream [s]&lt;br /&gt;  (let [step (fn step [s]&lt;br /&gt;                (when (not (empty? s))&lt;br /&gt;                  (print (stream-car s) &quot;&quot;)&lt;br /&gt;                  (recur (stream-cdr s))))]&lt;br /&gt;    (step s)&lt;br /&gt;    (println)))&lt;br /&gt;    &lt;br /&gt;(def fibs&lt;br /&gt;  (stream-cons 0&lt;br /&gt;    (stream-cons 1&lt;br /&gt;      (stream-add (stream-cdr fibs) fibs))))&lt;br /&gt;      &lt;br /&gt;(def ones (stream-cons 1 ones))&lt;br /&gt;(def twos (stream-add ones ones))&lt;br /&gt;&lt;br /&gt;(print-stream (take-stream twos 10)) ; =&gt; 2 2 2 2 2 2 2 2 2 2&lt;br /&gt;(print-stream (take-stream fibs 10)) ; =&gt; 0 1 1 2 3 5 8 13 21 34&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Interesting enough that I haven&#39;t thought of a way to seamlessly integrate the above stream code into Clojure. If we implement it using &lt;span class=&quot;code&quot;&gt;(deftype LazyStream [] clojure.lang.Seq ...)&lt;/span&gt; that would make &lt;span class=&quot;code&quot;&gt;cons&lt;/span&gt; a &lt;i&gt;function&lt;/i&gt; instead of a macro. Therefore I temporarily think that it&#39;s impossible to seamlessly integrate the implementation.</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/7673337722990772034/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/7673337722990772034' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/7673337722990772034'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/7673337722990772034'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/11/clojure-stream.html' title='Clojure Stream'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-126162372595378619</id><published>2010-10-31T06:43:00.000-07:00</published><updated>2010-10-31T06:44:22.556-07:00</updated><title type='text'>Definitions of Imperative &amp; Functional Programming from SICP</title><content type='html'>Programming without any use of assignments is accordingly known as functional programming.&lt;br /&gt;&lt;br /&gt;In contrast to functional programming, programming that makes extensive use of assignment is known as imperative programming.</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/126162372595378619/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/126162372595378619' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/126162372595378619'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/126162372595378619'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/10/definitions-of-imperative-functional.html' title='Definitions of Imperative &amp; Functional Programming from SICP'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-2605967783274423868</id><published>2010-10-30T05:06:00.000-07:00</published><updated>2010-10-30T05:31:27.348-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Clojure"/><title type='text'>求对数</title><content type='html'>下面是求以 1.12 为底，2.7 的对数。本方法极其低效，仅仅标记一下以备以后参考。&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(ns t&lt;br /&gt;  (:require [clojure.contrib.generic.math-functions :as math]))&lt;br /&gt;&lt;br /&gt;(defn abs [x]&lt;br /&gt;  (if (&lt; x 0) (* -1 x) x))&lt;br /&gt;&lt;br /&gt;(defn close-enough? [delta]&lt;br /&gt;  #(&lt; (abs (- %1 %2)) delta))&lt;br /&gt;  &lt;br /&gt;(defn calc-log [lg lg-level acc-stack]&lt;br /&gt;  (let [acc (math/pow 1.12 lg)&lt;br /&gt;        close? (close-enough? 0.00000000000000000001M)&lt;br /&gt;        lg-inc (/ 1 (math/pow 10 lg-level))&lt;br /&gt;        finer-lg-inc (/ 1 (math/pow 10 (inc lg-level)))]&lt;br /&gt;    (if (close? acc 2.7)&lt;br /&gt;      lg&lt;br /&gt;      (if (&gt; acc 2.7)&lt;br /&gt;        (recur (+ (- lg lg-inc) finer-lg-inc) (inc lg-level) acc-stack)&lt;br /&gt;        (recur (+ lg lg-inc) lg-level (conj acc-stack acc))))))&lt;br /&gt;      &lt;br /&gt;(println (calc-log 1 0 []))&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/2605967783274423868/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/2605967783274423868' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/2605967783274423868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/2605967783274423868'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/10/blog-post_30.html' title='求对数'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-2126062252813189139</id><published>2010-10-26T08:40:00.000-07:00</published><updated>2010-10-26T18:36:06.477-07:00</updated><title type='text'>空间</title><content type='html'>《Structure and Interpretation of Computer Programs》中提到，“The ability to create pairs whose elements are pairs is the essence of list structure&#39;s importance as a representational tool. We refer to this ability as the closure property of cons.”这话表面上是说 cons 作为 LISP 的核心，能够把自己产生的 pair 作为 cons 的参数进而去产生新的组合 pair，是列表数据结构在表示上非常本质的东西。“closure property”我倾向于翻译成封闭性。因为在代数中，给定 f(x) = y，如果任意 x 属于 Z，且 y 也属于 Z，那么 Z 就对 f 是封闭的。&lt;br /&gt;&lt;br /&gt;当然我们可以很容易地想到高中数学里面的列表。这跟 LISP 中的列表是完全对应的。正是这种特性造就了 LISP 语言的强大。但是这种模式对于即使不熟悉数学的程序员来说也应该似曾相识。比如 JUnit4 和 JMock 里面用的 Hamcrest Matcher。你可以任意组合各种 matcher 达到想要的效果。比如 assertThat(result, is(not(greaterThan(10)))。它使得 JUnit 摆脱了不停地往 Assert 类里面增加方法的局面。也是为什么 JUnit4 要绑定 Hamcrest Matcher 发布的原因。如今你很难看到一个优秀的测试框架里面不用 Hamcrest Matcher 的。Matcher 的模式就是：makeMatcher(someMather) === AnotherMatcher。也就是说，从逻辑上，makeMatcher 和 Matcher 这个抽象集合组成了一个代数空间。&lt;br /&gt;&lt;br /&gt;找到这样的空间，我们就可能找到了非常强大而优雅的工具。比如 SICP 里面提到的 painter。我们可以生成一个给定 painter 的水平镜像 painter、垂直镜像 painter、扭曲 painter，等等。这点对于我们做面向对象设计的时候也极有启发价值。想想 Hamcreset Matcher……</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/2126062252813189139/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/2126062252813189139' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/2126062252813189139'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/2126062252813189139'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/10/blog-post.html' title='空间'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-8015819876368652036</id><published>2010-08-30T04:08:00.000-07:00</published><updated>2010-09-01T09:48:41.316-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Clojure"/><title type='text'>快速排序</title><content type='html'>看了《The Joy of Clojure》的快速排序，觉得比较丑，而且不通用。所以自己又写了一个。不过有一个比不上书里面的。书里面的支持 lazy-seq，而下面这个不支持。可能正是因为要支持 lazy-seq 所以书里面的那个才会那么繁琐吧……&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn- split-by [pred coll]&lt;br /&gt;  [(filter pred coll) (remove pred coll)])&lt;br /&gt;&lt;br /&gt;(defn qsort-by [comparator work]&lt;br /&gt;  (letfn [(step [ret [pivot &amp; rst :as work]]&lt;br /&gt;            (if-not work&lt;br /&gt;              ret&lt;br /&gt;              (let [[left right] (split-by #(&lt; (comparator % pivot) 0) rst)]&lt;br /&gt;                (if (seq left)&lt;br /&gt;                  (recur ret (concat left [pivot] right))&lt;br /&gt;                  (recur (conj ret pivot) rst)))))]&lt;br /&gt;    (seq (step [] (seq work)))))&lt;br /&gt;&lt;br /&gt;(println (qsort-by - [57 66 72 27 16]))&lt;br /&gt;(println (qsort-by - []))&lt;br /&gt;(println (qsort-by - nil))&lt;br /&gt;&lt;br /&gt;;=&gt; (16 27 57 66 72)&lt;br /&gt;;=&gt; nil&lt;br /&gt;;=&gt; nil&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;快速排序（升序）我理解为：给定一个轴（pivot)，遍历所有还没有排序完整的元素（rst），把小于轴的元素放到轴的左边（left），把不小于轴的元素放到轴的右边（right）。完全排序好的元素放到结果集（ret）里。如果左边有元素，说明找到了比轴还要小的元素。那么结果集不动，因为还没有找到最小的元素。如果左边没有元素，说明当前的轴就是最小的，把它加入结果集。&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;code&quot;&gt;split-by&lt;/span&gt; 不同于 &lt;span class=&quot;code&quot;&gt;split-with&lt;/span&gt;。&lt;span class=&quot;code&quot;&gt;split-with&lt;/span&gt; 在遇到第一个不满足条件的元素后就终止了。而 &lt;span class=&quot;code&quot;&gt;split-by&lt;/span&gt; 会把整个集合都遍历一遍（这里的实现是遍历了两遍）。&lt;br /&gt;&lt;br /&gt;如果需要排序，请用 Clojure 核心库的 sort 函数。这个函数转调 java.util.Arrays.sort 函数，它是经过高度优化的。我的测试表明我的函数比标准库的慢了两三个数量级…… -_-!</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/8015819876368652036/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/8015819876368652036' title='4 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/8015819876368652036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/8015819876368652036'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/08/blog-post.html' title='快速排序'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-8824537062693802033</id><published>2010-08-25T08:02:00.000-07:00</published><updated>2010-08-27T09:30:21.200-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Clojure"/><title type='text'>Named arguments in Clojure</title><content type='html'>Clojure doesn&#39;t provide direct support for named arguments, which are supported under some popular dynamic languages like Ruby, Python. The following Python code was exerpted from `The Joy of Clojure&#39;:&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;def slope(p1=(0,0), p2=(1,1)):&lt;br /&gt;return (float(p2[1] - p1[1])) / (p2[0] - p1[0])&lt;br /&gt;slope((4,15), (3,21))&lt;br /&gt;#=&gt; -6.0&lt;br /&gt;slope(p2=(2,1))&lt;br /&gt;#=&gt; 0.5&lt;br /&gt;slope()&lt;br /&gt;#=&gt; 1.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It calculates the slope of a line, which can be determined by two given points. The following Clojure code mimics the named arguments:&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn slope&lt;br /&gt;([] (slope [0 0] [1 1]))&lt;br /&gt;([{:keys [p1 p2] :or {p1 [0 0] p2 [1 1]}}] (slope p1 p2))&lt;br /&gt;([p1 p2]&lt;br /&gt;(/ (- (float (p2 1)) (p1 1)) (- (p2 0) (p1 0)))))&lt;br /&gt;(slope [4 15] [3 21])&lt;br /&gt;;=&gt; -6.0&lt;br /&gt;(slope {:p2 [2 1]})&lt;br /&gt;;=&gt; 0.5&lt;br /&gt;(slope)&lt;br /&gt;;=&gt; 1.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I think it&#39;s sufficient for most of the cases. However, if you want to go deeper, let&#39;s use the defnk from clojure.contrib.def.&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;-------------------------&lt;br /&gt;clojure.contrib.def/defnk&lt;br /&gt;([fn-name &amp; fn-tail])&lt;br /&gt;Macro&lt;br /&gt;  Define a function accepting keyword arguments. Symbols up to the first&lt;br /&gt; keyword in the parameter list are taken as positional arguments.  Then&lt;br /&gt; an alternating sequence of keywords and defaults values is expected. The&lt;br /&gt; values of the keyword arguments are available in the function body by&lt;br /&gt; virtue of the symbol corresponding to the keyword (cf. :keys destructuring).&lt;br /&gt; defnk accepts an optional docstring as well as an optional metadata map.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Unfornately, no examples were given in the doc string. Here&#39;s a simple example:&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(require &#39;[clojure.contrib.def :as def])&lt;br /&gt;(def/defnk t [a :b 1] [a b])&lt;br /&gt;(t 3)&lt;br /&gt;;=&gt; [3 1]&lt;br /&gt;(t 3 :b 4)&lt;br /&gt;;=&gt; [3 4]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It should be noted that, the optional/named arguments must be put at the tail of the argument list. i.e. You can&#39;t write this: &lt;span class=&quot;code&quot;&gt;(def/defnk t [a :b 1 c] [a b c])&lt;/span&gt; If you do, weird thing would happen. Or, your function may not even compile.&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(def/defnk t [a :b 1 c] [a b c])&lt;br /&gt;(t 1 2 3)&lt;br /&gt;;=&gt; [1 1 nil]&lt;br /&gt;(def/defnk t [a :b 1 c d] [a b c d])&lt;br /&gt;;=&gt; java.lang.Exception: Unable to resolve symbol: d in this context&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/8824537062693802033/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/8824537062693802033' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/8824537062693802033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/8824537062693802033'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/08/named-arguments-in-clojure.html' title='Named arguments in Clojure'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-5633948762192056833</id><published>2010-08-16T04:23:00.000-07:00</published><updated>2010-08-17T05:46:59.354-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Clojure"/><title type='text'>在 Clojure 中处理异常的重要方法</title><content type='html'>Clojure 中虽然使用了 Java 的异常处理机制。但是，Clojure 很难自然地自定义自己的异常。我在与 Java 类库进行交互就时恰恰遇到了这种需求。下面的代码是与 svn-kit 进行交互的代码，它们提供了 svn-kit 的一个 wrapper。&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defmacro- try-catch-svn-ex [&amp; exprs]&lt;br /&gt;  `(try ~@exprs&lt;br /&gt;    (catch org.tmatesoft.svn.core.SVNAuthenticationException e#&lt;br /&gt;      :auth-ex)&lt;br /&gt;    (catch org.tmatesoft.svn.core.SVNException e#&lt;br /&gt;      (if (re-matches #&quot;.*404 Not Found.*&quot; (.getMessage e#))&lt;br /&gt;        nil&lt;br /&gt;        (throw e#)))))&lt;br /&gt;&lt;br /&gt;(defn svn-get-file! [svn-repo file-path local-file]&lt;br /&gt;  (with-open [os (output-stream (file local-file))]&lt;br /&gt;    (try-catch-svn-ex&lt;br /&gt;      (.getFile svn-repo file-path -1 (SVNProperties.) os)&lt;br /&gt;      local-file)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;调用 svn-get-file! 时可能会出现用户名密码无效的问题，这时候我希望能给用户重新输入的机会。但是又不想被其它的异常干扰。这时候我可以选择将 SVNAuthenticationException 暴露出去，但是明显捕获这样一个异常是很让外层函数头疼的事。同时，自定义 Clojure 异常在外部捕获更让人头疼。所以，我在捕获了 SVNAuthenticationException 后返回一个 :auth-ex。&lt;br /&gt;&lt;br /&gt;这种异常处理机制的最大的问题就是回到 C 语言时代检查函数返回值的方式上。这种方式写出来的程序会比较繁琐。最好的办法是用 Stuart Chouser 写的 clojure.contrib.error-kit 库。它提供了类似 Common Lisp 的异常处理体系。比传统的 try...catch 要强大很多。现在，我用 error-kit 库重写上面的函数：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(require &#39;[clojure.contrib.error-kit :as ek])&lt;br /&gt;&lt;br /&gt;(ek/deferror *svn-auth-error* [] [msg]&lt;br /&gt;  (:msg msg)&lt;br /&gt;  (:unhandled (ek/throw-msg Exception)))&lt;br /&gt;    &lt;br /&gt;(defmacro- try-catch-svn-ex [&amp; exprs]&lt;br /&gt;  `(try ~@exprs&lt;br /&gt;    (catch org.tmatesoft.svn.core.SVNAuthenticationException e#&lt;br /&gt;      (ek/raise *svn-auth-error* (.getMessage e#)))&lt;br /&gt;    (catch org.tmatesoft.svn.core.SVNException e#&lt;br /&gt;      (if (re-matches #&quot;.*404 Not Found.*&quot; (.getMessage e#))&lt;br /&gt;        nil&lt;br /&gt;        (throw e#)))))&lt;br /&gt;&lt;br /&gt;(defn svn-get-file! [svn-repo file-path local-file]&lt;br /&gt;  (with-open [os (output-stream (file local-file))]&lt;br /&gt;    (try-catch-svn-ex&lt;br /&gt;      (.getFile svn-repo file-path -1 (SVNProperties.) os)&lt;br /&gt;      local-file)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;注意我用 raise 调用代替了 :auth-ex 返回值。如果捕获到了权限异常，那么我们就 raise 一个 error。这个 error 必须用 deferror 函数定义。这个 *svn-auth-error* 在没有处理函数来处理它时会通过 throw-msg 调用抛出 Exception 异常，异常的消息内容就是 :msg 所指定的消息。&lt;br /&gt;&lt;br /&gt;注意 *svn-auth-error* 后面的第一个括号表示“父”error 是谁。这个父子关系内部通过标准库的 derive 方法定义。这里它没有父 error，所以留空。这时调用 svn-get-file! 的函数就可以拿到这个 error，可以选择让栈爆掉，也可以选择在异常抛出点继续执行。这里我们选择简单地处理后重新执行函数：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn svn-get-file-ex! [svn-repo file-path local-file]&lt;br /&gt;  (let [ret (ek/with-handler&lt;br /&gt;              (svn-get-file! svn-repo file-path local-file)&lt;br /&gt;              (ek/handle *svn-auth-error* [msg]&lt;br /&gt;                (println (str &quot;Error getting &quot; file-path &quot;, authentication failed&quot;))&lt;br /&gt;                (rm-scm-repo-username!)&lt;br /&gt;                (rm-scm-repo-password!)&lt;br /&gt;                (get-scm-repo-username!)&lt;br /&gt;                (get-scm-repo-password!)&lt;br /&gt;                (svn-get-file-ex! (get-scm-repo) file-path local-file)))]&lt;br /&gt;    (if&lt;br /&gt;      (nil? ret)&lt;br /&gt;        (ek/raise *get-scm-file-error* (str &quot;404 not found: &quot; file-path))&lt;br /&gt;      ret)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;注意此时对 svn-get-file-ex! 的递归调用不能用 recur。很遗憾，可能是因为 with-handler 或 handle 宏展开后定义了新的函数或者 loop。同时也请注意 deferror 时的 :unhandled 后面的 throw-msg 不要用 (throw (Exception. msg)) 来代替。如果这样做，你会发现异常是抛出去了，但是却捕获不到。原因是 :unhandled 后面期望跟的是一个函数定义。具体可以参看 throw-msg 的实现。&lt;br /&gt;&lt;br /&gt;更多关于 error-kit 的信息，比如 continue，请参阅：&lt;a href=&quot;http://groups.google.com/group/clojure/browse_thread/thread/2d1d91693887a45b&quot;&gt;ANN: clojure.contrib.error-kit&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;但是如果你不需要 error-kit 里的 continue 相关的功能的话，也可以使用 clojure.contrib.condition。这个库比较容易使用。而且还带了一个 print-stack-trace 方法，可以打印出比较干净的栈。示例可以参看 contrib 库源代码里面的 example 目录中的 condition/example.clj。&lt;br /&gt;&lt;br /&gt;这两种库实现上都利用 Java 的异常来跳出栈。所以，如果你想捕获所有的异常，包括这两种库抛出来的，可以用 catch Throwable。值得一提的是，condition 库的 print-stack-trace 是通用的。不仅可以打印 condition 库抛出来的异常，也可以打印其它的异常。&lt;br /&gt;&lt;br /&gt;contrib 库中还有一个 except，也是用来处理异常的。作者跟 condition 库是一个人。根据作者的原话，condition 库是 except 库的加强。</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/5633948762192056833/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/5633948762192056833' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/5633948762192056833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/5633948762192056833'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/08/clojure.html' title='在 Clojure 中处理异常的重要方法'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-4647665840032564167</id><published>2010-08-06T10:59:00.000-07:00</published><updated>2010-08-27T09:09:22.497-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Clojure"/><title type='text'>Functions added in Clojure 1.2</title><content type='html'>The following function docs were generated from Clojure 1.2.0-RC1, in the namespace of clojure.core.&lt;br /&gt;&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/agent-error&lt;br /&gt;([a])&lt;br /&gt;  Returns the exception thrown during an asynchronous action of the&lt;br /&gt;  agent if the agent is failed.  Returns nil if the agent is not&lt;br /&gt;  failed.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/bound?&lt;br /&gt;([&amp; vars])&lt;br /&gt;  Returns true if all of the vars provided as arguments have any bound value, root or thread-local.&lt;br /&gt;   Implies that deref&#39;ing the provided vars will succeed. Returns true if no vars are provided.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/case&lt;br /&gt;([e &amp; clauses])&lt;br /&gt;Macro&lt;br /&gt;  Takes an expression, and a set of clauses.&lt;br /&gt;&lt;br /&gt;  Each clause can take the form of either:&lt;br /&gt;&lt;br /&gt;  test-constant result-expr&lt;br /&gt;&lt;br /&gt;  (test-constant1 ... test-constantN)  result-expr&lt;br /&gt;&lt;br /&gt;  The test-constants are not evaluated. They must be compile-time&lt;br /&gt;  literals, and need not be quoted.  If the expression is equal to a&lt;br /&gt;  test-constant, the corresponding result-expr is returned. A single&lt;br /&gt;  default expression can follow the clauses, and its value will be&lt;br /&gt;  returned if no clause matches. If no default expression is provided&lt;br /&gt;  and no clause matches, an IllegalArgumentException is thrown.&lt;br /&gt;&lt;br /&gt;  Unlike cond and condp, case does a constant-time dispatch, the&lt;br /&gt;  clauses are not considered sequentially.  All manner of constant&lt;br /&gt;  expressions are acceptable in case, including numbers, strings,&lt;br /&gt;  symbols, keywords, and (Clojure) composites thereof. Note that since&lt;br /&gt;  lists are used to group multiple constants that map to the same&lt;br /&gt;  expression, a vector can be used to match a list if needed. The&lt;br /&gt;  test-constants need not be all of the same type.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/defprotocol&lt;br /&gt;([name &amp; opts+sigs])&lt;br /&gt;Macro&lt;br /&gt;  A protocol is a named set of named methods and their signatures:&lt;br /&gt;  (defprotocol AProtocolName&lt;br /&gt;&lt;br /&gt;    ;optional doc string&lt;br /&gt;    &quot;A doc string for AProtocol abstraction&quot;&lt;br /&gt;&lt;br /&gt;  ;method signatures&lt;br /&gt;    (bar [this a b] &quot;bar docs&quot;)&lt;br /&gt;    (baz [this a] [this a b] [this a b c] &quot;baz docs&quot;))&lt;br /&gt;&lt;br /&gt;  No implementations are provided. Docs can be specified for the&lt;br /&gt;  protocol overall and for each method. The above yields a set of&lt;br /&gt;  polymorphic functions and a protocol object. All are&lt;br /&gt;  namespace-qualified by the ns enclosing the definition The resulting&lt;br /&gt;  functions dispatch on the type of their first argument, which is&lt;br /&gt;  required and corresponds to the implicit target object (&#39;this&#39; in &lt;br /&gt;  Java parlance). defprotocol is dynamic, has no special compile-time &lt;br /&gt;  effect, and defines no new types or classes. Implementations of &lt;br /&gt;  the protocol methods can be provided using extend.&lt;br /&gt;&lt;br /&gt;  defprotocol will automatically generate a corresponding interface,&lt;br /&gt;  with the same name as the protocol, i.e. given a protocol:&lt;br /&gt;  my.ns/Protocol, an interface: my.ns.Protocol. The interface will&lt;br /&gt;  have methods corresponding to the protocol functions, and the&lt;br /&gt;  protocol will automatically work with instances of the interface.&lt;br /&gt;&lt;br /&gt;  Note that you should not use this interface with deftype or&lt;br /&gt;  reify, as they support the protocol directly:&lt;br /&gt;&lt;br /&gt;  (defprotocol P &lt;br /&gt;    (foo [this]) &lt;br /&gt;    (bar-me [this] [this y]))&lt;br /&gt;&lt;br /&gt;  (deftype Foo [a b c] &lt;br /&gt;   P&lt;br /&gt;    (foo [this] a)&lt;br /&gt;    (bar-me [this] b)&lt;br /&gt;    (bar-me [this y] (+ c y)))&lt;br /&gt;  &lt;br /&gt;  (bar-me (Foo. 1 2 3) 42)&lt;br /&gt;  =&gt; 45&lt;br /&gt;&lt;br /&gt;  (foo &lt;br /&gt;    (let [x 42]&lt;br /&gt;      (reify P &lt;br /&gt;        (foo [this] 17)&lt;br /&gt;        (bar-me [this] x)&lt;br /&gt;        (bar-me [this y] x))))&lt;br /&gt;  =&gt; 17&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/defrecord&lt;br /&gt;([name [&amp; fields] &amp; opts+specs])&lt;br /&gt;Macro&lt;br /&gt;  Alpha - subject to change&lt;br /&gt;  &lt;br /&gt;  (defrecord name [fields*]  options* specs*)&lt;br /&gt;  &lt;br /&gt;  Currently there are no options.&lt;br /&gt;&lt;br /&gt;  Each spec consists of a protocol or interface name followed by zero&lt;br /&gt;  or more method bodies:&lt;br /&gt;&lt;br /&gt;  protocol-or-interface-or-Object&lt;br /&gt;  (methodName [args*] body)*&lt;br /&gt;&lt;br /&gt;  Dynamically generates compiled bytecode for class with the given&lt;br /&gt;  name, in a package with the same name as the current namespace, the&lt;br /&gt;  given fields, and, optionally, methods for protocols and/or&lt;br /&gt;  interfaces.&lt;br /&gt;&lt;br /&gt;  The class will have the (immutable) fields named by&lt;br /&gt;  fields, which can have type hints. Protocols/interfaces and methods&lt;br /&gt;  are optional. The only methods that can be supplied are those&lt;br /&gt;  declared in the protocols/interfaces.  Note that method bodies are&lt;br /&gt;  not closures, the local environment includes only the named fields,&lt;br /&gt;  and those fields can be accessed directy.&lt;br /&gt;&lt;br /&gt;  Method definitions take the form:&lt;br /&gt;&lt;br /&gt;  (methodname [args*] body)&lt;br /&gt;&lt;br /&gt;  The argument and return types can be hinted on the arg and&lt;br /&gt;  methodname symbols. If not supplied, they will be inferred, so type&lt;br /&gt;  hints should be reserved for disambiguation.&lt;br /&gt;&lt;br /&gt;  Methods should be supplied for all methods of the desired&lt;br /&gt;  protocol(s) and interface(s). You can also define overrides for&lt;br /&gt;  methods of Object. Note that a parameter must be supplied to&lt;br /&gt;  correspond to the target object (&#39;this&#39; in Java parlance). Thus&lt;br /&gt;  methods for interfaces will take one more argument than do the&lt;br /&gt;  interface declarations. Note also that recur calls to the method&lt;br /&gt;  head should *not* pass the target object, it will be supplied&lt;br /&gt;  automatically and can not be substituted.&lt;br /&gt;&lt;br /&gt;  In the method bodies, the (unqualified) name can be used to name the&lt;br /&gt;  class (for calls to new, instance? etc).&lt;br /&gt;&lt;br /&gt;  The class will have implementations of several (clojure.lang)&lt;br /&gt;  interfaces generated automatically: IObj (metadata support) and&lt;br /&gt;  IPersistentMap, and all of their superinterfaces.&lt;br /&gt;&lt;br /&gt;  In addition, defrecord will define type-and-value-based equality and&lt;br /&gt;  hashCode.&lt;br /&gt;&lt;br /&gt;  When AOT compiling, generates compiled bytecode for a class with the&lt;br /&gt;  given name (a symbol), prepends the current ns as the package, and&lt;br /&gt;  writes the .class file to the *compile-path* directory.&lt;br /&gt;&lt;br /&gt;  Two constructors will be defined, one taking the designated fields&lt;br /&gt;  followed by a metadata map (nil for none) and an extension field&lt;br /&gt;  map (nil for none), and one taking only the fields (using nil for&lt;br /&gt;  meta and extension fields).&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/deftype&lt;br /&gt;([name [&amp; fields] &amp; opts+specs])&lt;br /&gt;Macro&lt;br /&gt;  Alpha - subject to change&lt;br /&gt;  &lt;br /&gt;  (deftype name [fields*]  options* specs*)&lt;br /&gt;  &lt;br /&gt;  Currently there are no options.&lt;br /&gt;&lt;br /&gt;  Each spec consists of a protocol or interface name followed by zero&lt;br /&gt;  or more method bodies:&lt;br /&gt;&lt;br /&gt;  protocol-or-interface-or-Object&lt;br /&gt;  (methodName [args*] body)*&lt;br /&gt;&lt;br /&gt;  Dynamically generates compiled bytecode for class with the given&lt;br /&gt;  name, in a package with the same name as the current namespace, the&lt;br /&gt;  given fields, and, optionally, methods for protocols and/or&lt;br /&gt;  interfaces. &lt;br /&gt;&lt;br /&gt;  The class will have the (by default, immutable) fields named by&lt;br /&gt;  fields, which can have type hints. Protocols/interfaces and methods&lt;br /&gt;  are optional. The only methods that can be supplied are those&lt;br /&gt;  declared in the protocols/interfaces.  Note that method bodies are&lt;br /&gt;  not closures, the local environment includes only the named fields,&lt;br /&gt;  and those fields can be accessed directy. Fields can be qualified&lt;br /&gt;  with the metadata :volatile-mutable true or :unsynchronized-mutable&lt;br /&gt;  true, at which point (set! afield aval) will be supported in method&lt;br /&gt;  bodies. Note well that mutable fields are extremely difficult to use&lt;br /&gt;  correctly, and are present only to facilitate the building of higher&lt;br /&gt;  level constructs, such as Clojure&#39;s reference types, in Clojure&lt;br /&gt;  itself. They are for experts only - if the semantics and&lt;br /&gt;  implications of :volatile-mutable or :unsynchronized-mutable are not&lt;br /&gt;  immediately apparent to you, you should not be using them.&lt;br /&gt;&lt;br /&gt;  Method definitions take the form:&lt;br /&gt;&lt;br /&gt;  (methodname [args*] body)&lt;br /&gt;&lt;br /&gt;  The argument and return types can be hinted on the arg and&lt;br /&gt;  methodname symbols. If not supplied, they will be inferred, so type&lt;br /&gt;  hints should be reserved for disambiguation.&lt;br /&gt;&lt;br /&gt;  Methods should be supplied for all methods of the desired&lt;br /&gt;  protocol(s) and interface(s). You can also define overrides for&lt;br /&gt;  methods of Object. Note that a parameter must be supplied to&lt;br /&gt;  correspond to the target object (&#39;this&#39; in Java parlance). Thus&lt;br /&gt;  methods for interfaces will take one more argument than do the&lt;br /&gt;  interface declarations. Note also that recur calls to the method&lt;br /&gt;  head should *not* pass the target object, it will be supplied&lt;br /&gt;  automatically and can not be substituted.&lt;br /&gt;&lt;br /&gt;  In the method bodies, the (unqualified) name can be used to name the&lt;br /&gt;  class (for calls to new, instance? etc).&lt;br /&gt;&lt;br /&gt;  When AOT compiling, generates compiled bytecode for a class with the&lt;br /&gt;  given name (a symbol), prepends the current ns as the package, and&lt;br /&gt;  writes the .class file to the *compile-path* directory.&lt;br /&gt;&lt;br /&gt;  One constructors will be defined, taking the designated fields.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/denominator&lt;br /&gt;([r])&lt;br /&gt;  Returns the denominator part of a Ratio.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/error-handler&lt;br /&gt;([a])&lt;br /&gt;  Returns the error-handler of agent a, or nil if there is none.&lt;br /&gt;  See set-error-handler!&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/error-mode&lt;br /&gt;([a])&lt;br /&gt;  Returns the error-mode of agent a.  See set-error-mode!&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/extend&lt;br /&gt;([atype &amp; proto+mmaps])&lt;br /&gt;  Implementations of protocol methods can be provided using the extend construct:&lt;br /&gt;&lt;br /&gt;  (extend AType&lt;br /&gt;    AProtocol&lt;br /&gt;     {:foo an-existing-fn&lt;br /&gt;      :bar (fn [a b] ...)&lt;br /&gt;      :baz (fn ([a]...) ([a b] ...)...)}&lt;br /&gt;    BProtocol &lt;br /&gt;      {...} &lt;br /&gt;    ...)&lt;br /&gt; &lt;br /&gt;  extend takes a type/class (or interface, see below), and one or more&lt;br /&gt;  protocol + method map pairs. It will extend the polymorphism of the&lt;br /&gt;  protocol&#39;s methods to call the supplied methods when an AType is&lt;br /&gt;  provided as the first argument. &lt;br /&gt;&lt;br /&gt;  Method maps are maps of the keyword-ized method names to ordinary&lt;br /&gt;  fns. This facilitates easy reuse of existing fns and fn maps, for&lt;br /&gt;  code reuse/mixins without derivation or composition. You can extend&lt;br /&gt;  an interface to a protocol. This is primarily to facilitate interop&lt;br /&gt;  with the host (e.g. Java) but opens the door to incidental multiple&lt;br /&gt;  inheritance of implementation since a class can inherit from more&lt;br /&gt;  than one interface, both of which extend the protocol. It is TBD how&lt;br /&gt;  to specify which impl to use. You can extend a protocol on nil.&lt;br /&gt;&lt;br /&gt;  If you are supplying the definitions explicitly (i.e. not reusing&lt;br /&gt;  exsting functions or mixin maps), you may find it more convenient to&lt;br /&gt;  use the extend-type or extend-protocol macros.&lt;br /&gt;&lt;br /&gt;  Note that multiple independent extend clauses can exist for the same&lt;br /&gt;  type, not all protocols need be defined in a single extend call.&lt;br /&gt;&lt;br /&gt;  See also:&lt;br /&gt;  extends?, satisfies?, extenders&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/extend-protocol&lt;br /&gt;([p &amp; specs])&lt;br /&gt;Macro&lt;br /&gt;  Useful when you want to provide several implementations of the same&lt;br /&gt;  protocol all at once. Takes a single protocol and the implementation&lt;br /&gt;  of that protocol for one or more types. Expands into calls to&lt;br /&gt;  extend-type:&lt;br /&gt;&lt;br /&gt;  (extend-protocol Protocol&lt;br /&gt;    AType&lt;br /&gt;      (foo [x] ...)&lt;br /&gt;      (bar [x y] ...)&lt;br /&gt;    BType&lt;br /&gt;      (foo [x] ...)&lt;br /&gt;      (bar [x y] ...)&lt;br /&gt;    AClass&lt;br /&gt;      (foo [x] ...)&lt;br /&gt;      (bar [x y] ...)&lt;br /&gt;    nil&lt;br /&gt;      (foo [x] ...)&lt;br /&gt;      (bar [x y] ...))&lt;br /&gt;&lt;br /&gt;  expands into:&lt;br /&gt;&lt;br /&gt;  (do&lt;br /&gt;   (clojure.core/extend-type AType Protocol &lt;br /&gt;     (foo [x] ...) &lt;br /&gt;     (bar [x y] ...))&lt;br /&gt;   (clojure.core/extend-type BType Protocol &lt;br /&gt;     (foo [x] ...) &lt;br /&gt;     (bar [x y] ...))&lt;br /&gt;   (clojure.core/extend-type AClass Protocol &lt;br /&gt;     (foo [x] ...) &lt;br /&gt;     (bar [x y] ...))&lt;br /&gt;   (clojure.core/extend-type nil Protocol &lt;br /&gt;     (foo [x] ...) &lt;br /&gt;     (bar [x y] ...)))&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/extend-type&lt;br /&gt;([t &amp; specs])&lt;br /&gt;Macro&lt;br /&gt;  A macro that expands into an extend call. Useful when you are&lt;br /&gt;  supplying the definitions explicitly inline, extend-type&lt;br /&gt;  automatically creates the maps required by extend.  Propagates the&lt;br /&gt;  class as a type hint on the first argument of all fns.&lt;br /&gt;&lt;br /&gt;  (extend-type MyType &lt;br /&gt;    Countable&lt;br /&gt;      (cnt [c] ...)&lt;br /&gt;    Foo&lt;br /&gt;      (bar [x y] ...)&lt;br /&gt;      (baz ([x] ...) ([x y &amp; zs] ...)))&lt;br /&gt;&lt;br /&gt;  expands into:&lt;br /&gt;&lt;br /&gt;  (extend MyType&lt;br /&gt;   Countable&lt;br /&gt;     {:cnt (fn [c] ...)}&lt;br /&gt;   Foo&lt;br /&gt;     {:baz (fn ([x] ...) ([x y &amp; zs] ...))&lt;br /&gt;      :bar (fn [x y] ...)})&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/extenders&lt;br /&gt;([protocol])&lt;br /&gt;  Returns a collection of the types explicitly extending protocol&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/extends?&lt;br /&gt;([protocol atype])&lt;br /&gt;  Returns true if atype extends protocol&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/flatten&lt;br /&gt;([x])&lt;br /&gt;  Takes any nested combination of sequential things (lists, vectors,&lt;br /&gt;  etc.) and returns their contents as a single, flat sequence.&lt;br /&gt;  (flatten nil) returns nil.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/fnil&lt;br /&gt;([f x] [f x y] [f x y z])&lt;br /&gt;  Takes a function f, and returns a function that calls f, replacing&lt;br /&gt;  a nil first argument to f with the supplied value x. Higher arity&lt;br /&gt;  versions can replace arguments in the second and third&lt;br /&gt;  positions (y, z). Note that the function f can take any number of&lt;br /&gt;  arguments, not just the one(s) being nil-patched.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/frequencies&lt;br /&gt;([coll])&lt;br /&gt;  Returns a map from distinct items in coll to the number of times&lt;br /&gt;  they appear.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/get-in&lt;br /&gt;([m ks] [m ks not-found])&lt;br /&gt;  Returns the value in a nested associative structure,&lt;br /&gt;  where ks is a sequence of ke(ys. Returns nil if the key is not present,&lt;br /&gt;  or the not-found value if supplied.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/group-by&lt;br /&gt;([f coll])&lt;br /&gt;  Returns a map of the elements of coll keyed by the result of&lt;br /&gt;  f on each element. The value at each key will be a vector of the&lt;br /&gt;  corresponding elements, in the order they appeared in coll.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/keep&lt;br /&gt;([f coll])&lt;br /&gt;  Returns a lazy sequence of the non-nil results of (f item). Note,&lt;br /&gt;  this means false return values will be included.  f must be free of&lt;br /&gt;  side-effects.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/keep-indexed&lt;br /&gt;([f coll])&lt;br /&gt;  Returns a lazy sequence of the non-nil results of (f index item). Note,&lt;br /&gt;  this means false return values will be included.  f must be free of&lt;br /&gt;  side-effects.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/map-indexed&lt;br /&gt;([f coll])&lt;br /&gt;  Returns a lazy sequence consisting of the result of applying f to 0&lt;br /&gt;  and the first item of coll, followed by applying f to 1 and the second&lt;br /&gt;  item in coll, etc, until coll is exhausted. Thus function f should&lt;br /&gt;  accept 2 arguments, index and item.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/namespace-munge&lt;br /&gt;([ns])&lt;br /&gt;  Convert a Clojure namespace name to a legal Java package name.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/numerator&lt;br /&gt;([r])&lt;br /&gt;  Returns the numerator part of a Ratio.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/object-array&lt;br /&gt;([size-or-seq])&lt;br /&gt;  Creates an array of objects&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/partition-all&lt;br /&gt;([n coll] [n step coll])&lt;br /&gt;  Returns a lazy sequence of lists like partition, but may include&lt;br /&gt;  partitions with fewer than n items at the end.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/partition-by&lt;br /&gt;([f coll])&lt;br /&gt;  Applies f to each value in coll, splitting it each time f returns&lt;br /&gt;   a new value.  Returns a lazy seq of partitions.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/rand-nth&lt;br /&gt;([coll])&lt;br /&gt;  Return a random element of the (sequential) collection. Will have&lt;br /&gt;  the same performance characteristics as nth for the given&lt;br /&gt;  collection.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/reductions&lt;br /&gt;([f coll] [f init coll])&lt;br /&gt;  Returns a lazy seq of the intermediate values of the reduction (as&lt;br /&gt;  per reduce) of coll by f, starting with init.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/reify&lt;br /&gt;([&amp; opts+specs])&lt;br /&gt;Macro&lt;br /&gt;  reify is a macro with the following structure:&lt;br /&gt;&lt;br /&gt; (reify options* specs*)&lt;br /&gt;  &lt;br /&gt;  Currently there are no options.&lt;br /&gt;&lt;br /&gt;  Each spec consists of the protocol or interface name followed by zero&lt;br /&gt;  or more method bodies:&lt;br /&gt;&lt;br /&gt;  protocol-or-interface-or-Object&lt;br /&gt;  (methodName [args+] body)*&lt;br /&gt;&lt;br /&gt;  Methods should be supplied for all methods of the desired&lt;br /&gt;  protocol(s) and interface(s). You can also define overrides for&lt;br /&gt;  methods of Object. Note that the first parameter must be supplied to&lt;br /&gt;  correspond to the target object (&#39;this&#39; in Java parlance). Thus&lt;br /&gt;  methods for interfaces will take one more argument than do the&lt;br /&gt;  interface declarations.  Note also that recur calls to the method&lt;br /&gt;  head should *not* pass the target object, it will be supplied&lt;br /&gt;  automatically and can not be substituted.&lt;br /&gt;&lt;br /&gt;  The return type can be indicated by a type hint on the method name,&lt;br /&gt;  and arg types can be indicated by a type hint on arg names. If you&lt;br /&gt;  leave out all hints, reify will try to match on same name/arity&lt;br /&gt;  method in the protocol(s)/interface(s) - this is preferred. If you&lt;br /&gt;  supply any hints at all, no inference is done, so all hints (or&lt;br /&gt;  default of Object) must be correct, for both arguments and return&lt;br /&gt;  type. If a method is overloaded in a protocol/interface, multiple&lt;br /&gt;  independent method definitions must be supplied.  If overloaded with&lt;br /&gt;  same arity in an interface you must specify complete hints to&lt;br /&gt;  disambiguate - a missing hint implies Object.&lt;br /&gt;&lt;br /&gt;  recur works to method heads The method bodies of reify are lexical&lt;br /&gt;  closures, and can refer to the surrounding local scope:&lt;br /&gt;  &lt;br /&gt;  (str (let [f &quot;foo&quot;] &lt;br /&gt;       (reify Object &lt;br /&gt;         (toString [this] f))))&lt;br /&gt;  == &quot;foo&quot;&lt;br /&gt;&lt;br /&gt;  (seq (let [f &quot;foo&quot;] &lt;br /&gt;       (reify clojure.lang.Seqable &lt;br /&gt;         (seq [this] (seq f)))))&lt;br /&gt;  == (\f \o \o))&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/remove-all-methods&lt;br /&gt;([multifn])&lt;br /&gt;  Removes all of the methods of multimethod.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/restart-agent&lt;br /&gt;([a new-state &amp; options])&lt;br /&gt;  When an agent is failed, changes the agent state to new-state and&lt;br /&gt;  then un-fails the agent so that sends are allowed again.  If&lt;br /&gt;  a :clear-actions true option is given, any actions queued on the&lt;br /&gt;  agent that were being held while it was failed will be discarded,&lt;br /&gt;  otherwise those held actions will proceed.  The new-state must pass&lt;br /&gt;  the validator if any, or restart will throw an exception and the&lt;br /&gt;  agent will remain failed with its old state and error.  Watchers, if&lt;br /&gt;  any, will NOT be notified of the new state.  Throws an exception if&lt;br /&gt;  the agent is not failed.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/satisfies?&lt;br /&gt;([protocol x])&lt;br /&gt;  Returns true if x satisfies the protocol&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/set-error-handler!&lt;br /&gt;([a handler-fn])&lt;br /&gt;  Sets the error-handler of agent a to handler-fn.  If an action&lt;br /&gt;  being run by the agent throws an exception or doesn&#39;t pass the&lt;br /&gt;  validator fn, handler-fn will be called with two arguments: the&lt;br /&gt;  agent and the exception.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/set-error-mode!&lt;br /&gt;([a mode-keyword])&lt;br /&gt;  Sets the error-mode of agent a to mode-keyword, which must be&lt;br /&gt;  either :fail or :continue.  If an action being run by the agent&lt;br /&gt;  throws an exception or doesn&#39;t pass the validator fn, an&lt;br /&gt;  error-handler may be called (see set-error-handler!), after which,&lt;br /&gt;  if the mode is :continue, the agent will continue as if neither the&lt;br /&gt;  action that caused the error nor the error itself ever happened.&lt;br /&gt;  &lt;br /&gt;  If the mode is :fail, the agent will become failed and will stop&lt;br /&gt;  accepting new &#39;send&#39; and &#39;send-off&#39; actions, and any previously&lt;br /&gt;  queued actions will be held until a &#39;restart-agent&#39;.  Deref will&lt;br /&gt;  still work, returning the state of the agent before the error.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/shuffle&lt;br /&gt;([coll])&lt;br /&gt;  Return a random permutation of coll&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/spit&lt;br /&gt;([f content &amp; options])&lt;br /&gt;  Opposite of slurp.  Opens f with writer, writes content, then&lt;br /&gt;  closes f. Options passed to clojure.java.io/writer.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/thread-bound?&lt;br /&gt;([&amp; vars])&lt;br /&gt;  Returns true if all of the vars provided as arguments have thread-local bindings.&lt;br /&gt;   Implies that set!&#39;ing the provided vars will succeed.  Returns true if no vars are provided.&lt;br /&gt;-------------------------&lt;br /&gt;clojure.core/vector-of&lt;br /&gt;([t])&lt;br /&gt;  Creates a new vector of a single primitive type t, where t is one&lt;br /&gt;  of :int :long :float :double :byte :short :char or :boolean. The&lt;br /&gt;  resulting vector complies with the interface of vectors in general,&lt;br /&gt;  but stores the values unboxed internally.</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/4647665840032564167/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/4647665840032564167' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/4647665840032564167'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/4647665840032564167'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/08/functions-added-in-clojure-12.html' title='Functions added in Clojure 1.2'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-3918574902822058418</id><published>2010-07-24T07:56:00.000-07:00</published><updated>2010-08-27T09:10:01.141-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="functional programming"/><title type='text'>推荐一篇 Monad 入门文章</title><content type='html'>&lt;a href=&quot;http://www.iis.sinica.edu.tw/%7Escm/ncs/2009/11/a-monad-primer/&quot;&gt;http://www.iis.sinica.edu.tw/~scm/ncs/2009/11/a-monad-primer/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Monad 我看了好久，找了 n 多文章始终无法理解。最后甚至起了冲动要去啃范畴论。上面这篇文章终于解了我的这块心结。</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/3918574902822058418/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/3918574902822058418' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/3918574902822058418'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/3918574902822058418'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/07/monad.html' title='推荐一篇 Monad 入门文章'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-6083086956324244059</id><published>2010-06-10T06:35:00.000-07:00</published><updated>2010-06-10T06:53:45.472-07:00</updated><title type='text'>Use Areca backup on 64 bit Windows</title><content type='html'>&lt;a href=&quot;http://www.areca-backup.org/&quot;&gt;Areca&lt;/a&gt; is an open-source backup software which is located on Source Forge. Its official releases doesn&#39;t support 64-bit Windows which is quite unconvient for me, because I&#39;m using 64-bit Windows on two machines, and backup is simply an essential to me.&lt;br /&gt;&lt;br /&gt;The anonying part when you try to start areca is that you can&#39;t see any error message and no log can be found. The problem is Areca uses 32-bit SWT library. You can fix this just by replacing the 32-bit SWT library with a &lt;a href=&quot;http://download.eclipse.org/eclipse/downloads/drops/R-3.5.2-201002111343/download.php?dropFile=swt-3.5.2-win32-win32-x86_64.zip&quot;&gt;64-bit one&lt;/a&gt;. Say you installed Areca to &lt;span class=&quot;code&quot;&gt;C:\Program Files (x86)\Areca&lt;/span&gt;. Go to that directory, delete the &lt;span class=&quot;code&quot;&gt;swt-win32-*.dll&lt;/span&gt; and paste your 64-bit SWT jar file to the lib directory. Go into the lib directory, delete the original &lt;span class=&quot;code&quot;&gt;org.eclipse.swt.win32.win32.x86_3.2.0.v3232m.jar&lt;/span&gt;，and rename the 64-bit jar file to this name. To say this way, you make Areca believes everything is fine, but actually the SWT library is 64-bit now.&lt;br /&gt;&lt;br /&gt;Now all set, enjoy using Areca.</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/6083086956324244059/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/6083086956324244059' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/6083086956324244059'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/6083086956324244059'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/06/use-areca-backup-on-64-bit-windows.html' title='Use Areca backup on 64 bit Windows'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-7221638649840308336</id><published>2010-05-30T10:17:00.000-07:00</published><updated>2010-05-30T22:57:35.276-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Clojure"/><title type='text'>Clojure 的类型提示</title><content type='html'>为了提高与 Java 类库交互时的性能，Clojure 提供了元数据用以定义某个给定的变量的类型。比如在函数定义的时候可以指定参数与返回值的类型：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn add-elem [#^java.util.List java-list elem]&lt;br /&gt;  (.add java-list elem)&lt;br /&gt;  java-list)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;等效于&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn add-elem [#^{:tag java.util.List} java-list elem]&lt;br /&gt;  (.add java-list elem)&lt;br /&gt;  java-list)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;如果不告诉编译器这个类型&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn naive-add-elem [java-list elem]&lt;br /&gt;  (.add java-list elem)&lt;br /&gt;  java-list)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Clojure 一样可以顺利执行，只不过 Clojure 是通过反射去调用 add 方法的。因为编译器没办法知道运行时的参数可能会是什么类型。所以，两种方法的执行时间差距还是有点大的：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(def java-list (java.util.ArrayList.))&lt;br /&gt;&lt;br /&gt;(time (dotimes [_ 100000]&lt;br /&gt;(naive-add-elem java-list 1))) ==&gt; &quot;Elapsed time: 342.663873 msecs&quot;&lt;br /&gt;&lt;br /&gt;(time (dotimes [_ 100000]&lt;br /&gt;(add-elem java-list 1)))       ==&gt; &quot;Elapsed time: 18.652259 msecs&quot;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;有一个数量级的差距，跟反射与普通方法调用的差距差不多。但是不带类型提示的版本会在重用性上取胜。比如：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(def java-list (java.util.ArrayList.))&lt;br /&gt;(def java-set (java.util.HashSet.))&lt;br /&gt;&lt;br /&gt;(naive-add-elem java-list 1)  ==&gt; #&amp;lt;ArrayList [1]&amp;gt;&lt;br /&gt;(naive-add-elem java-set 1)   ==&gt; #&amp;lt;Hashset [1]&amp;gt;&lt;br /&gt;&lt;br /&gt;(add-elem java-list 1)  ==&gt; #&amp;lt;ArrayList [1]&amp;gt;&lt;br /&gt;(add-elem java-set 1)   ==&gt; ClassCastException!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;与类型提示有点关系的就是 :inline。为了加快 Clojure 在 JVM 上的数字运算，Clojure 提供了内联。因为 Clojure 与 C/C++ 一样具有宏的功能（只不过强大了太多），所以 Clojure 也可以做到源代码展开。比如 num 的实现：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn num&lt;br /&gt;  {:tag Number&lt;br /&gt;   :inline (fn  [x] `(. clojure.lang.Numbers (num ~x)))}&lt;br /&gt;  [x] (. clojure.lang.Numbers (num x)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;编译器何时会选择内联我不知道，只是它的确有这种优化。因为在频繁数字运算时 HotSpot 会将包装类型优化成原始类型，所以 Clojure 依赖 JVM 的这个特性，通过内联将优化的任务交给 JVM 去做。Clojure 的官网说下面的两段代码在 JVM 的 -server 属性开启时运行速度一样：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;static public float asum(float[] xs) {&lt;br /&gt;    float ret = 0;&lt;br /&gt;    for (int i = 0; i &amp;lt; xs.length; i++)&lt;br /&gt;        ret += xs[i];&lt;br /&gt;    return ret;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;(defn asum [#^floats xs]&lt;br /&gt;  (areduce xs i ret (float 0)&lt;br /&gt;    (+ ret (aget xs i))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Clojure 在给数字运算型函数做内联的时候与普通宏不一样的地方是，内联的宏都返回一个函数。这样它们就可以被扔到 map, reduce 这样的函数去执行了。对于原始类型数组，Clojure 特别添加了支持：&lt;span class=&quot;code&quot;&gt;#^ints, #^floats, #^longs, #^doubles&lt;/span&gt;。关于内联具体请参看：&lt;a href=&quot;http://clojure.org/news&quot;&gt;http://clojure.org/news&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;对于那些无法在函数声明时在参数列表就加上类型提示的，可以在 Java 调用的时候再指明参数类型。比如：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn bigint&lt;br /&gt;  {:tag BigInteger}&lt;br /&gt;  [x] (cond&lt;br /&gt;       (instance? BigInteger x) x&lt;br /&gt;       (decimal? x) (.toBigInteger #^BigDecimal x)&lt;br /&gt;       (number? x) (BigInteger/valueOf (long x))&lt;br /&gt;       :else (BigInteger. x)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;这个函数在声明参数 x 的时候并没有指明类型，但是通过 &lt;span class=&quot;code&quot;&gt;decimal?&lt;/span&gt; 测试后 x 就应该是 &lt;span class=&quot;code&quot;&gt;BigDecimal&lt;/span&gt; 类型的了，这个时候就可以告诉编译 x 的类型是 BigDecimal。</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/7221638649840308336/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/7221638649840308336' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/7221638649840308336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/7221638649840308336'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/05/clojure_30.html' title='Clojure 的类型提示'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-516923503247288440</id><published>2010-05-29T03:36:00.000-07:00</published><updated>2010-05-30T22:48:57.906-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Clojure"/><title type='text'>Clojure agents</title><content type='html'>Clojure agents 是用 JDK 的线程池实现的。比如 send&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn send&lt;br /&gt;  [#^clojure.lang.Agent a f &amp; args]&lt;br /&gt;    (. a (dispatch f args false)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class=&quot;code&quot;&gt;clojure.lang.Agent#dispatch&lt;/span&gt; 方法把 f 封装成一个 Action 对象，再把执行这个 Action 的任务代理给 Action 自身的 execute 方法。execute 方法会把自身的 action 实例扔到线程池中去执行。看看 send 的兄弟，send-off&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn send-off&lt;br /&gt;  [#^clojure.lang.Agent a f &amp; args]&lt;br /&gt;    (. a (dispatch f args true)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;注意到了吗？这两个函数只差一个参数，send 在调用 dispatch 的时候最后一个参数是 false，而 send-off 是 true。这个参数为真表示 Action 在执行的时候是使用 CachedThreadPool，否则使用 FixedThreadPool 。FixedThreadPool 是大小固定的线程池，所以 send 方法的 f 函数不能包含 IO 操作，而 send-off 可以。&lt;br /&gt;&lt;br /&gt;相应地，agent 在执行时线程池会被初始化。所以，如果你的程序中使用到了 agents，在程序的最后要记得关闭线程池。如果你不关闭，在 eclipse 或者 IDEA 中你会看到程序执行完毕后标准输出窗口的&lt;i&gt;结束程序&lt;/i&gt;的按钮仍然可用。原因就是线程池未关闭。当然关闭线程池最重要的作用是确保线程池中所有的操作都已完成，然后释放资源。结束 agents 系统，关闭线程池的函数是 shutdown-agents，不带任何参数。</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/516923503247288440/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/516923503247288440' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/516923503247288440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/516923503247288440'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/05/clojure-agents.html' title='Clojure agents'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-6706508787392599036</id><published>2010-05-28T13:18:00.000-07:00</published><updated>2010-05-30T22:48:57.906-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Clojure"/><title type='text'>Clojure 核心库代码阅读技巧</title><content type='html'>核心库（core.clj）的实现代表了 Clojure 的标准和规范。通过阅读核心库的代码可以提高自身对 Clojure 的理解，并且帮助自己从 C 语系的命令式（imperative）的思想转变到非命令式。&lt;br /&gt;&lt;br /&gt;核心库的函数实现通常不超过 10 行，以五行左右居多。它的简洁性来源于对递归的深入使用。同时，也依赖于 if, cons, conj, seq, next 对 nil 的特殊处理。&lt;br /&gt;&lt;br /&gt;Clojure 中尽管有布尔型，但是不像其它的高级语言一样强制要求 if 接受布尔型。而是像 C/C++ 一样将 nil 作为 false 来看待。所以像 JavaScript 一样，经常会看到这样的代码：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(if x&lt;br /&gt;  then ; x 存在&lt;br /&gt;  else) ; x 不存在&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;cons 用来连接元素和集合。但是，如果 cons 的第二个参数，也就是集合，如果为 nil 的话，cons 会把这个集合当作空集来看待。比如：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(cons 1 [])   ==&gt; (1)&lt;br /&gt;(cons 1 nil)  ==&gt; (1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;conj 类似，只不过 conj 是第一个参数接集合。如果集合为 nil，也被当作空集。（当然 cons 和 conj 作用并不完全一样）seq 和 next 的特殊性是这样：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(seq [])  ==&gt; nil&lt;br /&gt;(next []) ==&gt; nil&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Clojure 标准库中各种函数对 nil 的宽容对待都是基于这几个基本函数做到的。比如下面是经过简化的 reduce 实现：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn reduce&lt;br /&gt;  ([f val coll]&lt;br /&gt;     (if-let [s (seq coll)]&lt;br /&gt;       (recur f (f val (first s)) (next s))&lt;br /&gt;       val)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;if-let 只有在 coll 不为空集时才会隐式地用 let 绑定 s。再比如 conj 的实现：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(def&lt;br /&gt; conj (fn conj &lt;br /&gt;        ([coll x] (. clojure.lang.RT (conj coll x)))&lt;br /&gt;        ([coll x &amp; xs]&lt;br /&gt;         (if xs&lt;br /&gt;           (recur (conj coll x) (first xs) (next xs))&lt;br /&gt;           (conj coll x)))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;注意第二个函数重载，当函数递归到最后一层的时候因为 next 调用的原因， xs 必为 nil。这时候 (if xs) 就会是 false，函数停止递归。&lt;br /&gt;&lt;br /&gt;核心库中大部分都是这样的短小的函数。但是因为性能优化的需要，函数可能会比你现在看到的要臃肿一些。比如 reduce 在 1.1.0 版本中的实际实现：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defn reduce&lt;br /&gt;  ([f coll]&lt;br /&gt;   (let [s (seq coll)]&lt;br /&gt;     (if s&lt;br /&gt;       (reduce f (first s) (next s))&lt;br /&gt;       (f))))&lt;br /&gt;  ([f val coll]&lt;br /&gt;     (let [s (seq coll)]&lt;br /&gt;       (if s&lt;br /&gt;         (if (chunked-seq? s)&lt;br /&gt;           (recur f &lt;br /&gt;                  (.reduce (chunk-first s) f val)&lt;br /&gt;                  (chunk-next s))&lt;br /&gt;           (recur f (f val (first s)) (next s)))&lt;br /&gt;         val))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;除去第一个重载给用户提供了便利之外，第二个重载中使用了 chunked-seq。chunked-seq 就是性能优化的产物。看核心库代码的时候如果感觉函数有点长，不好懂，那就先尝试把这些为了性能优化而加的代码去掉。可以在心里去掉，也可以把代码拷贝出来，从视觉上去掉。&lt;br /&gt;&lt;br /&gt;目前我看到的最难懂的代码就是 doseq 和 for 的实现，达到了 30 - 80 行。我的智商不高，看 doseq 花了很长时间。不过也因此得出一条重要的经验：看复杂代码的时候要先揣摩作者的意图，以意图作为切入点，以具体代码逻辑作验证，看看自己揣摩出来的意图是不是跟作者一致。作为一名传统的 C 语系的程序员，对 FP 风格的代码不熟的确给我的阅读带来了一定障碍。但是我保证，如果你跟我一样坚持下来，你会发现 Clojure 在微观级别的抽象表达能力比 C 语系的命令式语言要强上一个档次。这点对于你理解算法来说影响非常大。直观上觉得同样的程序 Java 写出来更好懂。但如果你试着去熟悉 Clojure 会发现事实刚好相反。看 doseq 的实现：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(defmacro doseq&lt;br /&gt;  &quot;Repeatedly executes body (presumably for side-effects) with&lt;br /&gt;  bindings and filtering as provided by \&quot;for\&quot;.  Does not retain&lt;br /&gt;  the head of the sequence. Returns nil.&quot;&lt;br /&gt;  [seq-exprs &amp; body]&lt;br /&gt;  (assert-args doseq&lt;br /&gt;     (vector? seq-exprs) &quot;a vector for its binding&quot;&lt;br /&gt;     (even? (count seq-exprs)) &quot;an even number of forms in binding vector&quot;)&lt;br /&gt;  (let [step (fn step [recform exprs]&lt;br /&gt;               (if-not exprs&lt;br /&gt;                 [true `(do ~@body)]&lt;br /&gt;                 (let [k (first exprs)&lt;br /&gt;                       v (second exprs)]&lt;br /&gt;                   (if (keyword? k)&lt;br /&gt;                     (let [steppair (step recform (nnext exprs))&lt;br /&gt;                           needrec (steppair 0)&lt;br /&gt;                           subform (steppair 1)]&lt;br /&gt;                       (cond&lt;br /&gt;                         (= k :let) [needrec `(let ~v ~subform)]&lt;br /&gt;                         (= k :while) [false `(when ~v&lt;br /&gt;                                                ~subform&lt;br /&gt;                                                ~@(when needrec [recform]))]&lt;br /&gt;                         (= k :when) [false `(if ~v&lt;br /&gt;                                               (do&lt;br /&gt;                                                 ~subform&lt;br /&gt;                                                 ~@(when needrec [recform]))&lt;br /&gt;                                               ~recform)]))&lt;br /&gt;                     (let [seq- (gensym &quot;seq_&quot;)&lt;br /&gt;                           chunk- (with-meta (gensym &quot;chunk_&quot;)&lt;br /&gt;                                             {:tag &#39;clojure.lang.IChunk})&lt;br /&gt;                           count- (gensym &quot;count_&quot;)&lt;br /&gt;                           i- (gensym &quot;i_&quot;)&lt;br /&gt;                           recform `(recur (next ~seq-) nil (int 0) (int 0))&lt;br /&gt;                           steppair (step recform (nnext exprs))&lt;br /&gt;                           needrec (steppair 0)&lt;br /&gt;                           subform (steppair 1)&lt;br /&gt;                           recform-chunk &lt;br /&gt;                             `(recur ~seq- ~chunk- ~count- (unchecked-inc ~i-))&lt;br /&gt;                           steppair-chunk (step recform-chunk (nnext exprs))&lt;br /&gt;                           subform-chunk (steppair-chunk 1)]&lt;br /&gt;                       [true&lt;br /&gt;                        `(loop [~seq- (seq ~v), ~chunk- nil,&lt;br /&gt;                                ~count- (int 0), ~i- (int 0)]&lt;br /&gt;                           (if (&lt; ~i- ~count-)&lt;br /&gt;                             (let [~k (.nth ~chunk- ~i-)]&lt;br /&gt;                               ~subform-chunk&lt;br /&gt;                               ~@(when needrec [recform-chunk]))&lt;br /&gt;                             (when-let [~seq- (seq ~seq-)]&lt;br /&gt;                               (if (chunked-seq? ~seq-)&lt;br /&gt;                                 (let [c# (chunk-first ~seq-)]&lt;br /&gt;                                   (recur (chunk-rest ~seq-) c#&lt;br /&gt;                                          (int (count c#)) (int 0)))&lt;br /&gt;                                 (let [~k (first ~seq-)]&lt;br /&gt;                                   ~subform&lt;br /&gt;                                   ~@(when needrec [recform]))))))])))))]&lt;br /&gt;    (nth (step nil (seq seq-exprs)) 1)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;首先不去看有关 chunked-seq 的部分，在这么复杂的函数（而且还是宏！）中它会极大地干扰你的阅读。随便一扫就可以发现函数的主体都在 step 这个内部函数里面。递归包含两部分：1、递推基础 2、递推规则。标准库很多函数都会定义一个内部函数 step。它就承担着定义递推规则的责任。所以 doseq 里面只有一句话 (nth (step nil (seq seq-exprs)) 1)))，它只负责提供递推基础。&lt;br /&gt;&lt;br /&gt;step 的第二个参数 exprs 从名字和调用上就可以看出是绑定表达式串（使用 doseq 时的中括号的部分）。recform 从名字上不好猜是什么，搜索一下 recform，发现这样一句：&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;recform `(recur (next ~seq-) nil (int 0) (int 0))&lt;/pre&gt;&lt;br /&gt;可以看出 recform 就是 recur-form 的意思。注意如果你跟我一样是严谨逻辑派的人，你会发现这样的假设并不严谨，不过不重要，我们目前的首要目标是猜出作者的意图，最后再用严谨的逻辑验证一次就好了。当然，这个过程可能因为猜测错误而反复多次。目前我们知道 recform 应该是用来承载 recur 的内容。那么相应地，needrec 也就应该表示是否需要继续递归的意思。通过这句话&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;(let [steppair (step recform (nnext exprs))&lt;/pre&gt;&lt;br /&gt;可以看出 steppair 表示 step 的返回值，它应该是一个大小为二的向量。再结合&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(let [steppair (step recform (nnext exprs))&lt;br /&gt;      needrec (steppair 0)&lt;br /&gt;      subform (steppair 1)]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;可以看出这个向量的第一个元素表示是否需要继续递归，而第二元素表示 step 递归缩减出来的子 form。有了上面这些猜测，我们可以看出 step 的最后三行是核心。&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(let [~k (first ~seq-)]&lt;br /&gt;  ~subform&lt;br /&gt;  ~@(when needrec [recform]))))))])))))]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;它们表示局部绑定 doseq 中指定集合的第一个元素，然后递归展开子 form。通过&lt;br /&gt;&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;(= k :let) [needrec `(let ~v ~subform)]&lt;br /&gt;(= k :while) [false `(when ~v&lt;br /&gt;  ~subform&lt;br /&gt;  ~@(when needrec [recform]))]&lt;br /&gt;(= k :when) [false `(if ~v&lt;br /&gt;  (do&lt;br /&gt;    ~subform&lt;br /&gt;    ~@(when needrec [recform]))&lt;br /&gt;  ~recform)]))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;可以看出 doseq 支持 for 所支持的三个额外参数 let, when, while。当这些都理解的时候就可以回过头去看 chunked-seq 的部分了。</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/6706508787392599036/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/6706508787392599036' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/6706508787392599036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/6706508787392599036'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/05/clojure.html' title='Clojure 核心库代码阅读技巧'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6121741363511450985.post-3487698973799998059</id><published>2010-05-25T06:28:00.000-07:00</published><updated>2010-08-27T09:09:40.473-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><title type='text'>精巧的状态机实现</title><content type='html'>&lt;pre class=&quot;code&quot;&gt;&lt;br /&gt;public enum SniperState {&lt;br /&gt;  JOINING {&lt;br /&gt;    @Override public SniperState whenAuctionClosed() { return LOST; }&lt;br /&gt;  },&lt;br /&gt;  BIDDING {&lt;br /&gt;    @Override public SniperState whenAuctionClosed() { return LOST; }&lt;br /&gt;  },&lt;br /&gt;  WINNING {&lt;br /&gt;    @Override public SniperState whenAuctionClosed() { return WON; }&lt;br /&gt;  },&lt;br /&gt;  LOST,&lt;br /&gt;  WON;&lt;br /&gt;  public SniperState whenAuctionClosed() {&lt;br /&gt;    throw new Defect(&quot;Auction is already closed&quot;);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Sniper 英语是狙击手的意思，在这个程序的上下文中表示拍卖交易中的自动出价器。它的状态包括刚刚加入拍卖交易过程、正在出价但未占上风、正在出价但暂时领先、已经失败、已经成功拍下。whenAuctionClosed() 是一个事件回调，表示拍卖交易已经关闭时的回调。&lt;br /&gt;&lt;br /&gt;因为已经失败和成功拍下这两个状态都是最终状态，所以调用 whenAuctionClosed() 会抛异常。而其它几个状态会重载这个方法，返回交易关闭后对应的状态。&lt;br /&gt;&lt;br /&gt;这段代码摘自《Growing Object-Oriented Software, Guided by Tests》。</content><link rel='replies' type='application/atom+xml' href='http://ydongpublic.blogspot.com/feeds/3487698973799998059/comments/default' title='博文评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6121741363511450985/3487698973799998059' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/3487698973799998059'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6121741363511450985/posts/default/3487698973799998059'/><link rel='alternate' type='text/html' href='http://ydongpublic.blogspot.com/2010/05/blog-post.html' title='精巧的状态机实现'/><author><name>杨冬</name><uri>http://www.blogger.com/profile/09429641560322620046</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>