是否可以使用Clojure';s带有Java枚举的case表单?

是否可以使用Clojure';s带有Java枚举的case表单?,clojure,clojure-java-interop,Clojure,Clojure Java Interop,casedoc说 与cond和condp不同,case执行恒定时间调度。。。各种各样的常数 表达是可以接受的情况 我想从case的恒定时间调度中获益,以匹配Java枚举。Java的switch语句可以很好地处理Enum,但在Clojure中执行以下操作: (defn foo [x] (case x java.util.concurrent.TimeUnit/MILLISECONDS "yes!")) (foo java.util.concurrent.TimeUnit/M

case
doc说

与cond和condp不同,case执行恒定时间调度。。。各种各样的常数 表达是可以接受的情况

我想从
case
的恒定时间调度中获益,以匹配Java枚举。Java的
switch
语句可以很好地处理Enum,但在Clojure中执行以下操作:

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

(foo java.util.concurrent.TimeUnit/MILLISECONDS)
结果为:
IllegalArgumentException无匹配子句:毫秒


案例中是否不支持枚举?我做错什么了吗?我必须求助于
cond
还是有更好的解决方案?

这里的问题是
case
的测试常量,如文档中所述,“必须是编译时文本”。因此,不是解析
java.util.concurrent.TimeUnit/毫秒
,而是测试字面符号
'java.util.concurrent.TimeUnit/毫秒

(foo java.util.concurrent.TimeUnit/MILLISECONDS) ; IllegalArgumentException
(foo 'java.util.concurrent.TimeUnit/MILLISECONDS) ; yes!
相反,解决方案是在
Enum
实例的
.ordinal
上分派,这是Java本身在通过Enum编译
switch
语句时所做的:

(defn foo [x]
  (case (.ordinal x)
    2 "yes!"))
您可以将此模式包装在宏中,该宏可为您正确计算大小写序号:

(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)))))))

您可以在enumm的名称上使用cond


(大小写(.name myEnumValue)
“NAME_MY_ENUM”(println“Hey,it works!”)


在我看来,与备选方案相比非常简单

这里有一个更简单的解决方案,它只在案例中使用等式检查-

(defn cases [v & args]
  (let [clauses (partition 2 2 args)]
    (some #(when (= (first %) v) (second %)) clauses))) 

=> (cases EventType/received EventType/send "A" EventType/received "B")
=> "B"

cemerick对此有一个总结和解决方法,谢谢你们!顺序解决方案(我在宏中使用它,所以可读性不是问题)和cemerick的解决方案都很好。我太草率了:显然cemerick的case+不适用于枚举:
(case+java.util.concurrent.TimeUnit/MINUTES java.util.concurrent.TimeUnit/MINUTES“yes!”;CompilerException java.lang.RuntimeException:无法在代码中嵌入对象
。(但序数解决方案显然会这样做)这失去了使用枚举的好处,因为您必须知道枚举的序数值,而该值可能会更改。它不仅可能会更改,而且很容易意外地输入错误的数字,因为该数字通常与枚举的语义无关。我相信@Federico Tomassetti的答案是这个问题的最佳答案。有没有办法不使用
eval
?可能使用
.name
.hashCode
而不是
.ordinal
,并在生成
case
之前生成子句?或者最终通过复制
clojure.core/case
maybe的代码?这不是一个固定时间的操作。该问题需要一个固定时间的解决方案。此答案应为公认答案。它保持了
case
的恒定时间性能,并保留了
Enum
的语义。它确实会丢失
Enum
的编译时检查,但编译时检查通常在Clojure中进行交易,以获得其他好处。