追求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)))
欢迎更正,评论和改进。
答案 0 :(得分:5)
您的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)))))
编辑:正如评论中所指出的,此解决方案将在加载时计算常量值,而不是在编译时计算。我很难想出一个重要的场景,但值得指出的是它的语义与你的方法略有不同。