Вкратце: ленивые последовательности плохо сочетаются с макросом time
, а ваша функция get-first-hit-from-each
возвращает ленивую последовательность. Чтобы ленивые последовательности работали с time
, оберните их в doall
, как это предлагается в документации< /а>. См. Ниже более полный мыслительный процесс:
Ниже приводится определение макроса time
в clojure.core
(источник):
(defmacro time
"Evaluates expr and prints the time it took. Returns the value of
expr."
{:added "1.0"}
[expr]
`(let [start# (. System (nanoTime))
ret# ~expr]
(prn (str "Elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs"))
ret#))
Обратите внимание, как макрос сохраняет возвращаемое значение expr
в ret#
, сразу после чего печатает прошедшее время? Только после этого возвращается ret#
. Ключевым моментом здесь является то, что ваша функция get-first-hit-from-each
возвращает ленивую последовательность (поскольку map
возвращает ленивую последовательность):
(type (get-first-hit-from-each "gray+cat" '("google" "bing")))
;; => clojure.lang.LazySeq
Таким образом, когда вы выполняете (time (get-first-hit-from-each "gray+cat" '("google" "bing")))
, в ret#
сохраняется ленивая последовательность, которая на самом деле не оценивается, пока мы не попытаемся использовать ее значение...
Мы можем проверить, была ли вычислена ленивая последовательность, используя функцию realized?
. Итак, давайте настроим макрос time
, добавив строку, чтобы проверить, было ли вычислено ret#
, сразу после печати прошедшего времени:
(defmacro my-time
[expr]
`(let [start# (. System (nanoTime))
ret# ~expr]
(prn (str "Elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs"))
(prn (realized? ret#)) ;; has the lazy sequence been evaluated?
ret#))
Теперь тестируем:
(my-time (get-first-hit-from-each "gray+cat" '("google" "bing")))
"Elapsed time: 0.223054 msecs"
false
;; => ("https://www.google.com/search?q%3Dgray+cat" "https://www.bing.com/search?q%3Dgray+cat")
Нет... так вот почему time
печатается неточно. Ни одна из вычислительно-длинных вещей фактически не запускается до того, как будет сделана распечатка.
Чтобы исправить это и получить точное время, нам нужно обеспечить оценку ленивой последовательности, что можно сделать, стратегически поместив doall
в кучу возможных мест, либо внутри вашей функции, обернув map
:
(defn get-first-hit-from-each
[query engines]
(let [futs (map (fn [engine]
(future (search-for query engine))) engines)]
(doall futs)
(doall (map deref futs))))
;; => #'propeller.core/get-first-hit-from-each
(time (get-first-hit-from-each "gray+cat" '("google" "bing")))
"Elapsed time: 2005.478689 msecs"
;; => ("https://www.google.com/search?q%3Dgray+cat" "https://www.bing.com/search?q%3Dgray+cat")
или внутри time
, оборачивая вызов функции:
(time (doall (get-first-hit-from-each "gray+cat" '("google" "bing"))))
06.07.2020