Clojure中的头部固位

Clojure中的头部固位,clojure,Clojure,在阅读“Clojure编程”(第98页)中关于头部保留的一段时,我无法理解使用示例拆分时会发生什么。我试着用repl做实验,但它让我更加困惑 (time (let [r (range 1e7) a (take-while #(< % 12) r) b (drop-while #(< % 12) r)] [(count a) (count b)])) "Elapsed time: 1910.401711 msecs" [

在阅读“Clojure编程”(第98页)中关于头部保留的一段时,我无法理解使用示例拆分时会发生什么。我试着用repl做实验,但它让我更加困惑

(time (let [r (range 1e7) 
            a (take-while #(< % 12) r)
            b (drop-while #(< % 12) r)]
        [(count a) (count b)]))
"Elapsed time: 1910.401711 msecs"
[12 9999988]

(time (let [r (range 1e7) 
            a (take-while #(< % 12) r)
            b (drop-while #(< % 12) r)]
        [(count b) (count a)]))
"Elapsed time: 3580.473787 msecs"
[9999988 12]

(time (let [r (range 1e7) 
            a (take-while #(< % 12) r)
            b (drop-while #(< % 12) r)]
        [(count b)]))
"Elapsed time: 3516.70982 msecs"
[9999988]
(时间(让[r(范围1e7))
a(花时间#(<%12)r)
b(在#(<%12)r)时放下)]
[(计数a)(计数b)])
“运行时间:1910.401711毫秒”
[12 9999988]
(时间(let[r(范围1e7))
a(花时间#(<%12)r)
b(在#(<%12)r)时放下)]
[(b项)(a项)])
“运行时间:3580.473787毫秒”
[9999988 12]
(时间(let[r(范围1e7))
a(花时间#(<%12)r)
b(在#(<%12)r)时放下)]
[(计数b)])
“运行时间:3516.70982毫秒”
[9999988]

正如您从上一个示例中看到的,如果我不计算
a
,时间会以某种方式增长。我想,我错过了一些东西,但是什么呢?

是O(1)。这就是为什么您的测量不依赖于它。

绑定在
中,让
表单执行,即使我们不使用此值

(let [x (println "Side effect")] 1)
上面的代码打印“副作用”,并返回1

在所有三个示例中,都使用了相同的let形式绑定,因此我看不出有任何区别。顺便说一下,在我的机器上,你所有的代码片段花费的时间大致相等

当你尝试这样的事情时,真正的区别是:

(time (let [r (range 2e7) 
        a (take 100 r)
        b (drop 100 r)]
    [(count a)]))
"Elapsed time: 0.128304 msecs"
[100]

(time (let [r (range 2e7) 
        a (take 100 r)
        b (drop 100 r)]
    [(count b)]))
"Elapsed time: 3807.591342 msecs"
[19999900]

由于
b
a
是惰性序列,
count
O(n)
时间内工作。但在第一个例子中,我们不计算b的计数,所以它几乎可以立即工作

集合的
count
函数为O(1),其中包括向量和列表


另一方面,序列是使它们计数的O(n)。这里的重要部分是函数
在返回序列时执行
在返回序列时执行。事实上,他们也是懒惰的,这并不是一个主要因素。

当使用时间作为基准时,多次运行测试以获得准确的结果

user> (defn example2 [] (let [r (range 1e7)                                             
              a (take-while #(< % 12) r)                                     
              b (drop-while #(< % 12) r)]                        
             [(count a) (count b)]))
#'user/example2

user> (dorun (take 1000 (repeatedly example2)))
nil

user> (time (example2))
"Elapsed time: 614.4 msecs"
[12 9999988]
user>(定义示例2[](let[r(范围1e7))
a(花时间#(<%12)r)
b(在#(<%12)r)时放下)]
[(计数a)(计数b)])
#'用户/示例2
用户>(多伦(取1000(重复示例2)))
无
用户>(时间(示例2))
“运行时间:614.4毫秒”
[12 9999988]
我指责运行时的差异,因为热点编译器尚未完全优化生成的类。我多次运行了第一个和第二个示例,得到了混合的相对结果:

运行示例1两次:

autotestbed.core> (time (let [r (range 1e7)                                                                
                                        a (take-while #(< % 12) r)                                     
                                                    b (drop-while #(< % 12) r)]                        
                              [(count a) (count b)]))
"Elapsed time: 929.681423 msecs"                                                                           
[12 9999988]
autotestbed.core> (time (let [r (range 1e7)                                                                
                                        a (take-while #(< % 12) r)                                     
                                                    b (drop-while #(< % 12) r)]                        
                              [(count a) (count b)]))
"Elapsed time: 887.81269 msecs"                                                                            
[12 9999988]
autotestbed.core>(时间(let[r(范围1e7))
a(花时间#(<%12)r)
b(在#(<%12)r)时放下)]
[(计数a)(计数b)])
“运行时间:929.681423毫秒”
[12 9999988]
自动测试。核心>(时间(let[r(范围1e7))
a(花时间#(<%12)r)
b(在#(<%12)r)时放下)]
[(计数a)(计数b)])
“运行时间:887.81269毫秒”
[12 9999988]
然后运行示例2几次:

core> (time (let [r (range 1e7)                                                                
                  a (take-while #(< % 12) r)                                     
                  b (drop-while #(< % 12) r)]                        
             [(count a) (count b)]))
"Elapsed time: 3838.751561 msecs"                                                                          
[12 9999988]
core> (time (let [r (range 1e7)                                                                
                  a (take-while #(< % 12) r)                                     
                  b (drop-while #(< % 12) r)]                        
             [(count a) (count b)]))
"Elapsed time: 970.397078 msecs"                                                                           
[12 9999988]
core>(时间(let[r(范围1e7))
a(花时间#(<%12)r)
b(在#(<%12)r)时放下)]
[(计数a)(计数b)])
“运行时间:3838.751561毫秒”
[12 9999988]
堆芯>(时间(let[r(范围1e7))
a(花时间#(<%12)r)
b(在#(<%12)r)时放下)]
[(计数a)(计数b)])
“运行时间:970.397078毫秒”
[12 9999988]

有些情况下,第二个示例同样快,它显示的时间完全取决于系统。。。。
如果重新执行,它将显示每次执行的不同运行时间

列表计数实际上是O(1),但根据“Clojure编程”,seq不是真正的列表,获取seq的长度需要成本。抱歉。我误导了你。是的,在你的例子中是lazysek,“count”是O(n)。在我的机器上(计数a)花费了0.019毫秒。因此,对于您的测量,它仍然看起来像O(1)。请尝试检查(计数b),因为在我的情况下,它是(计数b),而不是(计数a)。基本上在
(计数b)
之前使用
(计数a)
导致原始示例中的总时间增加
r
a
b
在let语句中立即绑定到未计算的惰性序列。绑定这些名称不会导致对序列求值。这个问题与的问题重复,这给出了一个很好的答案。