何时对唯一的元素求值



在什么位置对唯一元素求值?为每个:

(def set1 #{1, 2, 3, 3})
(def set2 #{1, 2, 3, (+ 1 2)})
(defn foo 
[a b]
#{1, 2, a, b})
(foo 3 (+ 1 2))

它是编译时和运行时的组合还是别的什么?

我猜语法#{...}在读取期间转换为(hash-set ...),并且所有内容在运行期间得到评估,就像任何正常的函数调用一样。在你的例子中,当你调用foo时,它首先计算3,然后是(+ 1 2),然后调用(foo 3 3),然后调用(hash-set 1 2 3 3),导致调用(clojure.lang.PersistentHashSet/create keys),将keys逐个添加到集合中。所以答案是:重复项在运行时被消除。

正确答案是"both"。

对于op的例子,它显然是在运行时完成的,这在repl:

中很容易看到。
user> (defn f [a b]
        (println "f" a b)
        #{1 2 a b})
#'user/f
user> (f 1 2)
f 1 2
IllegalArgumentException Duplicate key: 1  clojure.lang.PersistentHashSet.createWithCheck (PersistentHashSet.java:56)

so: f编译成功,(f 1 2)编译成功,执行时抛出异常。

正如@Hoagy Carmichael的回答中提到的

,同样的东西放入文件并使用clojure编译会抛出错误,但这并不是因为编译器检查重复,而是由于内部编译器在编译后运行顶级表单的行为。这不是编译时错误,而是运行时错误。而且,既然这是内部行为,我想没人能保证将来也会如此。

另一方面,当我粗略地简化了读者的行为时,我错了:它确实检查了传递给#{}form的所有形式的"文字"唯一性。所有这些函数定义都无法编译:
user> (defn f1 [a b]
        #{1 1 a})
IllegalArgumentException Duplicate key: 1 
user> (defn f1 [a b]
        #{1 a a})
IllegalArgumentException Duplicate key: a  
user> (defn f1 [a b]
        #{1 (inc a) (inc a)})
IllegalArgumentException Duplicate key: (inc a)  
user> (defn f1 [a b]
        #{1 @a @a}) ;; notice that at run-time `@a` could easily produce different vals. But the reader sees equal forms.
IllegalArgumentException Duplicate key: (clojure.core/deref a)  
user> (defn f1 [a b]
        #{1 (+ a b) (+ a b)})
IllegalArgumentException Duplicate key: (+ a b)  

tl;dr:对于OP的代码中的每一个case:编译时间

tl;dr++:虽然编译时检查哈希集字面量(#{...}) 总是发生,因为可能发生副作用,某些情况(不是全部)在运行时额外检查

尝试1

首先,将op的名称空间声明添加到OP的代码中,并将其放入名为op.clj的文件中,

;; op.clj
(ns op)
(def set1 #{1, 2, 3, 3})
(def set2 #{1, 2, 3, (+ 1 2)})
(defn foo 
[a b]
#{1, 2, a, b})
(foo 3 (+ 1 2))
第二,将clojure.jar放入文件 的目录中第三,创建一个名为compile 的快速构建脚本
#!/bin/bash
java -cp ".:clojure.jar" clojure.main - <<CLJ
(set! *compile-path* ".")
(compile 'op)
CLJ

第四,使compile可执行,并运行它

chmod +x compile
./compile

第五,接收堆栈跟踪

Exception in thread "main" java.lang.IllegalArgumentException: Duplicate key: 3, compiling:(op.clj:3:24)
    at clojure.lang.Compiler.compile(Compiler.java:7663)
    at clojure.lang.RT.compile(RT.java:408)
    at clojure.lang.RT.load(RT.java:453)
    at clojure.lang.RT.load(RT.java:421)
    at clojure.core$load$fn__7645.invoke(core.clj:6008)
    at clojure.core$load.invokeStatic(core.clj:6007)
    at clojure.core$load.doInvoke(core.clj:5991)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at clojure.core$load_one.invokeStatic(core.clj:5812)
    at clojure.core$compile$fn__7650.invoke(core.clj:6018)
    at clojure.core$compile.invokeStatic(core.clj:6018)
    at clojure.core$compile.invoke(core.clj:6010)
    at user$eval13.invokeStatic(Unknown Source)
    at user$eval13.invoke(Unknown Source)
    at clojure.lang.Compiler.eval(Compiler.java:6951)
    at clojure.lang.Compiler.load(Compiler.java:7403)
    at clojure.lang.Compiler.load(Compiler.java:7350)
    at clojure.core$load_reader.invokeStatic(core.clj:4039)
    at clojure.main$script_opt.invokeStatic(main.clj:336)
    at clojure.main$script_opt.invoke(main.clj:331)
    at clojure.main$main.invokeStatic(main.clj:422)
    at clojure.main$main.doInvoke(main.clj:385)
    at clojure.lang.RestFn.applyTo(RestFn.java:137)
    at clojure.lang.Var.applyTo(Var.java:700)
    at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: Duplicate key: 3
    at clojure.lang.PersistentHashSet.createWithCheck(PersistentHashSet.java:68)
    at clojure.lang.LispReader$SetReader.invoke(LispReader.java:1271)
    at clojure.lang.LispReader$DispatchReader.invoke(LispReader.java:793)
    at clojure.lang.LispReader.read(LispReader.java:265)
    at clojure.lang.LispReader.readDelimitedList(LispReader.java:1302)
    at clojure.lang.LispReader$ListReader.invoke(LispReader.java:1151)
    at clojure.lang.LispReader.read(LispReader.java:265)
    at clojure.lang.LispReader.read(LispReader.java:198)
    at clojure.lang.Compiler.compile(Compiler.java:7561)
    ... 24 more
第六,回顾第一行(重点是我的):线程"main"中的异常java.lang.IllegalArgumentException: Duplicate key: 3, 编译:(op.clj:3:24). 第七,知道这发生在编译时,你就放心了。

尝试2

把你的op.clj文件改成这样…

;; op.clj
(ns op)
(defn foo 
[a b]
#{1, 2, a, b})
(foo 3 (+ 1 2))

尝试再次运行compile,您将获得另一个堆栈跟踪以以下内容开始(强调我的):线程"main"中的异常java.lang.IllegalArgumentException:重复键:3,编译:(op.clj:3:24)。

编译时再次攻击!

变得疯狂

创建一个名为three的文件,其中只包含数字3:

3

修改op.clj如下:

;; op.clj
(ns op
  (:require [clojure.string :as s]))
(defn foo 
[a b]
#{1, 2, a, b})
(foo 3 (-> "three" slurp s/trim Integer/parseInt))

当试图运行compile时,我们再次遇到了我们亲爱的编译时朋友(强调我的):线程"main"中的异常java.lang.IllegalArgumentException: Duplicate key: 3, 编译:(op.clj:9:5)。

现在修改three以包含单个4

4

运行compile,注意没有堆栈跟踪!我们已经杀死了那头野兽!

现在再次编辑three,只包含数字3

3

删除op.clj,这样我们就可以确定只使用下位编译的类文件。

rm op.clj

启动一个repl和请求op

java -cp ".:clojure.jar" clojure.main
Clojure 1.9.0-alpha11
user=> (require 'op)
IllegalArgumentException Duplicate key: 3  clojure.lang.PersistentHashSet.createWithCheck (PersistentHashSet.java:56)

终于!运行时错误!我花了一点功夫才平静下来!

最新更新