论Clojure的“第一”功能

时间:2010-10-03 16:55:02

标签: performance clojure

我在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跳过了一些步骤吗?

如果我有疑问,请告诉我。谢谢!

3 个答案:

答案 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