AutoLISP: Unknown Lambda

Oct 13, 2013 15:17

Казуальное программирование. Как много боли в этом словосочетании.




Будучи завлечённым в программинг практической стороной вопроса, и уделяя ему достаточно много внимания, неизбежно переходишь ступени переосознания собственного опыта. В результате, тот объём, что раньше требовал месяцы работы занимает пару дней, а ощущение общей непонятливости своей предыдущей ступени усугубляется представлением себе объёма материала, который ещё предстоит к изучению, и который уже всем профессионалам давно очевиден.

Но есть вещи, которые не меняются.
Например, война, или, скажем, лямбда исчисление, умение пользоваться которой  зависит не от объёма знаний, а исключительно от уровня умственных способностей.

В мире не так много базовых вещей, которые нельзя написать какими-нибудь костылями, будучи недостаточно умным для написания чистой реализации (в конце концов, сортировка пузырьком - тоже сортировка...)
Выражения с рекурсивными лямбда-исчислениями в лиспе являются одними из таковых. Для того, чтобы такие сочинять, нужно сперва дорасти.

К щастью, пользоваться ими можно даже в том случае, если знаешь результат, который они выдают, и именно поэтому я попросил всемирно известного AutoLISP программиста восьмого уровня Lee McDonnel'a (aka Lee Mac) написать пару горячо желанных выражений (при этом задонировав довольно скромную сумму в поддержку его сайта www.lee-mac.com).
Многие из этих вещей оказались весьма неожиданными. То есть я понимаю каждый оператор по отдельности, но понять как их можно было расставить в такой последовательности мне не понятно до сих пор =). Приступим.

________________________________________________
= Последовательное суммирование листа ((2+3+5) (6+4+2) (1+2+1))
(setq lst '((2 3 5) (6 4 2) (1 2 1)) )
(apply 'mapcar (cons '+ lst))
> (9 9 8)

Что? cons '+ list ???  И прямо через виртуализацию 'mapcar...  Нет, вы видели? Ну и как это понимать? =)

________________________________________________
= Сквозное суммирование листа ((2+6+1) (3+4+2) (5+2+1))
(setq lst '((2 3 5) (6 4 2) (1 2 1)) )
(mapcar '(lambda ( x ) (apply '+ x)) lst)
> (10 12 4)

Вполне себе простенькая лямбда, можно сказать, базовый пример, но до чего же изящно ^_^

________________________________________________
= Списковая ассоциация
(setq xd '((5 . "A") (6 . "C") (7. "D") ))
(setq yd '(5 6))
(mapcar '(lambda (q) (cdr (assoc q xd)) ) yd )
> ("A" "C")

Очевидно? А у меня вот не вышло даже методом подбора...
Слегка поднаторев к тому времени в мапкар лямбдах, я попытался задвинуть свою собственную виртуализацию:

________________________________________________
= Пересобрать лист в пары так, чтобы первый и последний мембер тоже образовали пару
'(1 2 3 4) -> '((1 2) (2 3) (3 4) (4 1))
(setq l '(1 2 3 4 5 6))

(append
(apply 'append (mapcar '(lambda (a b)  (if (and a b) (list (list a b)) ) ) (cons nil l) l ) )
(list (list (last l) (car l) ))
)
> ((1 2) (2 3) (3 4) (4 5) (5 6) (6 1))
И некоторое время это было круто, особенно учитывая обработку мапкаром нескольких списков, чьи значения передаются двум аргументам лямбды (и ведь подсмотрел же где-то?).
До те пор, пока это не увидел товарищ Lee Mac

(mapcar 'list l (append (cdr l) (list (car l))) )
> ((1 2) (2 3) (3 4) (4 5) (5 6) (6 1))

Я был готов рыдать от отчаяния. Спорить с восьмым уровнем просто невозможно. Один мапкар? И без всякой, уж тем более двухаргументной лямбды? Но дальше было ещё круче...

________________________________________________
= Приведённая функция работы со сложными листами

(defun LM:groupassoc ( l f / a b c d )
(if (setq a (caar l))
(progn
(foreach b l
(if (equal a (car b))
    (setq c (cons (cdr b) c))
    (setq d (cons b d))
)
)
(cons (cons a (f (reverse c))) (LM:groupassoc (reverse d) f))
)
)
)
Эта функция является вложенным ядром для следующих функций. Здесь особенно примечательна передача функциии в качестве аргумента. Для меня это было что-то вроде сродни отправки компьютера по емейлу.

Коллекция cadr'ов вложенных в лист листов

(defun LM:collectcons ( l )
(LM:groupassoc l (lambda ( l ) (list (mapcar 'car l))))
)

Подсчёт cadr'ов вложенных в лист листов

(defun LM:countcons ( l )
(LM:groupassoc l (lambda ( l ) (apply '+ (mapcar 'car l))))
)

Суммирование cadr'ов вложенных листов

(defun LM:triplesum ( l )
(LM:groupassoc l (lambda ( l ) (apply 'mapcar (cons '+ (apply 'append l)))))
)Например:

(LM:collectcons '(((5 "df") 2) ((3 "cb") 8) ((5 "df") 2) ((2 "12") 8)))
>(((5 "df") (2 2)) ((3 "cb") (8)) ((2 "12") (8)))

(LM:countcons '(((5 "df") 2) ((3 "cb") 8) ((5 "df") 2) ((2 "12") 8)))
>(((5 "df") . 4) ((3 "cb") . 8) ((2 "12") . 8))

(LM:triplesum '((A (5 5 6 2)) (B (6 2 6 3)) (A  (1 1 1 2)) (A (7 1 1 2)) ))
>((A 13 7 8 6) (B 6 2 6 3))

Поскольку представить хотя бы КАК до этого можно додуматься практически невозможно, создаётся стойкое ощущение, что Ли является виртуальной машиной, выделенным сервером для генерации лисп-кода. Мы уже проходили президентов на виртуальных машинах. И если всё это можно изменить, то Война... она никогда не меняется.

upd_______________________________
Прекрасный пример поиска дубликатов в листе с заданием допуска

(defun 1DLM:ListDupesFuzz ( l f / r )

(while l
      (if (vl-some '(lambda ( x ) (equal x (car l) f)) (cdr l))
        (progn
          (setq r
             (append
             (reverse
                 (vl-remove-if 'null (mapcar '(lambda ( x ) (if (equal x (car l) f) x )) (cdr l)))
                )
            r)
          )
           (setq r (cons (car l) r))
         )
      )
      (setq l (vl-remove-if '(lambda ( x ) (equal x (car l) f)) (cdr l)))
  )
  (reverse r)
)
;;;_________________________________

(setq db '(0.0 3.2 5.0 7.0 7.2 7.3 10.0 50.0 52.0 53.0 70.0 72.0 73.0 90.0))
(LM:ListDupesFuzz1 db 2.0)
>(5.0 3.2 7.3 7.2 7.0 53.0 52.0 73.0 72.0)
(LM:ListDupesFuzz1 db 1.0)
>(7.3 7.2 7.0)
(LM:ListDupesFuzz1 db 0.1)
>(7.3 7.2)

expression, autolisp, лисп, programming, autocad, lisp, mapcar, lambda

Previous post Next post
Up