clojure 的 `into` 在 common lisp 中

clojure's `into` in common lisp

clojure 有一个方便的 (into to-coll from-coll) 函数,将元素从 from-coll 添加到 to-coll,保留 to-coll 的类型。

如何在普通的 lisp 中实现这一点? 第一次尝试是

(defun into (seq1 seq2)
  (concatenate (type-of seq1) seq1 seq2))

但这一个显然失败了,因为 type-of 在其结果中包含向量的长度,不允许添加更多元素(从 sbcl 开始),尽管它仍然适用于列表作为第一个参数 (虽然仍然因空列表而失败)。

问题是:是否可以在不使用通用方法and/or复杂type-of结果处理(例如删除vectors/arrays等的长度)的情况下构成这种功能?

我同意 into 充当 append(与 clojure 不同,其中 into 结果取决于目标集合类型)我们称它为 concat-into

在 Clojure 中,当您使用 into 时,您对第一个集合的类型有一个具体的想法(大部分时间),因为它改变了语义:如果它是一个列表,附加元素将conj 放在前面,如果它是矢量,它们将 conj 放在后面,如果它是地图,您需要提供地图条目指示符(即实际的地图条目或两个-元素向量),集合更灵活,但也有自己的语义。这就是为什么我猜测直接使用 concatenate,显式提供类型,可能足以适合许多用例。

除此之外,我认为扩展此功能可能会有用(Common Lisp 只有一组封闭的序列类型),但为此,使用泛型函数似乎太方便而无法忽略。提供可扩展、通用和高性能的解决方案并非易事。

编辑:总而言之:不,你无法通过巧妙地应用一两个“内置函数”来获得这种行为,但你当然可以使用通用函数编写一个可扩展的通用解决方案。

好的,我唯一想到的(除了泛型方法)就是这个非常简单的函数:

(defun into (target source)
  (let ((target-type (etypecase target
                       (vector (list 'array (array-element-type target) (*)))
                       (list 'list))))
    (concatenate target-type target source)))

CL-USER> (into (list 1 2 4) "asd")
;;=> (1 2 4 #\a #\s #\d)

CL-USER> (into #*0010 (list 1 1 0 0))
;;=> #*00101100

CL-USER> (into "asdasd" (list #\a #\b))
;;=> "asdasdab"

也是简单的 empty 实现:

(defun empty (target)
  (etypecase target
    (vector (make-array 0
                        :element-type (array-element-type target)
                        :adjustable t :fill-pointer 0))
    (list)))

结果确实(如 @Svante 所述)没有确切的类型,而是 "the collection with the element type being the same as that of target"。它不符合 clojure 的协议(list 目标应该放在前面)。

看不出它有什么缺陷(如果有的话),所以很高兴听到这个消息。无论如何,因为它只是为了教育,所以就可以了。