生成数组时出现ArityException

时间:2016-12-02 08:14:47

标签: clojure

所以我在这里有这段代码,它会生成如下数组:

(gen_array 10 3) -> [10 10 10] (puts 3 10's into array)
(gen_array "Hello" 3) -> ["Hello" "Hello" "Hello"]

但它不起作用,并在ArityException上返回thread "main"

这是一个更详细的错误:

Exception in thread "main" clojure.lang.ArityException: Wrong number of args (2) passed to: PersistentVector

以下是代码:

(ns test.core
  (:gen-class))

(defn gen_array [n times]
  ((def out []) (for [x (range times)] (conj out n)) out))

(defn -main []
  (println (gen_array 10 3)))

2 个答案:

答案 0 :(得分:1)

你的代码有一些概念性的语义和风格误解:

首先,实际错误是由于您尝试将(def out [])作为函数返回的内容调用而引起的。因为它返回一个向量,所以将它作为函数调用是完全可以的,如果你将索引作为参数传递给它,但是在这里传递两个参数,即(for [x (range times)] (conj out n))out,所以它结束了有一个arity例外。

第二:即使你将它重写为

(defn gen_array [n times] 
  (def out []) 
  (for ...))

或(这是引入绑定的正确方法)

(defn gen_array [n times]
  (let [out []]
    (for [x (range times)] (conj out n))
    out))

它仍然无法添加到out,因为clojure的数据是不可变的,for是懒惰的,所以你总是会得到空列表:< / p>

user> (gen_array :x 10)
;; []

使用for完成此任务的正确方法是:

user> (defn gen-array [n times]
        (vec (for [_ (range times)] n)))
;; #'user/gen-array

user> (gen-array :x 10)
;; [:x :x :x :x :x :x :x :x :x :x]

但最简单的是:

user> (defn gen-array [n times]
        (vec (repeat times n)))
;; #'user/gen-array

user> (gen-array :x 10)
;; [:x :x :x :x :x :x :x :x :x :x]

答案 1 :(得分:0)

Leetwinski已经回答得很好,但我想补充一些东西,并以不同的方式解释问题。

我认为你错过了这一点,因为你用Java或类似的语言进行编程。

第一个错误:初始集合的定义

在Clojure中,def创建一个全局变量(在命名空间的上下文中),它也存在于函数之外。 但是在这里,你定义的累加器只需要存在于函数内部,就像在类方法中一样(命名空间是一个类,函数是编译后它的子类)。

为此目的,正确的方法是使用let,然后您的对象out仅存在于let的上下文中:

(let [a 2]
  ...do a lot of thing with your local a...
 ) ;; after that, you cannot call a anymore

第二个错误:out不可变

Clojure标准对象不可变,i.e.使用f作为函数执行(f值)时,会隐式创建新对象,并且值本身不会更改:

(def value 2)
(inc value)
value ;; 2

所以即使你这样做了:

(defn gen_array [n times]
  (let [out []]
    (for [x (range times)] (conj out n))))

(gen_array 2 2)

您将返回([2] [2])

第三个错误:您认为for和Java一样

因为Clojure用于通过迭代输入集合来创建集合,如最后一个示例所示。实际上,当您将for与单个集合一起使用时,它等同于map

在这里,你当然可以使用for但是你最初考虑的是(在向量中积累值)是通过使用更接近Java的reduceloop/recur来实现的。循环比formap迭代现有集合来转换值(它就像迭代器一样)。可以使用doseqdotimes

各种解决方案

我将重写上面给出的解决方案(当然repeat是最好的)但是这里有一些解决方案可以向您展示如何实现您的想法(Java风格):

;; Note that many of these pieces of code are not idiomatic, just for fun

;; Reduce implementation
(defn gen_array [n times]
  (reduce
    (fn [agg n]
      (conj agg n))
    [] (range times))) ;; out is []


;; Uses transitory transient collection
(defn gen_array [n times]
  (persitent!
    (reduce
      (fn [agg n]
        (conj! agg n))
      (transient []) (range times))))

;; Uses an atom, which is mutable

(defn gen_array [n times]
  (let [agg (atom [])
        _ (dotimes [_ times] (swap! agg conj n))]
    @agg))

;; Loop/recur version

(defn gen_array [n times]
  (loop [out []
         i 0]
    (if (= times i)
        out
        (recur (conj out n) (inc i)))))

;; Real Java Object[] assuming you want to pass any Object !

(defn gen_array [n times]
  (let [out (object-array n)]
    (amap out idx ret n)))