преобразовать в оператор многопоточности - PullRequest
1 голос
/ 29 февраля 2020

У меня есть следующий код внутри let

; clause in a let
lessons-full (into []
                   (map #(dissoc (merge % (first (lesson-detail %)))
                                 :id :series_id :lesson_id :num))
                   lessons)

странным образом, я в итоге случайно использовал форму преобразователя в , получив парен в неправильном месте. Это версия, показанная выше. Для сравнения, версия без преобразователя:

; clause in a let
lessons-full (into []
                   (map #(dissoc (merge % (first (lesson-detail %)))
                                 :id :series_id :lesson_id :num)
                        lessons))

, которая также работает.

У меня есть еще пара вещей, которые я хочу сделать в этом преобразовании, включая преобразование значения ключа, называемого :type, которая в настоящее время является строкой, с keyword. Однако это становится высокой когнитивной нагрузкой. Пока не разбираюсь в операторах потоков. Кто-нибудь может помочь с первыми шагами / процессом мышления для этого?

lessons - это список карт из запроса jdb c.


обновление: черновой ответ - мыслительный процесс для преобразования в поток последнего оператора

Шаг 1

Подготовка к жонглированию. 1. Начните с последнего потока ->>, 2. поместите аргумент, lessons вперед, 3. поместите окончательное преобразование into в конце. Эта версия работает, но мы еще не закончили:

; clause in a let
lessons-full (->> lessons
                  (map #(dissoc (merge % (first (lesson-detail %)))
                                         :id :series_id :lesson_id :num) ,,,)
                  (into [] ,,,))

Обратите внимание, что тройные запятые ,,, игнорируются clojure, и мы добавляем их, чтобы помочь визуализировать, где находится аргумент внедряется последним макросом ->>.

Шаг 2

Мы вытаскиваем dissoc, но так как мы использовали его в вызове map когда мы вытаскиваем его, нам нужно заключить его в другой вызов map.

; clause in a let
lessons-full (->> lessons
                  (map #(merge % (first (lesson-detail %))) ,,,)
                  (map #(dissoc % :id :series_id :lesson_id :num) ,,,)
                  (into [] ,,,))

Так что это тоже работает. Позвольте этому погрузиться.


обновление 2

Наконец, вот код, который достигает моей первоначальной цели:

; clause in a let
lessons-full (->> lessons
                  (map #(merge % (lesson-detail %)) ,,,)
                  (map #(dissoc % :id :series_id :lesson_id :num) ,,,)
                  (map #(assoc % :type (keyword (:type %))) ,,,)
                  (into [] ,,,))

Похоже, что список композиций не так просто использовать внутри последнего макроса потока, если я не ошибаюсь. Кроме того, я трижды картирую карту. В моем текущем случае использования это может не иметь значения, но есть ли что-то, что можно здесь сказать относительно производительности или каких-либо других возможных улучшений?

Ответы [ 2 ]

1 голос
/ 07 марта 2020

Я бы написал так, используя преобразователи

Теоретически у вас больше гибкости в определении вычислений и их использовании независимо от структур данных

(let [xf (comp (map #(merge % (first (lesson-detail %))))
               (map #(dissoc % :id :series_id :lession_id :num))
               (map #(update % :type keyword)))]
  ;; into array, eager
  (into [] xf lessons)
  ;; lazy sequence
  (sequence xf lessons))
1 голос
/ 01 марта 2020

Если проблема заключается в когнитивной загрузке кода, который вы предлагаете, вы можете выделить операцию, которую вы выполняете на каждом уроке, в ее собственную функцию, такую ​​как эта:

(defn preprocess-lesson [lesson]
  (dissoc (merge lesson (first (lesson-detail lesson)))
          :id :series_id :lesson_id :num))

This имеет несколько преимуществ:

  • Вы можете написать контрольный пример для preprocess-lesson
  • Очевидно, что эта операция предварительной обработки урока требует только lesson в качестве аргумента и делает не зависит от некоторого значения из окружающей области.
  • Преобразование последовательности уроков выглядит чище

Если вы хотите использовать оператор потоков, вы должны написать

(->> lessons
     (map preprocess-lesson))

Если вы хотите использовать преобразователь, вы можете написать

(into []  (map preprocess-lesson) lessons)

Предположим, что вы хотите выполнить еще несколько операций с уроком, вы даже можете использовать оператор потоков внутри preprocess-lesson:

(defn preprocess-lesson [lesson common-lesson-data]
  (-> lesson
      (merge (first (lesson-detail lesson)))
      (dissoc :id :series_id :lesson_id :num)
      (assoc :type (get lesson "type"))
      (dissoc "type")
      (merge common-lesson-data)))

и затем назовите его как

(->> lessons
     (map #(preprocess-lesson % {:teacher "Rudolf"})))

Пока не разбираетесь в операторах потоков. Может ли кто-нибудь помочь с первыми шагами / процессом мышления для этого

Существуют макросы с многопоточностью, которые сделают ваш код более читабельным благодаря прояснению потока данных в ваших вычислениях. Вместо того, чтобы писать

(filter a? (map b (filter c? X)))

, вы можете написать

(->> X
     (filter c?)
     (map b)
     (filter a?))

, что может быть более читабельным и легче понять намерение. Но кроме этого, они не добавляют никакой дополнительной выразительной силы только к вложенным выражениям типа (filter a? (map b (filter c? X))). Короче говоря, используйте макросы потоков, чтобы сделать ваш код более читабельным, а не ради их использования. Если выделение некоторого фрагмента кода в отдельную функцию делает ваш код более читабельным, сделайте это.

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