Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Types 为什么Clojure变量arity参数会根据使用情况获得不同的类型?_Types_Clojure_Variadic Functions - Fatal编程技术网

Types 为什么Clojure变量arity参数会根据使用情况获得不同的类型?

Types 为什么Clojure变量arity参数会根据使用情况获得不同的类型?,types,clojure,variadic-functions,Types,Clojure,Variadic Functions,在回答时,我遇到了Clojure的变量arity函数args中我没有预料到的东西: user=> (defn wtf [& more] (println (type more)) :ok) #'user/wtf ;; 1) user=> (wtf 1 2 3 4) clojure.lang.ArraySeq :ok ;; 2) user=> (let [x (wtf 1 2 3 4)] x) clojure.lang.ArraySeq :ok ;; 3) user

在回答时,我遇到了Clojure的变量arity函数args中我没有预料到的东西:

user=> (defn wtf [& more] (println (type more)) :ok)
#'user/wtf

;; 1)
user=> (wtf 1 2 3 4)
clojure.lang.ArraySeq
:ok

;; 2)
user=> (let [x (wtf 1 2 3 4)] x)
clojure.lang.ArraySeq
:ok

;; 3)
user=> (def x (wtf 1 2 3 4))
clojure.lang.PersistentVector$ChunkedSeq
#'user/x
user=> x
:ok

为什么1)和2)中的类型
ArraySeq
,而3)中的类型
PersistentVector$ChunkedSeq

Clojure在很大程度上不是根据类实现的,而是根据接口和协议实现的(java接口上的Clojure抽象,具有一些额外的特性)

好的Clojure代码在
ArraySeq
PersistentVector$ChunkedSeq
的比较中不起作用,而是调用
IndexedSeq
IReduce
ASeq
等公开的方法或协议函数,如果它们的参数实现了它们。或者更可能使用基本的
clojure.core
函数,这些函数是根据这些接口或协议实现的

例如,请注意
reduce
的定义:

user> (source reduce)
(defn reduce
  "f should be a function of 2 arguments. If val is not supplied,
  returns the result of applying f to the first 2 items in coll, then
  applying f to that result and the 3rd item, etc. If coll contains no
  items, f must accept no arguments as well, and reduce returns the
  result of calling f with no arguments.  If coll has only 1 item, it
  is returned and f is not called.  If val is supplied, returns the
  result of applying f to val and the first item in coll, then
  applying f to that result and the 2nd item, etc. If coll contains no
  items, returns val and f is not called."
  {:added "1.0"}
  ([f coll]
     (clojure.core.protocols/coll-reduce coll f))
  ([f val coll]
     (clojure.core.protocols/coll-reduce coll f val)))
nil
如果查找
coll reduce
,您会发现基于实现的接口或协议的各种实现:


简短回答:这是Clojure的一个模糊实现细节。该语言唯一能保证的是变量函数的rest参数将作为
clojure.lang.ISeq
的实例传递,如果没有其他参数,则作为
nil
的实例传递。您应该相应地编写代码


长答案:它与函数调用是编译还是简单计算有关。不必对计算和编译之间的区别进行完整的论述,只需知道Clojure代码被解析为AST就足够了。根据上下文的不同,AST中的表达式可以直接进行计算(类似于解释),也可以作为动态生成的类的一部分编译成Java字节码。后一种情况发生在lambda表达式的主体中,它将计算为实现
IFn
接口的动态生成类的实例。有关评估的更详细说明,请参见

在大多数情况下,编译代码和计算代码之间的差异对程序来说是看不见的;他们将以完全相同的方式行事。这是编译和评估导致细微不同行为的极少数情况之一。但是,需要指出的是,这两种行为都是正确的,因为它们都符合语言的承诺

Clojure代码中的函数调用被解析为
Clojure.lang.Compiler
中的
InvokeExpr
实例。如果正在编译代码,则编译器将发出字节码,该字节码将使用适当的arity()调用
IFn
对象上的
invoke
方法。如果只是对代码求值而没有编译,那么函数参数将捆绑在一个
PersistentVector
中,并传递给
IFn
对象()上的
applyTo
方法

具有可变参数列表的Clojure函数被编译成类的子类。此类实现了IFn的所有方法,收集参数,并将其分派到相应的
doInvoke
arity。您可以在
applyTo
的实现中看到,在0个必需参数的情况下(与
wtf
函数的情况一样),输入序列被传递到
doInvoke
方法,并且对函数实现可见。同时,4-arg版本的
invoke
将参数捆绑在
ArraySeq
中,并将其传递给
doInvoke
方法,因此现在您的代码将看到一个
ArraySeq

更复杂的是,Clojure的
eval
函数(REPL正在调用的函数)的实现将在内部将正在评估的列表表单包装到thunk(一个异常的、无参数的函数)中,然后编译并执行thunk。因此,几乎所有调用都使用对
invoke
方法的编译调用,而不是由编译器直接解释。
def
表单有一个特例,它在不编译的情况下显式地计算代码,这解释了您在那里看到的不同行为

clojure.core/apply
的实现也调用了
applyTo
方法,通过这种逻辑,传递给
apply
的任何列表类型都应该在函数体中看到。事实上:

user=> (apply wtf [1 2 3 4])
clojure.lang.PersistentVector$ChunkedSeq
:ok

user=> (apply wtf (list 1 2 3 4))
clojure.lang.PersistentList
:ok

它变得更好:
((fn[](def x(wtf 1 2 3 4)))
我同意这里所说的,但它没有解决问题。我可能没有足够明确地将它与问题联系起来,但这是我尝试的“为什么”(而@Alex解决的是“如何”)。我的问题可能更为尖锐。尽管如此,我还是很感谢你抽出时间来回答。谢谢你。你能澄清一下“显式地计算代码而不编译”是什么意思吗?计算未编译的代码意味着什么?Clojure代码被解析为AST。根据上下文的不同,AST中的表达式可以直接进行计算(类似于解释),也可以作为动态生成的类的一部分编译成Java字节码。发生这种情况的典型案例是lambda表达式的主体,它将计算为实现
IFn
接口的动态生成类的实例。我知道这偏离了主题,但。。。没有编译成字节码的东西:你说的是像符号解析这样的东西,以及像关键字、字符串、数字等这样的对自己进行计算的形式吗?是的,事实证明,由于计算将大多数列表形式封装在匿名
fn
中,所以你提到的那些情况,以及
def
形式的特例,是唯一可以避免字节码生成的实例。如果您能提供
(extend-protocol CollReduce
  nil
  (coll-reduce
   ([coll f] (f))
   ([coll f val] val))

  Object
  (coll-reduce
   ([coll f] (seq-reduce coll f))
   ([coll f val] (seq-reduce coll f val)))

  clojure.lang.IReduce
  (coll-reduce
   ([coll f] (.reduce coll f))
   ([coll f val] (.reduce coll f val)))

  ;;aseqs are iterable, masking internal-reducers
  clojure.lang.ASeq
  (coll-reduce
   ([coll f] (seq-reduce coll f))
   ([coll f val] (seq-reduce coll f val)))
  ...) ; etcetera
user=> (apply wtf [1 2 3 4])
clojure.lang.PersistentVector$ChunkedSeq
:ok

user=> (apply wtf (list 1 2 3 4))
clojure.lang.PersistentList
:ok