Common LISP — Как вернуть оператор

В настоящее время я пытаюсь научиться программировать на "Common Lisp". Поэтому я сейчас занимаюсь небольшой проблемой, касающейся определения макросов. Упомянутый макрос должен работать следующим образом: он берет список чисел, создает временный (изначально пустой) список, который заполняется числами в квадрате и возвращает его. К сожалению, у меня есть некоторые проблемы с этим исходным кодом.

    (defmacro square_loop (args)
        (let ((res ()))
            (loop for x in args
                do (push (* x x) res))
            res
        )
    )

Что касается сообщений об ошибках, я думаю, что ошибка заключается в том, что я возвращаю не список, а вызов функции, который имеет то же содержимое, что и список. Однако это будет расцениваться как вызов функции, что неверно. Есть ли способ, как я могу написать макрос правильно?

РЕДАКТИРОВАТЬ
Я знаю, что технически могу решить эту проблему, используя такие функции, как mapcar. Тем не менее, я хотел посмотреть, возможна ли вообще моя идея макроса.

Макросы не для этого. Если вам нужна функция, используйте функцию. Макрос — это то, что преобразует исходный код в другой исходный код и используется для расширения языка.

ignis volens 30.03.2023 13:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
86
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я думаю, вы не понимаете, что такое макросы и каков их вариант использования. Я предлагаю вам прочитать такие материалы, как

7. Макросы: стандартные конструкции управления

8. Макросы: определяем свои собственные

2.4.6 Обратная цитата + запятая (операторы, используемые в моем решении)

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

Но только для обучения...

(defmacro square-loop (args)
  (let ((res '()))
    (loop for x in args
          do (push (* x x) res))
    `',res))

> (square-loop (1 2 3))
(9 4 1)

Обратите внимание на три вещи:

  • чехол для шашлыка (квадратная_петля -> квадратная петля)
  • правильное оформление скобок
  • push помещает каждый элемент в начало списка, поэтому конечный результат фактически инвертируется — я не уверен, хотите ли вы этого, поэтому вот аналогичное решение без реверсирования:
(defmacro square-loop (args)
  `',(mapcar (lambda (x) (* x x)) args))

> (square-loop (1 2 3))
(1 4 9)

Спасибо за подробный ответ. Короткий дополнительный вопрос: Итак, если макросы используются для расширения языка, определение DSL было бы подходящей ситуацией, когда я мог бы использовать макросы, верно?

IlikedCPlusPlus 30.03.2023 13:44

@IlikedCPlusPlus: да, макросы для этого и нужны: беспрепятственно расширять язык, который у вас есть, на язык, который вы хотите.

ignis volens 30.03.2023 14:01
CL-USER 1 > (defmacro square_loop (args)
              (let ((res ()))
                (loop for x in args
                      do (push (* x x) res))
                res))
SQUARE_LOOP

CL-USER 2 > (square_loop (list 1 2 3))

Error: In * of (LIST LIST) arguments should be of type NUMBER.
  1 (continue) Return a value to use.
  2 Supply new arguments to use.
  3 (abort) Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

CL-USER 3 : 1 > 

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

Итак, есть один случай, когда он может сделать что-то полезное:

CL-USER 6 > (square_loop (1 2 3 4))

Error: Illegal car 16 in compound form (16 9 4 1).
  1 (continue) Evaluate 16 and ignore the rest.
  2 (abort) Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

CL-USER 7 : 1 > 

Так что даже это не работает, так как макрос возвращает (16 9 4 1) в качестве результата -> в виде кода! Но (16 9 4 1) недопустимый код Lisp. Помните, что корректный код на Лиспе предполагает наличие оператора (функции, макроса, специальной формы или лямбда-выражения) в качестве первого элемента формы на Лиспе. Но 16 не допустимый оператор, это число:

CL-USER 8 > (16 9 4 1)

Error: Illegal car 16 in compound form (16 9 4 1).
  1 (continue) Evaluate 16 and ignore the rest.
  2 (abort) Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

CL-USER 9 : 1 > 

Если вы хотите вернуть данные, вам нужно будет указать их:

'(16 9 4 1)

Мы можем изменить макрос:

CL-USER 10 > (defmacro square_loop (args)
              (let ((res ()))
                (loop for x in args
                      do (push (* x x) res))
                (list 'quote res)))
SQUARE_LOOP

CL-USER 11 > (square_loop (1 2 3 4))
(16 9 4 1)

Выше берет буквальный список и создает новую форму: (quote (16 9 4 1)). Затем это выражение в кавычках оценивается как (16 9 4 1).

Тем не менее: (square_loop (list 1 2 3 4)) не сработает...

И:

(let ((list '(1 2 3 4)))
  (square_loop list))

Выше тоже не получится. Почему? Это бы тебе узнать...

Другие вопросы по теме