Skip to content

Latest commit

 

History

History
187 lines (155 loc) · 5.4 KB

File metadata and controls

187 lines (155 loc) · 5.4 KB

Ключевое отличие mapcon:

mapcon передаёт функции весь остаток списка, а не только текущий элемент.

(setq Lst '(1 2 3 4))

# mapcar - получает только элемент
(mapcar
   '((X)
      (println "Element:" X)
      X)
   Lst)
# Вызовы:
# Element: 1
# Element: 2
# Element: 3
# Element: 4
# -> (1 2 3 4)

# mapcon - получает весь остаток списка (включая текущий элемент)
(mapcon
   '((L)
      (println "List:" L "First:" (car L) "Rest:" (cdr L))
      (list (car L)))
   Lst)
# Вызовы:
# List: (1 2 3 4) First: 1 Rest: (2 3 4)
# List: (2 3 4)   First: 2 Rest: (3 4)
# List: (3 4)     First: 3 Rest: (4)
# List: (4)       First: 4 Rest: NIL
# -> (1 2 3 4)

Практические примеры с контекстом:

1. Поиск пар соседних элементов

# Найти все пары соседних элементов
(de find-pairs (Lst)
   (mapcon
      '((L)
         (when (cdr L)  # Есть следующий элемент?
            (list (list (car L) (cadr L)))))
      Lst))

(find-pairs '(1 2 3 4 5))
# -> ((1 2) (2 3) (3 4) (4 5))

# С mapcar это невозможно, т.к. нет доступа к следующему элементу!

2. Обработка команд с "заглядыванием вперёд"

(de handle-with-lookahead (CmdList)
   (mapcon
      '((L)
         (let (Cmd (caar L)
               Args (cdar L)
               NextCmd (caadr L))  # Смотрим на следующую команду!
            
            # Если следующая команда 'echo, добавляем вывод
            (if (= NextCmd 'echo)
               (list 
                  (prinl "Next is echo, result: " 
                     (handle Cmd Args)))
               (list (handle Cmd Args)))))
      CmdList))

(handle-with-lookahead
   '((add 5 3)
     (echo "Result")
     (mul 2 4)))
# -> Next is echo, result: 8
#    Result
#    (8 NIL 8)

3. Группировка последовательных элементов

# Сгруппировать последовательные числа
(de group-consecutive (Lst)
   (mapcon
      '((L)
         (let (Curr (car L)
               Next (cadr L))
            (if (and Next (= (inc Curr) Next))
               NIL  # Продолжаем группу
               (list Curr))))  # Конец группы
      Lst))

(group-consecutive '(1 2 3 5 6 7 10 11))
# -> (3 7 11) - концы групп

4. Валидация последовательности команд

(de validate-sequence (CmdList)
   (mapcon
      '((L)
         (let (Cmd (caar L)
               NextCmd (caadr L))
            # div не может быть после div (для примера)
            (if (and (= Cmd 'div) (= NextCmd 'div))
               (list "ERROR: Two divisions in a row!")
               (list (handle Cmd (cdar L))))))
      CmdList))

(validate-sequence
   '((add 10 5)
     (div 30 2)
     (div 20 4)))  # Две div подряд!
# -> (15 15 "ERROR: Two divisions in a row!")

5. Накопление контекста через список

# Каждая команда знает обо всех последующих
(de process-with-future (CmdList)
   (mapcon
      '((L)
         (let (Cmd (caar L)
               Args (cdar L)
               Future (cdr L)
               FutureCount (length Future))
            (println 
               "Processing:" Cmd 
               "Remaining commands:" FutureCount)
            (list (handle Cmd Args))))
      CmdList))

(process-with-future
   '((add 1 2)
     (mul 3 4)
     (sub 10 5)))
# Processing: add Remaining commands: 2
# Processing: mul Remaining commands: 1
# Processing: sub Remaining commands: 0
# -> (3 12 5)

Сравнение всех map* функций:

(setq Lst '(10 20 30))

# mapcar - элемент -> результат
(mapcar '((X) (* X 2)) Lst)
# -> (20 40 60)

# mapc - элемент -> результат (но возвращает исходный список)
(mapc '((X) (println X)) Lst)
# -> (10 20 30)

# mapcan - элемент -> список, конкатенация
(mapcan '((X) (list X (- X))) Lst)
# -> (10 -10 20 -20 30 -30)

# mapcon - остаток списка -> список, конкатенация
(mapcon 
   '((L) 
      (list (car L) (length L)))  # Текущий элемент + длина остатка
   Lst)
# -> (10 3 20 2 30 1)
#    ^   ^  ^   ^  ^  ^
#    эл  дл эл  дл эл дл

# maplist - остаток списка -> результат
(maplist '((L) (length L)) Lst)
# -> (3 2 1)

Итого:

mapcon используется когда:

  • Нужно сравнивать текущий элемент со следующими
  • Нужно знать, сколько элементов осталось
  • Нужно принимать решения на основе будущих элементов
  • Нужно группировать или валидировать последовательности
  • Нужен доступ к "контексту" - что идёт дальше в списке

Обычный mapcar не может этого, т.к. видит только один элемент за раз!