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
目标应该放在前面)。
看不出它有什么缺陷(如果有的话),所以很高兴听到这个消息。无论如何,因为它只是为了教育,所以就可以了。
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
目标应该放在前面)。
看不出它有什么缺陷(如果有的话),所以很高兴听到这个消息。无论如何,因为它只是为了教育,所以就可以了。