论Clojure的"第一"功能

Ham*_*jan 7 performance clojure

我在Rich的视频中看到了以下关于序列http://blip.tv/file/734409的示例, 大约需要33-36分钟:

(first "abcd") => \a
Run Code Online (Sandbox Code Playgroud)

现在,他说这扩展到(有点):

(first "abcd") => (first (seq "abcd")) => (first '(\a \b \c \d))
Run Code Online (Sandbox Code Playgroud)

因此,它看起来像一个O(N)操作,因为正在创建字符串的完整副本.首先,如果a String是不可变的,那为什么要复制?(编辑:根据答案,它可能不是;只是在打印时看起来那样.)其次,假设first在Java中的其他东西上操作是可变的,比如一个整数的链表.应该first以懒惰的方式行事(例如,首先创建一个持久的序列)?将它立即评估并保存是否有意义?我认为,这将是一种破坏漂亮抽象的黑客,但能快速完成工作.当你打电话时(seq "abcd"),你不知道它将如何使用.当你打电话给firsta时seq,你知道该怎么做.可是,当你调用first"abcd",我认为执行一个hacky和快速"抓住并保存它",方法比抓住一个序列然后调用更好first.

我错过了什么吗?Rich Hickey跳过了一些步骤吗?

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

Rob*_*lan 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);
          .
          .
          .
Run Code Online (Sandbox Code Playgroud)

所以,这是一个java问题,而不是一个clojure问题.我没有检查,但我相当确定CharSequence做了"正确的事情".

  • 没有必要将999项复制到seq中,因为seq只是根据需要使用底层数据结构,并且出于性能和不变性的原因,缓存它找到的内容.如果你在某个集合上调用`first`(例如,你的链表),那么seq需要查看的那个集合的唯一元素就是第一个.基础集合中尚未观察到的元素可能会发生变异,恕不另行通知.只有当seq实际读取一个元素时,该元素才会在seq中"持久". (2认同)

lev*_*and 4

其他答案是正确的,但我将利​​用这个机会指出 Clojure 不变性哲学的一个有趣的效果。

因此,它看起来像是一个 O(N) 操作,因为正在制作字符串的完整副本。

与 Clojure 中的其他数据结构一样,字符串是不可变的。(它们是一个特例,是在 JVM 而不是 Clojure 中实现的,但这对于这一点来说并不重要。)

不可变对象的副本是免费的。没错,免费。因为你根本不需要复制。内存中分配的同一对象可以简单地重复使用,因为它保证始终与“复制”时相同。

所以像这样的函数seq 永远不需要实际复制任何东西。它只是直接对传递的任何内容进行操作,并返回一个(可能是惰性的)序列,该序列为您调用的任何内容提供抽象接口seq

所以,seq总是 O(1)。