#clojure
#clojure
Вопрос:
Существует ли «правильный» способ повторения двумерной последовательности в Clojure? Предположим, у меня был список списков чисел, подобный этому
((1 2 3)
(4 5 6)
(7 8 9))
и я хотел сгенерировать новый список списков с каждым числом, увеличенным на единицу. Есть ли простой способ сделать это в Clojure, не полагаясь на вложенные карты или цикл / повторения? Я смог это сделать, но мои решения уродливы, и мне трудно их понять, когда я их перечитываю.
Спасибо
Ответ №1:
То, что вы описываете, — это именно то, clojure.walk
для:
(матрица определения [[1 2 3] [4 5 6] [7 8 9]]) (используйте 'clojure.прохождение: только [предварительный просмотр]) (предварительный переход #(если (число? %) (включая %) %) матрицу) => [[2 3 4] [5 6 7] [8 9 10]]
Примечание 1: использование векторов вместо круглых скобок для буквальных последовательных коллекций является идиоматичным.
Примечание 2: переход сохраняет тип.
Комментарии:
1. Спасибо, что упомянули примечание 1 — я все еще немного сомневаюсь, когда использовать то или иное.
2. На этот ответ ссылается шпаргалка Clojure по адресу clojure.org.
Ответ №2:
Вы всегда можете просто использовать понимание списка. Я обнаружил, что использую их довольно часто, исходя из императивного фона, поэтому я не знаю, насколько это идиоматично. В вашем конкретном случае вы можете сделать:
(for [my-list my-matrix] (map inc my-list))
Комментарии:
1. Я собираюсь принять этот вариант, хотя остальные, безусловно, являются допустимыми ответами. Это просто кажется мне самым коротким и наиболее читаемым.
2. Следует отметить, что
for
создает отложенную последовательность, поэтому она «повторяется» только тогда, когда запрашивается значение.
Ответ №3:
Для двумерного случая вы могли бы сделать что-то вроде:
(map #(map inc %) my-two-d-list)
Это не так уж плохо для чтения: примените функцию #(map inc %)
к каждому элементу в списке.
В случае более высокого порядка вы в основном говорите о обходе по дереву. Вам нужна функция, которая принимает дерево и функцию и применяет эту функцию к каждому узлу в дереве. Вы можете найти функции для этого в clojure.walk.
Ответ №4:
Другие ответы Шона и Мэтта показывают краткие и эффективные способы получения правильного результата.
Однако есть несколько важных расширений, которые вы можете внести в это:
- Было бы неплохо обработать случай более высоких измерений
- Хорошо бы обернуть функциональность в функцию более высокого порядка
Пример кода:
;; general higher order function
(defn map-dimensions [n f coll]
(if (= n 1)
(map f coll)
(map #(map-dimensions (dec n) f %) coll)))
;; use partial application to specialise to 2 dimensions
(def map-2d (partial map-dimensions 2))
(map-2d inc
'((1 2 3)
(4 5 6)
(7 8 9)))
=> ((2 3 4) (5 6 7) (8 9 10))
Комментарии:
1. Тем не менее, полезно посмотреть, как это можно сделать с нуля.
2. Также
prewalk
требуется функция, которая отличает конечные узлы от узлов ответвления. Вы не можете просто датьinc
, например.
Ответ №5:
С момента появления core.matrix
в 2013 году это теперь гораздо лучший способ обработки операций над многомерными массивами:
(use 'clojure.core.matrix)
(def M [[1 2 3]
[4 5 6]
[7 8 9]])
(emap inc M)
=> [[2 3 4 ]
[5 6 7 ]
[8 9 10]]
Преимущества использования core.matrix
:
- Чистый, идиоматичный код Clojure
- Множество функций управления n-мерным массивом общего назначения —
transpose
,shape
,reshape
,slice
subarray
,,, и т.д. - Возможность подключения высокопроизводительных реализаций массива (например, для больших числовых массивов)
Ответ №6:
Запоздалый ответ и, возможно, не совсем то, что нужно: вы могли бы попробовать сгладить. Это вернет последовательность, которую вы можете повторить:
(flatten '((1 2 3)
(4 5 6)
(7 8 9)))
user=> (1 2 3 4 5 6 7 8 9)
И для того, чтобы увеличивать элементы матрицы и повторно собирать матрицу:
(partition 3 (map inc (flatten '((1 2 3)
(4 5 6)
(7 8 9)))))