在与Clojure合作一段时间后,我已经积累了一些关于 laziness 的知识。我知道常用的API(例如 map )是否是惰性的。但是,当我开始使用不熟悉的API(例如 with-open )时,我仍然感到怀疑。
是否有任何文档显示Clojure核心模块的完整API列表?
答案 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
。