Как преобразовать список в подсписки? - PullRequest
0 голосов
/ 25 февраля 2020
((1 2 3)
(2 3 4)
(3 4 5)
(4 5 6))

от

(1 2 3 4 5 6)

А какой тип такой операции?

Что я пробовал:

(loop
   :with l2 = '()
   :with l1 = '(1 2 3 4 5 6)
   :for i :in l1
   :do (push (subseq l1 0 3) l2))

Ответы [ 5 ]

4 голосов
/ 25 февраля 2020

В качестве альтернативы используйте map:

(let ((l '(1 2 3 4 5 6)))
  (map 'list #'list l (cdr l) (cddr l)))

;; ((1 2 3) (2 3 4) (3 4 5) (4 5 6))

Вы можете прочитать его как:

  • для списка l со значениями (1 2 3 4 5 6)
  • map над списком и двумя последовательными cdr s
  • путем применения #'list к элементам списков map проходит по параллельному циклу
  • (останавливается, когда самый короткий список израсходовано)
  • и собираем результаты как / в 'list

@ WillNess предложил еще проще:

(let ((l '(1 2 3 4 5 6)))
  (mapcar #'list l (cdr l) (cddr l)))

спасибо! Тогда мы можем обобщить, используя только map вариантов:

(defun subseqs-of-n (l n)
  (apply #'mapcar #'list (subseq (maplist #'identity l) 0 n)))

(maplist #'identity l) эквивалентно (loop for sl on l collect sl). Тем не менее,

(loop for sl on l
      for i from 0 to n
      collect sl)

лучше, потому что он останавливается на n-м цикле цикла ...

4 голосов
/ 25 февраля 2020

Вы продвигаете один и тот же подсписок каждый раз через l oop.

. Вы можете использовать :for sublist on до l oop через последовательные хвосты списка.

И используйте :collect, чтобы составить список всех результатов, вместо того, чтобы нажимать на свой собственный список

(loop
   :for l1 on '(1 2 3 4 5 6)
   :if (>= (length l1) 3)
      :collect (subseq l1 0 3)
   :else 
      :do (loop-finish))
3 голосов
/ 26 февраля 2020

Сначала давайте определим функцию take-n, которая либо возвращает n элементов, либо пустой список, если элементов недостаточно. Он не будет сканировать весь список.

(defun take-n (n list)
  (loop repeat n
        when (null list) return (values nil nil)
        collect (pop list)))

Затем мы перемещаем эту функцию take-n по списку, пока она не вернет NIL.

(defun moving-slice (n list)
  (loop for l on list
        for p = (take-n n l)
        while p
        collect p))

Пример:

CL-USER 207 > (moving-slice 3 '(1 2))
NIL

CL-USER 208 > (moving-slice 3 '(1 2 3))
((1 2 3))

CL-USER 209 > (moving-slice 3 '(1 2 3 4 5 6 7))
((1 2 3) (2 3 4) (3 4 5) (4 5 6) (5 6 7))
2 голосов
/ 25 февраля 2020

Вот вариант ответа Бармара (который должен быть принятым), который является более общим и вызывает только length один раз.

(defun successive-leading-parts (l n)
  (loop repeat (1+ (- (length l) n))
        for lt on l
        collect (subseq lt 0 n)))
> (successive-leading-parts '(1 2 3 4) 3)
((1 2 3) (2 3 4))

> (successive-leading-parts '(1 2 3 4) 2)
((1 2) (2 3) (3 4))
1 голос
/ 27 февраля 2020

Или классический более C -подобный for-l oop -ing с индексами для его решения. Но используйте его больше для строк / векторов, но меньше для списков, потому что его производительность составляет

  • для списков квадратичных c
  • для векторов (строк!) Линейных, поэтому желательно, чтобы используется с ними!

кредитов и благодаря @WillNess, который указал на оба пункта (см. комментарии ниже).

(defun subseqs-of-n (ls n) ;; works on strings, too!
  (loop :for i :from 0 :to (- (length ls) n)
        :collect (subseq ls i (+ i n))))

Так что на векторах / строках используйте:

(subseqs-of-n "gattaca" 5)
;; ("gatta" "attac" "ttaca")
...