首页 > 解决方案 > Clojure apply 没有实现惰性序列的前四个元素?

问题描述

看起来 apply 强制实现给定惰性序列的四个元素。

(take 1
      (apply concat
             (repeatedly #(do
                            (println "called")
                            (range 1 10)))))

=> "called"
=> "called"
=> "called"
=> "called"

有没有办法做一个不这样的申请?

谢谢你

标签: clojure

解决方案


有没有办法做一个apply不这样的行为?

我认为简短的回答是:必须重新实现 Clojure 的一些基本功能。的实现直接依赖于 Clojure 的可调用函数的实现,并尝试通过枚举参数的输入序列apply来发现给定函数的正确性。.invoke

在惰性、未分块的序列/reducers/transducers 上使用函数而不是使用带有apply. 例如,这是您使用传感器重新实现的示例,它只调用一次 body 函数(每个长度range):

(sequence
  (comp
    (mapcat identity)
    (take 1))
  (repeatedly #(do
                 (println "called")
                 (range 1 10))))
;; called
;; => (1)

apply用, concat, seq,LazySeq等深入了解您的示例中发生的情况:

  • repeatedly返回一个新LazySeq实例:(lazy-seq (cons (f) (repeatedly f))).
  • 对于给定的 2-arity (apply concat <args>)apply调用RT.seq其参数列表,对于 a LazySeqthen 调用LazySeq.seq,这将调用您的函数
  • apply然后调用一个Java impl。applyToHelper尝试获取参数序列长度的方法。applyToHelper尝试使用 来确定参数列表的长度RT.boundedLength,它内部调用nextand seq,因此它可以找到适当的重载IFn.invoketo 调用
  • concat本身增加了另一层lazy-seq行为。

您可以看到这些调用的堆栈跟踪,如下所示:

(take 1
  (repeatedly #(do
                 (clojure.stacktrace/print-stack-trace (Exception.))
                 (range 1 10))))

第一个跟踪来自apply的初始调用seq,随后的跟踪来自RT.boundedLength.


推荐阅读