Понимание примера параллелизма Clojure - PullRequest
6 голосов
/ 06 января 2011

Я просто просматриваю различную документацию по параллелизму Clojure и натолкнулся на пример на сайте (http://clojure.org/concurrent_programming).

(import '(java.util.concurrent Executors))
(defn test-stm [nitems nthreads niters]
(let [refs  (map ref (replicate nitems 0))
      pool  (Executors/newFixedThreadPool nthreads)
      tasks (map (fn [t]
                   (fn []
                     (dotimes [n niters]
                       (dosync
                         (doseq [r refs]
                           (alter r + 1 t))))))
                (range nthreads))]
(doseq [future (.invokeAll pool tasks)]
  (.get future))
(.shutdown pool)
(map deref refs)))

Я понимаю, что он делает и как работает, но не понимаю, зачем нужна вторая анонимная функция fn []?

Большое спасибо,

Dusha.

P.S. Без этого второго fn [] я получаю исключение NullPointerException.

Ответы [ 2 ]

5 голосов
/ 06 января 2011

Вот классический пример использования функций высшего порядка:

;; a function returns another function
(defn make-multiplyer [times]
  (fn [x]
    (* x times)))

;; now we bind returned function to a symbol to use it later
(def multiply-by-two (make-multiplyer 2))

;; let's use it
(multiply-by-two 100)   ; => 200

В этом примере кода fn внутри fn работает так же. Когда карта вызывает (fn [t] (fn [] ...)), она получает внутренний fn.

(def list-of-funcs (map (fn [t]
                          (fn [] (* t 10)))   ; main part
                        (range 5)))
;; Nearly same as
;; (def list-of-funcs (list (fn [] (* 0 10))
;;                          (fn [] (* 1 10))
;;                          ...
;;                          (fn [] (* 4 10))))


(for [i list-of-funcs]
  (i))
; => (0 10 20 30 40)

Обновление: и, как сказал Алекс, задачи в примере кода привязаны к списку вызываемых объектов, который затем передается в .invokeAll ().

2 голосов
/ 06 января 2011

Первый fn - это то, что карта использует для создания последовательности fn - по одному для каждого из потоков. Это потому, что задачи - это последовательность функций! Метод .invokeAll () ожидает коллекцию вызываемых объектов (функции Clojure реализуют интерфейс вызываемых элементов)

из Clojure.org: Специальные формы

fns реализуют интерфейсы Java Callable, Runnable и Comparator.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...