haw*_*eye 6 scheme continuations clojure
在The Seasoned Schemer一书中- 作者编写了以下代码:
(define intersectall
(lambda (lset)
(letcc hop
(letrec
((A (lambda (lset)
(cond
((null? (car lset)) (hop (quote ())))
((null? (cdr lset)) (car lset))
(else
(intersect (car lset)
(A (cdr lset))))))))
(cond
((null? lset) (quote ()))
(else (A lset)))))))
Run Code Online (Sandbox Code Playgroud)
这可能是它在Clojure中的外观:
(defmacro letcc
[name & body]
`(letfn [(~name [arg#]
(throw (ex-info (str '~name) {:name '~name :value arg#})))]
(try ~@body
(catch clojure.lang.ExceptionInfo e#
(if (= '~name (:name (ex-data e#)))
(:value (ex-data e#))
(throw e#))))))
(defn intersectall
[lset]
(letcc hop
(letfn [(A [lset]
(cond (empty? (first lset))
(hop ())
(empty? (rest lset))
(first lset)
:else
(intersect (first lset) (A (rest lset)))))]
(cond (empty? lset)
()
:else
(A lset)))))
Run Code Online (Sandbox Code Playgroud)
我的问题是:你如何letcc
在Clojure 做?
核心Clojure语言不支持一流的延续.这一点,以及JVM没有提供捕获当前延续的方法的事实,意味着无法实现letcc
对所有情况都满意的方法.
但是,在某些情况下可以实现延续.具体来说,如果您拥有所有代码(即必须捕获continuation的代码),那么您可以使用continuation-passing-style(CPS).基本上,您为每个函数添加一个额外的参数.此参数是表示该调用的延续的函数.您通过调用continuation函数"返回"一个值.当然,这种风格本身就很难写 - 但幸运的是,这是一种我们可以通过宏轻松应用于特定代码的转换.
就其本身而言,CPS不适用于不进行尾调用优化(TCO)的平台.因为CPS中任何函数的最后一步是调用另一个函数,没有TCO,堆栈会快速溢出,除了最简单的计算.这个问题可以通过采用thunking和trampolining来解决.
如上所述,您可以使用宏编写自己的CPS转换.但是,我会邀请你结账我的pulley.cps库,它已经为你做了这个.有替代方案,但据我所知,pulley.cps是唯一提供以下所有功能的Clojure库:
call-cc
/let-cc
try
/ catch
/ finally
)支持binding
形式(它们也是正确的尾递归!)替代方案包括:
binding
失败,因为它不理解try
/ finally
块)并且在4年内没有被触及.do
符号(例如,domonad
宏)极大地模糊了直接和一元风格之间的界限.