是否可以使用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中进行交易,以获得其他好处。