标签: lisp-macros

这个反引号“语法”如何在Lisp中工作?

这是Paul Graham撰写的On Lisp上的简化示例(方案类似于语法)。

(define-macro (bar)
  (let ((x 10) (y '(1 2 3)) (z 'foo))
    `(list ,x `(,',z ,,@y))))
Run Code Online (Sandbox Code Playgroud)

我知道,,@y应该如何工作,但不能确切确定,',z应该如何工作,首先应该评估什么,以什么顺序进行评估。(我知道它应该评估为foo符号,因为它(10 (foo 1 2 3))以guile形式返回,但是我不确定确切的步骤是什么)。

我需要在JavaScript中使用lisp来获得结果:

(10 ((unquote z) 1 2 3))
Run Code Online (Sandbox Code Playgroud)

因为它只评估它从左到右的形式(我只处理特殊的,,逗号)。您应该如何评估此表达式。

书中也有这个例子:

(defmacro propmacro (propname)
   `(defmacro ,propname (obj)
       `(get ,obj ',',propname)))
Run Code Online (Sandbox Code Playgroud)

',',应该如何评估?在这种情况下应采取什么步骤?

还有其他带有反引号/准引号的怪异情况吗?您能否显示这些示例以及应如何评估它们以及以什么顺序进行示例?

lisp macros common-lisp lisp-macros

4
推荐指数
1
解决办法
107
查看次数

setf如何在引擎盖下工作?

目前正在学习常见的口齿不清,继Peter Seibel的Practical Common Lisp(我在第11章,关于收藏)之后,我很难理解setf幕后的工作方式.

考虑这个表达式:

(setf a 10)
Run Code Online (Sandbox Code Playgroud)

我完全理解解释器如何(1)检索名为的变量a,以及(2)更改它指向的值10.

现在,我是特定集合的例子,例如列表,向量或散列表,setf也可用于更改集合包含的值.例如,使用向量:

(defparameter *x* '(a b c d))
(setf (elt *x* 1) bb)
Run Code Online (Sandbox Code Playgroud)

这让我怀疑setf,因为它最终会找到非平凡的信息,或制造黑魔法.我看到了多种可能性.

1. setf是一个函数

(elt *x* 1)表达式返回'b,因此setf实际上被有工作(setf b bb).然后我不明白如何setf推断*x*它必须修改哪个对象(这里是列表),而不具有elt保持它来自集合的指示的返回值,以及指向所述集合的指针.看似复杂.

2. setf是一个宏

这个想法就像setf宏一样,它直接与它一起工作(setf (elt *x* 1) bb),因此可以提取elt *x* 1部件来推断使用哪个对象/集合,因此必须进行修改.

它似乎不是非常有效,也不可靠,也不能抵抗复杂的操作.但是,因为我无法运行此代码:

(funcall (find-symbol (concatenate 'string "E" "LT")) *x* 1)  ; -> B …
Run Code Online (Sandbox Code Playgroud)

common-lisp setf lisp-macros

3
推荐指数
2
解决办法
511
查看次数

具有宏列表的宏作为Common Lisp中的参数

在Common Lisp中,如何定义一个"元宏",它将宏(和其他参数)列表作为参数,并组合这些宏以生成所需的代码.

这个问题等同于编写一个"高阶宏",它定义了其他宏的变量列表中的宏.

提示问题的具体情况是我对CLSQL的实验,我想从CLSQL-testsuite重新表达员工类

(clsql:def-view-class employee ()
  ((employee-id
    :db-kind :key
    :db-constraints (:not-null)
    :type integer)
   (first-name
    :accessor employee-first-name
    :type (string 30)
    :initarg :first-name)
   (last-name
    :accessor employee-last-name
    :type (string 30)
    :initarg :last-name)
   (email
    :accessor employee-email
    :type (string 100)
    :initarg :email)
   (company-id
     :type integer
     :initarg :company-id)
   (company
    :accessor employee-company
    :db-kind :join
    :db-info (:join-class company
              :home-key companyid
              :foreign-key companyid
              :set nil))
   (manager-id
    :type integer
    :nulls-ok t
    :initarg :manager-id)
   (manager
    :accessor employee-manager
    :db-kind :join
    :db-info (:join-class employee
              :home-key managerid
              :foreign-key emplid
              :set nil))))
Run Code Online (Sandbox Code Playgroud)

(def-view-class-with-traits …
Run Code Online (Sandbox Code Playgroud)

common-lisp clsql lisp-macros

3
推荐指数
1
解决办法
87
查看次数

如何在球拍中查看扩展宏?

我得到了这个答案/sf/answers/4922329401/关于编写一个简单的宏来记录宏扩展时的时间,然后总是返回该时间。

#lang racket

(begin-for-syntax
  (define the-time (current-seconds)))

(define-syntax (macro-expansion-seconds stx)
  (datum->syntax stx the-time))

(macro-expansion-seconds)
(macro-expansion-seconds)
(macro-expansion-seconds)
Run Code Online (Sandbox Code Playgroud)

它工作得很好,但现在有没有一种简单的方法可以在(macro-expansion-seconds)不评估它的情况下查看它的扩展版本?(用于调试更复杂的)

racket lisp-macros

3
推荐指数
1
解决办法
516
查看次数

lisp 宏来构建表达式列表及其评估

我正在尝试在 Common Lisp 中编写一个宏,它接受任意数量的表达式并构建一个包含每个表达式的列表,然后在一行中对其进行评估。例如,如果我将宏命名为

(defmacro list-builder (&rest exp)
    ...)
Run Code Online (Sandbox Code Playgroud)

我跑

(let ((X 1) (Y 2)) (list-builder (+ X Y) (- Y X) X))
Run Code Online (Sandbox Code Playgroud)

我希望它返回:

'((+ X Y) 3 (- Y X) 1 X 1)
Run Code Online (Sandbox Code Playgroud)

到目前为止我能做的最好的是使用代码获取表达式列表

(defmacro list-builder (&rest exp)
  `',@`(',exp ,exp))

INPUT: (let ((X 1) (Y 2)) (list-builder (+ X Y) (+ Y X) X))
'((+ X Y) (+ Y X) X)
Run Code Online (Sandbox Code Playgroud)

macros common-lisp lisp-macros

2
推荐指数
1
解决办法
504
查看次数

Common Lisp 本地影子函数同名

我以前不止一次有过这个问题。

一般问题

是否可以使用具有相同名称的包装器在本地ff透明地隐藏函数

即,如何在本地将 (f 包裹的参数...) 扩展为 (f args...)?

Flet 似乎允许我们这样做,但有局限性,即生成的包装器不可设置。是否有可能在不诉诸舰队的情况下这样做?

理想情况下,会有一个宏让我们编写“包装”f调用,并将代码扩展为原始的“非包装”f调用。

起初我相信macrolet可能是这样,因为它在文档中说它首先扩展宏然后在扩展形式上应用setf,但我无法使用它(继续阅读下面)。

动机

这在某些参数是隐式的并且不应一遍又一遍地重复以获取更多 DRY 代码的上下文中很有用。

我之前的问题 (let-curry) 中有一个特定的例子。试图“自动”分配函数的一些参数(let-curry)。

船队注意事项

我在那里得到了一些很好的答案,但是,我遇到了一些限制。通过使用 flet 来完成函数名称的这种局部“阴影”到其上的包装器,此类包装器不可设置,因此,此类包装器不能像原始函数一样灵活使用,只能读取值,而不是写入.

具体问题

通过上面的链接,如何编写宏 flet-curry 并使包装函数可设置?

奖励:该宏能否以 0 运行时开销将包装的调用扩展到原始调用?

我尝试在该帖子中选择答案并使用 macrolet 而不是 flet 无济于事。

谢谢!


更新

我被要求为这个通用问题举一个具体的例子。

代码中的愿望注释:

(locally (declare (optimize safety))
  (defclass scanner ()
    ((source
      :initarg :source
      :accessor source
      :type string)
     (tokens
      :initform nil
      :accessor tokens
      :type list)
     (start
      :initform 0
      :accessor start
      :type …
Run Code Online (Sandbox Code Playgroud)

macros common-lisp lisp-macros

2
推荐指数
1
解决办法
111
查看次数

使用宏来定义计算10个与复活节相关的日期的函数

我目前正在学习格雷厄姆的书"ANSI Common Lisp"中的lisp,作为练习,我正在编写基于Julian-day的日历计算.如您所知,复活节星期日每年都在变化,大约有10个特殊日子,实际日期取决于复活节星期日.

我想为每一天定义一个函数,所有函数都遵循以下模式:

(defun carnaval (year)
  "Carnaval Monday of YEAR.

This is 48 days before Easter Sunday."
  (- (easter year) 48))
Run Code Online (Sandbox Code Playgroud)

使用宏似乎是合适的,而不是重复10次类似的声明:

(defmacro %defeasterday (function-name screen-name offset)
  `(defun ,function-name (year)
     ,(format nil "~A of YEAR.~%~%This is ~A day~:p ~A Easter Sunday"
              screen-name
              (abs offset)
              (if (< 0 offset) "after" "before"))
     (+ (easter year) ,offset)))
Run Code Online (Sandbox Code Playgroud)

(起始%标记我的意图是不将宏导出到定义它的包中.)

该宏可用于为日期基于东方星期日的每一天定义一个函数:

(%defeasterday carnaval   "Carnaval Monday" -48)
(%defeasterday mardi-gras "Mardi gras"      -47)
(%defeasterday ash        "Ash Wednesday"   -46)
…
Run Code Online (Sandbox Code Playgroud)

现在,为了练习,我想将所有数据打包到列表中并%defeasterday在其项目上使用宏.我的尝试是

(mapc #'(lambda …
Run Code Online (Sandbox Code Playgroud)

time common-lisp lisp-macros

1
推荐指数
1
解决办法
91
查看次数

Common Lisp 中的 lambda 表达式是宏还是标记?

我正在尝试通过《Common Lisp:符号计算的温和介绍》一书来学习 Common Lisp 。此外,我正在使用 SBCL、Emacs 和 Slime。

在第 7 章中,作者提出以下建议lambda expressions在此处输入图片说明

这让我感到困惑,因为 SBCL 的 REPL 返回:

CL-USER> #'lambda
#<CLOSURE (:MACRO LAMBDA) {1000D736DB}>
Run Code Online (Sandbox Code Playgroud)

显然,作者使用了Lisp Works(不是 100% 确定)。我认为这与上述差异无关。不过,我觉得还是提一下比较好。

我的 SBCL 的 REPL 也返回macro了众所周知的宏,例如and

CL-USER> #'and
#<CLOSURE (:MACRO AND) {1000D7365B}>
Run Code Online (Sandbox Code Playgroud)

请注意,具有“普通”功能的行为例如append是不同的:

CL-USER> #'append
#<FUNCTION APPEND>
Run Code Online (Sandbox Code Playgroud)

这里的这篇文章似乎稍微触及了lambda 表达式的非单一性质。但是,它没有提到任何有关标记的内容

我是否遗漏了 lambda 表达式的本质?

lambda common-lisp markers read-eval-print-loop lisp-macros

1
推荐指数
1
解决办法
75
查看次数

为什么在编译函数定义时会对宏进行求值(Clozure Common Lisp)?

我有:

(defmacro assign (name value)
  (format t "assigning ~A to ~A~%" `,name `,value))

(defun opcode-call (&rest args)
  (mapcar (lambda (arg) 
             (if (stringp arg) 
                 (let ((var (gensym)))
                   (assign var arg)
                   var) 
                 arg)) 
          args))
Run Code Online (Sandbox Code Playgroud)

当我编译操作码调用时,REPL输出:

assigning VAR to ARG
OPCODE-CALL
Run Code Online (Sandbox Code Playgroud)

为什么在编译时对赋值进行求值?

common-lisp ccl lisp-macros

0
推荐指数
1
解决办法
45
查看次数