Сегодня я столкнулся с общей ситуацией: мне нужно было разделить список на 2 подсписка — элементы, которые прошли предикат, и элементы, которые не прошли предикат. Я уверен, что столкнулся с этой проблемой несколько раз, но это было некоторое время, и я забыл, какие варианты были доступны для меня. Беглый взгляд на http://clojure.github.com/clojure/ показывает несколько потенциальных функций: разделение на части , разделение на части и группирование .
раздел по
документам:
Использование: (partition-by f coll)
Применяет f к каждому значению в coll, разбивая его каждый раз, когда f возвращает
новое значение. Возвращает ленивый последовательность разделов.
Давайте предположим, что у нас есть коллекция целых и мы хотим разделить их на список четностей и список коэффициентов. В следующем сеансе REPL показан результат вызова секционирования с нашим списком целых чисел.
пользователь => (разделить по четным? [1 2 4 3 5 6]) ((1) (2 4) (3 5) (6))
Функция секционирования работает, как описано; к сожалению, это не совсем то, что я ищу. Мне нужна функция, которая возвращает ((1 3 5) (2 4 6)).
разделить с
документами:
Использование: (split-with pred coll)
Возвращает вектор [(take-while pred coll) (drop-while pred coll)]
Функция split-with звучит многообещающе, но быстрый сеанс REPL показывает, что это не то, что мы ищем.
user => (split-with even? [1 2 4 3 5 6]) [() (1 2 4 3 5 6)]
Как указано в документации, коллекция разбивается на первый элемент, который не соответствует предикату — (даже? 1).
Группировка по
документам:
Использование: (group-by f coll)
Возвращает карту элементов coll, связанных по результату f для каждого элемента. Значение в каждом ключе будет вектором соответствующих элементов в порядке их появления в coll.
Функция группировки работает, но она дает нам немного больше, чем мы ищем.
пользователь => (группировка по четности? [1 2 4 3 5 6]) {false [1 3 5], true [2 4 6]}
Результат в виде карты не совсем то, что мы хотим, но использование некоторой
деструктуризации позволяет нам получить значения, которые мы ищем.
user => (let [{evens true odds false} (четное сгруппирование? [1 2 4 3 5 6])] [четные шансы]) [[2 4 6] [1 3 5]]
Групповые результаты, смешанные с деструктуризацией, делают свое дело, но есть и другой вариант.
Из документов:
Использование: (juxt f)
(juxt fg)
(juxt fgh)
(juxt fgh & fs)
Alpha — имя, подлежащее изменению.
Принимает набор функций и возвращает fn, который является сопоставлением
этих fns. Возвращаемый fn принимает переменное число аргументов и
возвращает вектор, содержащий результат применения каждого
аргумента fn к
аргументам (слева направо).
((juxt abc) x) => [(ax) (bx) (cx)]
В первый раз, когда я натолкнулся на юкст, я нашел это немного пугающим. Я не могу сказать вам почему, но если вы чувствуете то же самое — не чувствуйте себя плохо. Оказывается, юкст — это именно то, что мы ищем. Следующий сеанс REPL показывает, как объединить juxt с
фильтром и
удалить для получения желаемых результатов.
user => ((удалить фильтр juxt) даже? [1 2 4 3 5 6]) [(2 4 6) (1 3 5)]
Таким образом, есть один улов в использовании juxt, весь список обрабатывается фильтрами и удаляется. В целом это приемлемо; однако это стоит учитывать при написании кода, чувствительного к производительности.
С http://blog.jayfields.com/2011/08/clojure-partition-by-split-with-group.html