"Joy of Clojure" 2 版。清单 11.9 关于承诺
"Joy of Clojure" 2 edition. Listing 11.9 about promises
我正在检查指定书籍的 Listing 11.9(pdf 的第 269 页)。
谁能解释一下 tests
值是如何设置的(第 [tests all-tests :as results]
行)?
谢谢
tests
好像是一个函数,所以:as
把运行tests
的结果(输出)放在all-tests
上.
(编辑:)
仔细检查后,with-promises
宏似乎将 tests
设置为测试计数。
根据我正在阅读的内容(对宏了解不多),参数似乎映射 ("tests" "all-tests" ":as" "results") -> ("n" "tasks" "_" "as")
但我不太明白的是这意味着 when
需要一个results
("as") 的值,当我们应该创建它时。反正tests
的值是在宏最后的let
里设置的
在我看来,这段代码 far 太聪明了。 Fogus是一位大师,但这不是他最好的作品。
(如果我错了,hopefully someone will be inspired。)
为没有 The Joy of Clojure(顺便说一句,我确实很喜欢这本书)的人设置问题的上下文,所讨论的宏是:
(defmacro with-promises [[n tasks _ as] & body]
(when as
`(let [tasks# ~tasks
n# (count tasks#)
promises# (take n# (repeatedly promise))]
(dotimes [i# n#]
(dothreads!
(fn []
(deliver (nth promises# i#)
((nth tasks# i#))))))
(let [~n tasks#
~as promises#]
~@body))))
并这样使用:
(defn run-tests [& all-tests]
(with-promises
[tests all-tests :as results]
(into (TestRun. 0 0 0)
(reduce #(merge-with + %1 %2) {}
(for [r results]
(if @r
{:run 1 :passed 1}
{:run 1 :failed 1}))))))
最后调用 运行-tests 就像:
(run-tests pass fail fail fail pass)
=> #user.TestRun{:run 5, :passed 2, :failed 3}
最终,宏的后半部分正在做一个 let 赋值和 运行ning 主体,所以你最终得到
(let [tests tasks#
results promises#]
(into (TestRun. 0 0 0)
;; rest of body
在宏中,~n 不引用 '(let
周围的起始反引号,因此您可以将其读作 n
,这是宏的第一个参数(好吧,第一个作为宏的第一个参数的向量的参数)。
这一切都发生在宏使用自定义 dothreads 设置承诺之后!使用线程池的函数 - 线程池中没有一个对理解宏很重要。
您可以通过将宏包装在 (pprint (macroexpand-1 '(with-promises ...
中来确定有关宏的更多信息,它会生成类似的内容(我已将自动生成的名称替换为更简单的内容,v1、n1、p1 和 i1):
(clojure.core/let
[v1 all-tests
n1 (clojure.core/count v1)
p1 (clojure.core/take n1 (clojure.core/repeatedly clojure.core/promise))]
(clojure.core/dotimes
[i1 n1]
(user/dothreads!
(clojure.core/fn
[]
(clojure.core/deliver
(clojure.core/nth p1 i1)
((clojure.core/nth v1 i1))))))
(clojure.core/let
[tests v1
results p1]
(into
(TestRun. 0 0 0)
;; ... rest of main body
这清楚地表明传入的参数在最终的 let 绑定中用作变量。
然而,在此示例用法中(即 run-tests
函数),tests
变量实际上并未在 with-promises 调用的主体中使用,只有 results
, 所以你质疑它是对的,根本不需要它。
查看宏定义,对于这种情况可能会有进一步的优化,因为 tasks# 绑定似乎除了包装 tasks
之外没有提供任何额外的东西。起初我想知道这是否与 dothreads 中的不变性有关! call 或 macro-niceness 提供围绕使用的闭包,而不是直接使用宏的参数。
我尝试更改宏以完全删除 tasks#
并直接使用 ~tasks
,这似乎仍然有效,因为 "tests" 不是正文中必需的绑定变量在 运行-tests 中,您可以同时删除宏中的 n
参数和最终 let 绑定的 ~n tasks#
部分而不会出现问题。
实际上,在读了好几遍之后,我终于明白了,就是让整个向量看起来像一个标准的解构绑定。
编辑:关于 "tests" 的更多解释。
这只是一个名字,它可能是"foo-tests","foo-bar",因为它最终被用来在let绑定中定义一些东西。
如果 运行-tests 主体是这样的:
(defn run-tests [& all-tests]
(with-promises
[foo all-tests :as results]
(println "foo was set to" foo)
(into (TestRun. 0 0 0)
;; rest of body
你可以看到 foo(和结果)是如何被用来最终定义变量的(哎呀——你知道我的意思),这些变量可以在调用宏的主体部分中使用。主体是初始向量 [foo all-tests :as results]
之后的所有内容,但在原始代码中,已声明 tests
但未使用。
我正在检查指定书籍的 Listing 11.9(pdf 的第 269 页)。
谁能解释一下 tests
值是如何设置的(第 [tests all-tests :as results]
行)?
谢谢
tests
好像是一个函数,所以:as
把运行tests
的结果(输出)放在all-tests
上.
(编辑:)
仔细检查后,with-promises
宏似乎将 tests
设置为测试计数。
根据我正在阅读的内容(对宏了解不多),参数似乎映射 ("tests" "all-tests" ":as" "results") -> ("n" "tasks" "_" "as")
但我不太明白的是这意味着 when
需要一个results
("as") 的值,当我们应该创建它时。反正tests
的值是在宏最后的let
里设置的
在我看来,这段代码 far 太聪明了。 Fogus是一位大师,但这不是他最好的作品。
(如果我错了,hopefully someone will be inspired。)
为没有 The Joy of Clojure(顺便说一句,我确实很喜欢这本书)的人设置问题的上下文,所讨论的宏是:
(defmacro with-promises [[n tasks _ as] & body]
(when as
`(let [tasks# ~tasks
n# (count tasks#)
promises# (take n# (repeatedly promise))]
(dotimes [i# n#]
(dothreads!
(fn []
(deliver (nth promises# i#)
((nth tasks# i#))))))
(let [~n tasks#
~as promises#]
~@body))))
并这样使用:
(defn run-tests [& all-tests]
(with-promises
[tests all-tests :as results]
(into (TestRun. 0 0 0)
(reduce #(merge-with + %1 %2) {}
(for [r results]
(if @r
{:run 1 :passed 1}
{:run 1 :failed 1}))))))
最后调用 运行-tests 就像:
(run-tests pass fail fail fail pass)
=> #user.TestRun{:run 5, :passed 2, :failed 3}
最终,宏的后半部分正在做一个 let 赋值和 运行ning 主体,所以你最终得到
(let [tests tasks#
results promises#]
(into (TestRun. 0 0 0)
;; rest of body
在宏中,~n 不引用 '(let
周围的起始反引号,因此您可以将其读作 n
,这是宏的第一个参数(好吧,第一个作为宏的第一个参数的向量的参数)。
这一切都发生在宏使用自定义 dothreads 设置承诺之后!使用线程池的函数 - 线程池中没有一个对理解宏很重要。
您可以通过将宏包装在 (pprint (macroexpand-1 '(with-promises ...
中来确定有关宏的更多信息,它会生成类似的内容(我已将自动生成的名称替换为更简单的内容,v1、n1、p1 和 i1):
(clojure.core/let
[v1 all-tests
n1 (clojure.core/count v1)
p1 (clojure.core/take n1 (clojure.core/repeatedly clojure.core/promise))]
(clojure.core/dotimes
[i1 n1]
(user/dothreads!
(clojure.core/fn
[]
(clojure.core/deliver
(clojure.core/nth p1 i1)
((clojure.core/nth v1 i1))))))
(clojure.core/let
[tests v1
results p1]
(into
(TestRun. 0 0 0)
;; ... rest of main body
这清楚地表明传入的参数在最终的 let 绑定中用作变量。
然而,在此示例用法中(即 run-tests
函数),tests
变量实际上并未在 with-promises 调用的主体中使用,只有 results
, 所以你质疑它是对的,根本不需要它。
查看宏定义,对于这种情况可能会有进一步的优化,因为 tasks# 绑定似乎除了包装 tasks
之外没有提供任何额外的东西。起初我想知道这是否与 dothreads 中的不变性有关! call 或 macro-niceness 提供围绕使用的闭包,而不是直接使用宏的参数。
我尝试更改宏以完全删除 tasks#
并直接使用 ~tasks
,这似乎仍然有效,因为 "tests" 不是正文中必需的绑定变量在 运行-tests 中,您可以同时删除宏中的 n
参数和最终 let 绑定的 ~n tasks#
部分而不会出现问题。
实际上,在读了好几遍之后,我终于明白了,就是让整个向量看起来像一个标准的解构绑定。
编辑:关于 "tests" 的更多解释。
这只是一个名字,它可能是"foo-tests","foo-bar",因为它最终被用来在let绑定中定义一些东西。
如果 运行-tests 主体是这样的:
(defn run-tests [& all-tests]
(with-promises
[foo all-tests :as results]
(println "foo was set to" foo)
(into (TestRun. 0 0 0)
;; rest of body
你可以看到 foo(和结果)是如何被用来最终定义变量的(哎呀——你知道我的意思),这些变量可以在调用宏的主体部分中使用。主体是初始向量 [foo all-tests :as results]
之后的所有内容,但在原始代码中,已声明 tests
但未使用。