clojure - 如何让 Clojure 在编译时计算常量局部表达式

标签 clojure

追求 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/

相关文章:

Clojure - 为什么 into 对列表的行为不同于对向量的行为?

mysql - 使用 clojure.java.jdbc 从 MySQL 流式传输

clojure - 为 Clojure 的序列添加自定义行为

clojure - "SSL doesn' t have a valid keystore"尝试连接到 Datomic Cloud 时出错

java - 使用 Leiningen 制作 uberjar 时如何针对特定版本的 java

clojure - 如何在 clojure 中获取当前日期 YYYYMMDD?

java - 调用动态生成名称的类的方法

clojure - 使用 reduce 避免 clojure 中的 StackOverflowError

ide - ClojureCLR 编辑器

java - 使 Java 类在 Clojure 中作为序列工作