追求4Clojure Problem 178 - Best Hand,我这样做是为了将卡值从字符转换为数字:

 (fn [ch]
   (or
    ({\A 1} ch)
    ((zipmap "TJQK" (iterate inc 10)) ch)
    (- (int ch) (int \0))))


在每次调用时都会评估zipmap表达式,始终生成{\K 13, \Q 12, \J 11, \T 10}

我们如何使编译器仅对其进行一次评估?



经过一番弯腰,我想到了

(defmacro constant [exp] (eval exp))


...因此包裹zipmap调用:

(constant (zipmap "TJQK" (iterate inc 10)))


我认为这相当于

(eval '(zipmap "TJQK" (iterate inc 10)))


...但不带引号的eval

(eval (zipmap "TJQK" (iterate inc 10)))


欢迎更正,评论和改进。

最佳答案

您的constant宏将适用于这种特殊情况,因为被评估的表单仅具有解析为clojure.core和编译时文字的函数的符号。在其他情况下,您可能会遇到符号解析的问题,例如从函数参数计算局部常量以用于匿名函数。

一种更通用的方法是将调用移至zipmap表单之外的fn。一种选择是使用zipmapdef调用的结果存储在var中,就像在(def my-const (zipmap "TJQK" (iterate inc 10)))中一样。

但是,在这种情况下,当您要执行匿名函数时,创建全局可访问的var可能会过大。因此,我建议将fn放在捕获恒定值的let绑定中:

 (let [face-cards (zipmap "TJQK" (iterate inc 10))]
   (fn [ch]
     (or
       ({\A 1} ch)
       (face-cards ch)
       (- (int ch) (int \0)))))


编辑:正如评论中指出的那样,此解决方案将在加载时而不是编译时计算常数值。我很难提出一个重要的方案,但是值得指出的是,它的语义与您的方法略有不同。

08-27 17:04