追求 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
表单之外。一种选择是使用 def
将 zipmap 调用的结果存储在 var 中,如 (def my-const (zipmap "TJQK"(iterate inc 10 )))
。
但是,在创建匿名函数的情况下,创建全局可访问的 var 可能有点过分了。因此,我建议将 fn
放入捕获常量值的 let
绑定(bind)中:
(let [face-cards (zipmap "TJQK" (iterate inc 10))]
(fn [ch]
(or
({\A 1} ch)
(face-cards ch)
(- (int ch) (int \0)))))
编辑:正如评论中所指出的,该解决方案将在加载时而不是编译时计算常量值。我很难想出一个重要的场景,但值得指出的是,它的语义与您的方法略有不同。
关于clojure - 如何让 Clojure 在编译时计算常量局部表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22813713/