是否可以将Clojure的案例表单与Java枚举一起使用?

pro*_*ron 10 clojure clojure-java-interop

case医生说

与cond和condp不同,case执行常量时间调度......所有常量表达式都是可以接受的.

我想从caseJava枚举上的常量时间调度中获益.Java的switch声明适用于枚举,但在Clojure中执行以下操作:

(defn foo [x] 
   (case x 
      java.util.concurrent.TimeUnit/MILLISECONDS "yes!"))

(foo java.util.concurrent.TimeUnit/MILLISECONDS)
Run Code Online (Sandbox Code Playgroud)

结果是: IllegalArgumentException No matching clause: MILLISECONDS

枚举是否不受支持case?难道我做错了什么?我必须诉诸cond或有更好的解决方案吗?

Bey*_*mor 6

这里的问题是case,如文档中所述,测试常量"必须是编译时文字 ".因此,不是解析,而是java.util.concurrent.TimeUnit/MILLISECONDS对文字符号'java.util.concurrent.TimeUnit/MILLISECONDS进行测试.

(foo java.util.concurrent.TimeUnit/MILLISECONDS) ; IllegalArgumentException
(foo 'java.util.concurrent.TimeUnit/MILLISECONDS) ; yes!
Run Code Online (Sandbox Code Playgroud)

相反,解决方案是.ordinalEnum实例上进行调度,这是Java switch在枚举时编译语句时所执行的操作:

(defn foo [x]
  (case (.ordinal x)
    2 "yes!"))
Run Code Online (Sandbox Code Playgroud)

您可以将此模式包装在宏中,以便为您正确评估案例序数:

(defmacro case-enum
  "Like `case`, but explicitly dispatch on Java enum ordinals."
  [e & clauses]
  (letfn [(enum-ordinal [e] `(let [^Enum e# ~e] (.ordinal e#)))]
    `(case ~(enum-ordinal e)
       ~@(concat
          (mapcat (fn [[test result]]
                    [(eval (enum-ordinal test)) result])
                  (partition 2 clauses))
          (when (odd? (count clauses))
            (list (last clauses)))))))
Run Code Online (Sandbox Code Playgroud)

  • cemerick在http://cemerick.com/2010/08/03/enhancing-clojures-case-to-evaluate-dispatch-values/上有一个写作和解决方法. (3认同)

Fed*_*tti 6

您可以在枚举名称上使用 cond

(case (.name myEnumValue) "NAME_MY_ENUM" (println "Hey, it works!"))

与替代方案相比,在我看来非常简单