追求4Clojure 问题178 - 最好的手,我有这个用于将卡值从字符转换为数字:
(fn [ch]
(or
({\A 1} ch)
((zipmap "TJQK" (iterate inc 10)) ch)
(- (int ch) (int \0))))
Run Code Online (Sandbox Code Playgroud)
在zipmap
每次通话表达式,总是产生{\K 13, \Q 12, \J 11, \T 10}
.
我们怎样才能让编译器只评估一次?
经过多次脑力激荡,我想到了
(defmacro constant [exp] (eval exp))
Run Code Online (Sandbox Code Playgroud)
...包裹zipmap
呼叫:
(constant (zipmap "TJQK" (iterate inc 10)))
Run Code Online (Sandbox Code Playgroud)
我认为这相当于
(eval '(zipmap "TJQK" (iterate inc 10)))
Run Code Online (Sandbox Code Playgroud)
......但不是eval
没有引用:
(eval (zipmap "TJQK" (iterate inc 10)))
Run Code Online (Sandbox Code Playgroud)
欢迎更正,评论和改进.
您的constant
宏将适用于此特定情况,因为正在评估的表单只包含解析函数clojure.core
和编译时文字的符号.在其他情况下,您可能会遇到符号解析问题,例如从函数参数计算本地常量以在匿名函数中使用.
更通用的方法是将调用移到表单zipmap
外部fn
.一种选择是将zipmap
调用结果存储在var中def
,如下所示(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)))))
Run Code Online (Sandbox Code Playgroud)
编辑:正如评论中指出的,此解决方案将在加载时计算常量值,而不是在编译时计算.我很难想出一个重要的场景,但值得指出的是它的语义与你的方法略有不同.
归档时间: |
|
查看次数: |
561 次 |
最近记录: |