追求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
。一种选择是使用zipmap
将def
调用的结果存储在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)))))
编辑:正如评论中指出的那样,此解决方案将在加载时而不是编译时计算常数值。我很难提出一个重要的方案,但是值得指出的是,它的语义与您的方法略有不同。