是否有完整的Clojure核心模块的惰性函数列表?

时间:2016-02-04 15:51:04

标签: clojure

在与Clojure合作一段时间后,我已经积累了一些关于 laziness 的知识。我知道常用的API(例如 map )是否是惰性的。但是,当我开始使用不熟悉的API(例如 with-open )时,我仍然感到怀疑。

是否有任何文档显示Clojure核心模块的完整API列表?

2 个答案:

答案 0 :(得分:5)

您可以通过打开Clojure代码https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj找到返回延迟序列的函数 并搜索“返回懒惰”

我不知道有任何精选的清单。

经验法则是:如果它返回一个序列,它将是一个惰性序列,如果它返回一个值,它将强制进行评估。

使用新函数,宏或特殊形式时,请阅读docstring。大多数开发环境都有一个显示文档字符串的键,或者至少导航到源代码(您可以在其中查看文档字符串),并且始终存在http://clojure.org/api/api

在with-open的情况下:

用开 宏 用法:(带开放式绑定和身体) bindings => [name init ...]

在try表达式中计算body,其名称绑定到值 inits,以及在每个上调用(.close name)的finally子句 以相反的顺序命名。

我们可以看到调用with-open的结果是对最终关闭的表达式的评估。所以我们知道没有什么是懒惰的。然而,这并不意味着你不需要考虑内部开放的懒惰,恰恰相反!

(with-open [r (io/reader "myfile")]
  (line-seq r))

这是一个常见的陷阱。 line-seq返回一个懒惰的序列!这里的问题是在文件关闭后将实现延迟序列,因为在退出with-open范围时文件被关闭。因此,您需要在退出with-open范围之前完全处理延迟序列。

我的建议是避免尝试将您的程序视为“懒惰位”和“立即位”,而只是注意当涉及到io或副作用时,您需要在事情发生时进行处理。以及应该发生的事情。

答案 1 :(得分:4)

挖掘Timothy Pratley关于在doc中搜索的提议:

让它变得有趣!

你的repl拥有查找惰性函数列表所需的一切。

首先,有一个clojure.repl/doc宏,可以在repl中将文档打印到 out

user> (doc +)
-------------------------
clojure.core/+
([] [x] [x y] [x y & more])
  Returns the sum of nums. (+) returns 0. Does not auto-promote
  longs, will throw on overflow. See also: +'
nil

遗憾的是,我们无法简单地获取字符串,但我们始终可以将*out*重新绑定为StringWriter,然后获取其字符串值。

所以,我们想要从clojure.core命名空间获取所有符号,获取他们的文档,将它们全部写入字符串,并找到包含“返回惰性”的每个符号。这里有帮助:clojure.core/ns-publics,将公共名称的地图返回到他们的vars:

user> (take 10 (ns-publics 'clojure.core))
([primitives-classnames #'clojure.core/primitives-classnames] 
 [+' #'clojure.core/+'] 
 [decimal? #'clojure.core/decimal?] 
 [restart-agent #'clojure.core/restart-agent] 
 [sort-by #'clojure.core/sort-by] 
 [macroexpand #'clojure.core/macroexpand] 
 [ensure #'clojure.core/ensure] 
 [chunk-first #'clojure.core/chunk-first] 
 [eduction #'clojure.core/eduction] 
 [tree-seq #'clojure.core/tree-seq])

所以我们只需从那里获取所有密钥并查找他们的文档。 让我们为此制作一个宏:

user> (defmacro all-docs []
        (let [names (keys (ns-publics 'clojure.core))]
          `(binding [*out* (java.io.StringWriter.)]
             (do ~@(map #(list `doc %) names))
             (str *out*))))
#'user/all-docs

它正是我所说的,让所有公众的文档都成为字符串。

现在我们只是处理它:

user> (def all-doc-items (clojure.string/split 
                           (all-docs) 
                           #"-------------------------"))
#'user/all-doc-items

user> (nth all-doc-items 10)
"\nclojure.core/tree-seq\n([branch? children root])\n  Returns a lazy sequence of the nodes in a tree, via a depth-first walk.\n   branch? must be a fn of one arg that returns true if passed a node\n   that can have children (but may not).  children must be a fn of one\n   arg that returns a sequence of the children. Will only be called on\n   nodes for which branch? returns true. Root is the root node of the\n  tree.\n"

现在只是过滤它们:

user> (def all-lazy-fns (filter #(re-find #"(?i)returns a lazy" %) all-doc-items))
#'user/all-lazy-fns

user> (count all-lazy-fns)
30

user> (println (take 3 all-lazy-fns))
(
clojure.core/tree-seq
([branch? children root])
  Returns a lazy sequence of the nodes in a tree, via a depth-first walk.
   branch? must be a fn of one arg that returns true if passed a node
   that can have children (but may not).  children must be a fn of one
   arg that returns a sequence of the children. Will only be called on
   nodes for which branch? returns true. Root is the root node of the tree.

clojure.core/keep-indexed
([f] [f coll])
  Returns a lazy sequence of the non-nil results of (f index item). Note,
  this means false return values will be included.  f must be free of
  side-effects.  Returns a stateful transducer when no collection is
  provided.

clojure.core/take-nth
([n] [n coll])
  Returns a lazy seq of every nth item in coll.  Returns a stateful
  transducer when no collection is provided.
)
nil

然后现在使用这些all-lazy-fns