Статьи

Clojure: разделение на разделение, разделение на, группирование и juxt

Сегодня я столкнулся с общей ситуацией: мне нужно было разделить список на 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

Из документов:


Использование: (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