我在Rich关于序列的视频中看到了以下示例 http://blip.tv/file/734409 大约33-36分钟:
(first "abcd") => \a
现在,他说这扩展到(有点):
(first "abcd") => (first (seq "abcd")) => (first '(\a \b \c \d))
因此,它看起来像O(N)
操作,因为正在制作字符串的完整副本。首先,如果String
是不可变的,那为什么要复制它? (编辑:基于答案,它可能不是;只是在打印时看起来那样。)其次,假设first
在Java中的其他东西上操作是可变的,比如一个整数的链表。 first
应该以懒惰的方式行事(例如,首先创建一个持久的序列)?将它立即评估并保存是否有意义?我认为,这将是一种破坏漂亮抽象的黑客,但能快速完成工作。当您致电(seq "abcd")
时,您不知道它将如何使用。当您在first
上拨打seq
时,您知道该怎么做。但是,当您在first
上致电"abcd"
时,我认为执行hacky并快速“抓住并保存”,方法比抓住序列更好,然后调用first
。
我错过了什么吗? Rich Hickey跳过了一些步骤吗?
如果我有疑问,请告诉我。谢谢!
答案 0 :(得分:6)
并不表示正在制作完整的字符串副本。 (但这是一个很好的问题。)
在clojure.lang.RT的源代码中,您会注意到运行时使用charsequence来创建序列:
static ISeq seqFrom(Object coll){
if(coll instanceof Seqable)
return ((Seqable) coll).seq();
else if(coll == null)
return null;
else if(coll instanceof Iterable)
return IteratorSeq.create(((Iterable) coll).iterator());
else if(coll.getClass().isArray())
return ArraySeq.createFromObject(coll);
else if(coll instanceof CharSequence)
return StringSeq.create((CharSequence) coll);
.
.
.
所以,这是一个java问题,而不是一个clojure问题。我没有检查过,但我相当确定CharSequence做了“正确的事情”。
答案 1 :(得分:4)
其他答案是正确的,但我会利用这个机会指出Clojure不变性哲学的有趣影响。
因此,它看起来像是一个O(N)操作,因为正在制作字符串的完整副本。
与Clojure中的其他数据结构一样,字符串是不可变的。 (它们是一个特殊情况,在JVM而不是Clojure中实现,但这在这一点上并不重要。)
不可变对象的副本 free 。那是对的,免费的。因为你根本不需要复制。内存中相同的已分配对象可以简单地重复使用,因为它保证始终与“复制”时相同。
所以像seq
这样的函数永远必须实际复制任何东西。它只对直接传递的任何内容进行操作,并返回一个(可能是懒惰的)序列,该序列为您调用seq
的任何内容提供抽象接口。
所以,seq
总是O(1)。
答案 2 :(得分:3)
您可以查看seq
,就好像它根据字符串创建一个惰性序列一样。
它实际上并没有创建一个惰性序列,它实际上是CharSequence的java实现的短路,但这是一个实现细节。
从复杂的角度来看,first
上的O(1)
为seq
,您可以在任何可迭代的任何时间内创建seq
。