seq to vec conversion - 键必须是整数

时间:2014-08-19 16:20:11

标签: clojure

我想在向量中获取nil元素的索引,例如。 [1 nil 3 nil nil 4 3 nil] => [1 3 4 7]

(defn nil-indices [vec]
  (vec (remove nil? (map
    #(if (= (second %) nil) (first %))
      (partition-all 2 (interleave (range (count vec)) vec)))))
  )

运行此代码会导致

  

java.lang.IllegalArgumentException:键必须是整数   (NO_SOURCE_FILE:0)

如果我忽略围绕一切的(vec)调用,它似乎有效,但返回序列而不是向量。

谢谢!

2 个答案:

答案 0 :(得分:4)

请改为尝试:

(defn nil-indices [v]
  (vec (remove nil? (map
    #(if (= (second %) nil) (first %))
      (partition-all 2 (interleave (range (count v)) v))))))

Clojure是一个LISP-1:它有一个函数和数据的命名空间,所以当你调用(vec ...)时,你试图将结果序列传递给数据参数,而不是标准库vec函数。

答案 1 :(得分:1)

请参阅问题的其他答案(您正在隐藏vec),但请考虑使用更简单的方法。

map可以接受多个参数,在这种情况下,它们作为附加参数传递给map函数,例如(map f c1 c2 ...)调用(f (first c1) (first c2) ...)等,直到其中一个序列参数用完为止。

这意味着您的(partition-all 2 (interleave ...))是一种非常详细的说法(map list (range) v)。还有一个函数map-indexed可以执行相同的操作。但是,它只需要一个序列参数,因此(map-indexed f c1 c2)不合法。

以下是使用map-indexed,线程和nil?重写您的函数的清晰度:

(defn nil-indices [v]
  ; Note: map fn called like (f range-item v-item)
  ;       Not like (f (range-item v-item)) as in your code.
  (->> (map-indexed #(when (nil? %2) %1) v) ;; like (map #(when ...) (range) v)
       (remove nil?)
       vec))

但是,您可以使用简化和reduce-kv功能来执行此操作。此函数类似于reduce,但减少函数接收三个参数而不是两个:累加器,集合中项目的(向量索引,映射键),以及项目本身。使用reduce-kv,您可以更清楚地重写此功能(并且它可能运行得更快,尤其是瞬态):

(defn nil-indices [v]
  (reduce-kv #(if (nil? %3) (conj %1 %2) %1) [] v))