Ваша функция работает, но использует APPLY
.Количество списков, которые вы можете передать apply
, ограничено CALL-ARGUMENTS-LIMIT
, что может быть достаточно большим в данной реализации.Однако ваша функция должна принимать произвольное количество списков, поэтому, если вы хотите кодировать переносимо, вам не следует использовать apply
в вашем случае.
Zip
Функция zip
объединяет все первые элементы, затем все вторые элементы и т. Д. Из списка списков.Если ваш входной список списков следующий:
'((a b c d)
(0 1 2 3)
(x y z t))
Тогда zip создаст следующий список:
(list (combine (list a 0 x))
(combine (list b 1 y))
(combine (list c 2 z))
(combine (list d 3 t)))
Если вы присмотритесь, вы увидите, что можете разделить работувыполняется как отображение функции combine
поверх транспонирования вашего первоначального списка списков (в виде матрицы):
((a b c d) ((a 0 x)
(0 1 2 3) ===> (b 1 y)
(x y z t)) (c 2 z)
(d 3 t))
И так,zip
можно определить как:
(defun zip (combine lists)
(mapcar #'combine (transpose lists)))
В качестве альтернативы, вы можете использовать MAP-INTO
, если вам нужно повторно использовать память, выделенную при вызове transpose
:
(defun zip (combiner lists &aux (transposed (transpose lists)))
(map-into transposed combiner transposed))
Транспонирование
Существуют различные способы транспонировать список списков.Здесь я собираюсь использовать REDUCE
, который известен как fold в некоторых других функциональных языках.На каждом шаге редукции промежуточная функция редукции будет принимать частичный результат и список, и создавать новый результат.Наш результат также список списков.Ниже я показываю текущий список на каждом шаге сокращения и результирующий список списков.
Первый шаг
(a b c d) => ((a)
(b)
(c)
(d))
Второй шаг
(0 1 2 3) => ((0 a)
(1 b)
(2 c)
(3 d))
Третий шаг
(x y z t) => ((x 0 a)
(y 1 b)
(z 2 c)
(t 3 d))
Обратите внимание, что элементы помещаются перед каждым списком,что объясняет, почему каждый результирующий список переворачивается с ожидаемым результатом.Таким образом, функция transpose
должна восстанавливать каждый список после выполнения сокращения:
(defun transpose (lists)
(mapcar #'reverse
(reduce (lambda (result list)
(if result
(mapcar #'cons list result)
(mapcar #'list list)))
lists
:initial-value nil)))
Как и раньше, может быть предпочтительнее избежать выделения слишком большого объема памяти.
(defun transpose (lists)
(let ((reduced (reduce (lambda (result list)
(if result
(mapcar #'cons list result)
(mapcar #'list list)))
lists
:initial-value nil)))
(map-into reduced #'nreverse reduced)))
Пример
(transpose '(("ab" "cd" "ef") ("01" "23" "45")))
=> (("ab" "01") ("cd" "23") ("ef" "45"))
(import 'alexandria:curry)
(zip (curry #'join-string "|")
'(("ab" "cd" "ef") ("01" "23" "45")))
=> ("ab|01" "cd|23" "ef|45")
Редактировать: в других ответах уже приведены примеры определений для join-string
.Вы также можете использовать функцию join
из cl-strings .