何时对独特元素进行评估?

时间:2016-08-28 12:34:05

标签: clojure compilation

在什么时候评估独特元素的集合。每个人:

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

它是编译时和运行时之间的组合吗?

2 个答案:

答案 0 :(得分:2)

我想语法#{...}在读取时间内转换为(hash-set ...),并且所有内容都在运行时进行评估,就像任何正常的函数调用一样。在你打电话给foo的情况下,它首先评估3,然后评估(+ 1 2),然后拨打(foo 3 3),然后调用(hash-set 1 2 3 3)来调用(clojure.lang.PersistentHashSet/create keys) {1}},将keys逐个添加到集合中。所以答案是:在运行时删除重复项。

<强>更新

正确答案是“两个”。

至于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)

所以:f编译正常,(f 1 2)编译正常,执行时抛出异常。

正如在@Hoagy Carmichael的回答中所提到的,同样放入文件并使用clojure编译会引发错误,但这不是因为编译器检查重复,而是由于内部编译器的行为在运行顶级表单之后汇编。所以它不是真正的编译时间,它是运行时错误。此外,由于它是内部行为,我想,没人会保证将来会这样。

另一方面,当我大致简化了读者的行为时,我错了:它确实检查了传递给#{}形式的所有表单的“文字”唯一性。所有这些函数定义都无法编译:

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)  

答案 1 :(得分:0)

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

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:重复键: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:Duplicate key:3,编译 :( op.clj:3:24)。

再次编译时间

变得疯狂

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

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:重复键:3 ,编译 :( op.clj:9:5)。

现在修改three以包含一个

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)

最后! 运行时错误!并且需要花费一些工作才能完成!