Как я могу объединить две последовательности в clojure?

Что такое идиоматический способ объединить (или получить объединение) двух списков (или последовательностей) в Clojure?

(merge l1 l2)

не похоже на решение:

a=> (merge '(1 2 3) '(2 3 4))
((2 3 4) 1 2 3)

person user1639206    schedule 30.09.2012    source источник
comment
как вы определяете слияние? например существуют ли дубликаты, и если да, то как обрабатываются дубликаты? также вы знаете, если списки уже отсортированы?   -  person mikera    schedule 30.09.2012
comment
к вашему сведению. Имя функции merge уже занято clojure.core. Чтобы избежать путаницы, вы можете выбрать другое имя для вашей merge функции. См. clojuredocs.org/clojure_core/clojure.core/merge.   -  person tnoda    schedule 30.09.2012
comment
Плакат фактически использовал clojure.core/merge, но не хеш-карты или другие ассоциативные данные, и указанная функция имеет неопределенное поведение в этом контексте.   -  person rplevy    schedule 01.10.2012


Ответы (5)


Я думаю, что решение andih отлично работает. Вот альтернативный способ, потому что эй, почему бы и нет. Он использует concat и distinct:

user> (distinct (concat '(1 2 3) '(2 3 4)))
=> (1 2 3 4)
person Omri Bernstein    schedule 30.09.2012
comment
Я бы просто посоветовал не использовать concat из соображений производительности, так как он на удивление медленный. Смотрите мой ответ ниже для дальнейшего обсуждения. - person rplevy; 01.10.2012
comment
@rplevy спасибо за комментарий (и ваш ответ ниже). Я не понимал, что у concat есть проблема с производительностью. - person Omri Bernstein; 01.10.2012

Если вам нужны отдельные несортированные данные (наборы), вам следует использовать структуру данных наборов Clojure вместо векторов или списков. И, как косвенно предположил andih, существует основная библиотека для операций над множествами: http://clojure.github.com/clojure/clojure.set-api.html

(require '[clojure.set :refer [union]])

(union #{1 2 3} #{3 4 5})
=> #{1 2 3 4 5}

Если наборы по какой-то причине вам не нужны, читайте дальше. Будьте осторожны с concat, когда у вас есть значительный объем данных в ваших последовательностях, и рассмотрите возможность использования into, который намного лучше оптимизирован как алгоритм слияния векторов. Я не знаю, почему concat не реализован с использованием into (или еще лучше — почему concat вообще существует? Кстати, хотя into значительно быстрее, чем concat, он все же намного медленнее, чем conj. RRB-деревья Bagwell, совместимые с обоими Clojure и Scala решат эту проблему, но еще не реализованы для Clojure).

Чтобы перефразировать неустановленное решение Омри с точки зрения «в»:

(distinct (into [1 2 3] [3 4 5]))
=> (1 2 3 4 5)
person rplevy    schedule 30.09.2012
comment
что плохого в конкате? Это постоянное время, потому что оно ленивое, а не реализация с линейным временем, которую вы получаете в строгих языках. - person Philip Potter; 01.10.2012
comment
Кроме того, как in может быть медленнее, чем conj? это реализовано с помощью conj. (into foo bar) аналогичен (reduce conj foo bar), за исключением того, что он будет использовать транзиенты, если они доступны. - person Philip Potter; 01.10.2012
comment
Нет, я сказал, что conj быстрее! (но если вам нужно объединить векторы, а не просто соединять их друг с другом, это разные потребности.) - person rplevy; 01.10.2012
comment
Филип: into использует транзиенты, очень похожие на транзиенты для примера конкатенации векторов в Joy of Clojure, что является хорошим местом для более подробного обсуждения этого. - person rplevy; 01.10.2012
comment
@PhilipPotter, конечно, concat не может быть постоянным временем для всех входов? Должно быть линейное время по мере роста размера входных данных? - person Petrus Theron; 03.03.2014
comment
@pete извините за поздний ответ. concat ленив, так что да, это может быть постоянное время, потому что на самом деле это не так уж много работает. он возвращает что-то, что при использовании в качестве последовательности сначала извлекает элементы из первого аргумента, а затем из второго, когда первый исчерпан. - person Philip Potter; 15.10.2014

Один из способов получить объединение двух списков — использовать union

Clojure> (into #{} (clojure.set/union '(1,2,3) '(3,4,5)))
#{1 2 3 4 5}

или если вы хотите получить список

(into '() (into #{} (clojure.set/union '(1,2,3) '(3,4,5))))
(5 4 3 2 1)
person andih    schedule 30.09.2012
comment
(-› #{} (в [1 2 3]) (в [3 4 5]) послед.) - person tnoda; 30.09.2012
comment
На самом деле вы ничего не достигаете, когда union работает с аргументами списка. Все, что он делает, это (reduce conj '(1,2,3) '(3,4,5)). clojure.set предназначены для работы с заданными аргументами. - person Marko Topolnik; 30.09.2012

Если вы не возражаете против дубликатов, вы можете попробовать concat:

(concat '(1 2 3 ) '(4 5 6 1) '(2 3)) 
;;==> (1 2 3 4 5 6 1 2 3) 
person Kevin Zhu    schedule 18.03.2015

Одним из вариантов является сведение:

(def colls '((1 2 3) (2 3 4)))
(flatten colls) ;; => (1 2 3 2 3 4)
(distinct (flatten colls)) ;; => (1 2 3 4)

Одна вещь, о которой следует знать, это то, что он будет сглаживать глубоко вложенные коллекции:

(flatten [[1 2 [3 4 5]] [1 [2 [3 4]]]]) ;; => (1 2 3 4 5 1 2 3 4)

Но хорошо работает для карт:

(flatten [[{} {} {}] [{} {} {}]]) ;; => ({} {} {} {} {} {})
person Kris    schedule 28.03.2017