Scheme 在公共lisp中模拟方案定义

Scheme 在公共lisp中模拟方案定义,scheme,lisp,common-lisp,Scheme,Lisp,Common Lisp,我想知道如何在CommonLisp中模拟SchemeDefine,并想编写一个宏来模拟define。那么cl的defun deparameter defvar和scheme的define之间有什么不同,我如何做到这一点呢?虽然不清楚您为什么要这样做,但可以使用以下方法进行粗略的近似: (defmacro define ((name &rest args) &body body) `(defun ,name ,args ,@body)) 然后,例如: (pprint

我想知道如何在CommonLisp中模拟SchemeDefine,并想编写一个宏来模拟define。那么cl的defun deparameter defvar和scheme的define之间有什么不同,我如何做到这一点呢?

虽然不清楚您为什么要这样做,但可以使用以下方法进行粗略的近似:

(defmacro define ((name &rest args) &body body)
  `(defun ,name ,args
     ,@body))
然后,例如:

(pprint (macroexpand-1 '(define (xcons x y) (cons x y))))
; (DEFUN XCONS (X Y)
;   (CONS X Y))
CL-USER> (pprint (macroexpand-1 '(define (list . args)
                                  args)))

; (DEFUN LIST (&REST #:G1068)
;   (LET ((ARGS #:G1068))
;     ARGS))
这有两个问题

这不仅允许Scheme arglist。也就是说,它有点泄漏。如果您输入诸如&rest、&optional、&key或&aux之类的变量,而Common Lisp将专门处理这些变量,那么您将得到奇怪的行为。例如,如果您这样做:

(pprint (macroexpand-1 '(define (foo a &rest b)
                         (list a b))))
; (DEFUN FOO (A &REST B) (LIST A &REST B))
在Scheme中,这将是三个参数的函数,但在公共Lisp中,它是任意正非零个参数的函数

这不允许所有方案参数列表。特别是,它不处理虚线arglist,如中所示:

(define (list* x . xs)
  (cons x xs))
要处理这些类型的情况,您需要更复杂的参数列表翻译。下面是一个返回通用Lisp函数可接受的新arglist的函数,它在函数体中重新启动原始Scheme arglist中变量的绑定:

(defun cl-arglist (scheme-arglist)
  "Return a Common Lisp arglist corresponding to the Scheme
arglist, and a list of bindings (as for LET) that will
re-establish the variables declared in the original Scheme
arglist."
  (labels ((convert (s-arglist cl-arglist bindings)
             (cond
               ((atom s-arglist)
                ;; dotted arglists
                (unless (null s-arglist)
                  (let ((cl-var (gensym)))
                    (setq cl-arglist (list* cl-var '&rest cl-arglist)
                          bindings (list* (list s-arglist cl-var) bindings))))
                (values (nreverse cl-arglist)
                        (nreverse bindings)))
               ;; variable in arglist
               (t
                (let ((s-var (first s-arglist))
                      (cl-var (gensym)))
                  (convert (rest s-arglist)
                           (list* cl-var cl-arglist)
                           (list* (list s-var cl-var) bindings)))))))
    (convert scheme-arglist '() '())))

(defmacro define ((name . arglist) . body)
  (multiple-value-bind (arglist bindings) (cl-arglist arglist)
    `(defun ,name ,arglist
       (let ,bindings
         ,@body))))
例如:

(pprint (macroexpand-1 '(define (xcons x y) (cons x y))))
; (DEFUN XCONS (X Y)
;   (CONS X Y))
CL-USER> (pprint (macroexpand-1 '(define (list . args)
                                  args)))

; (DEFUN LIST (&REST #:G1068)
;   (LET ((ARGS #:G1068))
;     ARGS))
也就是说,要使Scheme代码在CommonLisp中运行而无需修改,您需要的不仅仅是这些。Scheme只有一个名称空间,而Common Lisp有多个名称空间。这意味着在Scheme中,您可以编写:

(let ((f (lambda (x y) (+ x y))))
  (f 2 3))
;=> 5
在Common Lisp中,您必须编写:

(let ((f (lambda (x y) (+ x y))))
  (funcall f 2 3))
;=> 5
我不确定是否有任何代码分析可以确定所有需要将F2 3转换为funcall F2 3的情况以及不需要的情况

虽然不清楚您为什么要这样做,但可以使用以下方法进行粗略近似:

(defmacro define ((name &rest args) &body body)
  `(defun ,name ,args
     ,@body))
然后,例如:

(pprint (macroexpand-1 '(define (xcons x y) (cons x y))))
; (DEFUN XCONS (X Y)
;   (CONS X Y))
CL-USER> (pprint (macroexpand-1 '(define (list . args)
                                  args)))

; (DEFUN LIST (&REST #:G1068)
;   (LET ((ARGS #:G1068))
;     ARGS))
这有两个问题

这不仅允许Scheme arglist。也就是说,它有点泄漏。如果您输入诸如&rest、&optional、&key或&aux之类的变量,而Common Lisp将专门处理这些变量,那么您将得到奇怪的行为。例如,如果您这样做:

(pprint (macroexpand-1 '(define (foo a &rest b)
                         (list a b))))
; (DEFUN FOO (A &REST B) (LIST A &REST B))
在Scheme中,这将是三个参数的函数,但在公共Lisp中,它是任意正非零个参数的函数

这不允许所有方案参数列表。特别是,它不处理虚线arglist,如中所示:

(define (list* x . xs)
  (cons x xs))
要处理这些类型的情况,您需要更复杂的参数列表翻译。下面是一个返回通用Lisp函数可接受的新arglist的函数,它在函数体中重新启动原始Scheme arglist中变量的绑定:

(defun cl-arglist (scheme-arglist)
  "Return a Common Lisp arglist corresponding to the Scheme
arglist, and a list of bindings (as for LET) that will
re-establish the variables declared in the original Scheme
arglist."
  (labels ((convert (s-arglist cl-arglist bindings)
             (cond
               ((atom s-arglist)
                ;; dotted arglists
                (unless (null s-arglist)
                  (let ((cl-var (gensym)))
                    (setq cl-arglist (list* cl-var '&rest cl-arglist)
                          bindings (list* (list s-arglist cl-var) bindings))))
                (values (nreverse cl-arglist)
                        (nreverse bindings)))
               ;; variable in arglist
               (t
                (let ((s-var (first s-arglist))
                      (cl-var (gensym)))
                  (convert (rest s-arglist)
                           (list* cl-var cl-arglist)
                           (list* (list s-var cl-var) bindings)))))))
    (convert scheme-arglist '() '())))

(defmacro define ((name . arglist) . body)
  (multiple-value-bind (arglist bindings) (cl-arglist arglist)
    `(defun ,name ,arglist
       (let ,bindings
         ,@body))))
例如:

(pprint (macroexpand-1 '(define (xcons x y) (cons x y))))
; (DEFUN XCONS (X Y)
;   (CONS X Y))
CL-USER> (pprint (macroexpand-1 '(define (list . args)
                                  args)))

; (DEFUN LIST (&REST #:G1068)
;   (LET ((ARGS #:G1068))
;     ARGS))
也就是说,要使Scheme代码在CommonLisp中运行而无需修改,您需要的不仅仅是这些。Scheme只有一个名称空间,而Common Lisp有多个名称空间。这意味着在Scheme中,您可以编写:

(let ((f (lambda (x y) (+ x y))))
  (f 2 3))
;=> 5
在Common Lisp中,您必须编写:

(let ((f (lambda (x y) (+ x y))))
  (funcall f 2 3))
;=> 5
我不确定是否有任何代码分析可以确定所有需要将F2 3转换为funcall F2 3的情况以及不需要的情况

在通用Lisp中,在Scheme中定义不容易实现。它也不容易在Scheme中实现。它的转换在不同的范围内是不同的:

定义测试值;定义测试,值可以是任何值,甚至lambda。。这构成了一个过程对象 定义测试参数。。。身体定义程序测试 定义一些乐趣 定义测试。。。 定义Test2x。。。 ... 是一种奇特的写作方式:

定义一些乐趣 letrec测试。。。 测试2λx。。。 ... 那么,Common Lisp中的eqvivalent是什么:

定义myplus-otherfun;setf符号函数“myplus otherfun” 定义myplus lambda参数。身体定义myplus参数。身体 定义myplus。阿格斯。身体定义myplus参数。身体 定义值10;DEF参数值10 这是我对宏的看法。但这对内部定义不起作用:

defmacro定义名称或ll正文表达式(&M) 弗雷特点着休息 让我们最后一次 如果上次为null cdr lst 追加最后一个lst 列出“最后一辆车”和“最后一辆休息cdr” cond consp name或ll;定义乐趣a b。。。 `程序 defun,car name或ll,点到rest cdr name或ll,@expressions defparameter,车名或ll',车名或ll 消费汽车用语;定义有趣的lambda a b。。。 eq caar表达式的lambda `定义、命名或ll、@cadar表达式、@cddar表达式 t`let值,cons'程序表达式 当函数P值 setf符号函数',名称或ll值 defparameter、名称或ll值 定义过程lambda x*x 定义myproc proc 定义myplus'+ 定义“测试”测试 定义testproc a b+a b 定义testproc2 lambda。b申请myplus a b 锂 st proc 10 myproc 10 myplus 2 3测试程序2 3 testproc2 1函数所有testproc2 1 测试;=>100 5 5 5测试 在公共Lisp中,在Scheme中定义不容易实现。它也不容易在Scheme中实现。它的转换在不同的范围内是不同的:

定义测试值;定义测试,值可以是任何值,甚至lambda。。这构成了一个过程对象 定义测试参数。。。身体定义程序测试 定义一些乐趣 定义测试。。。 定义Test2x。。。 ... 是一种奇特的写作方式:

定义一些乐趣 letrec测试。。。 测试2λx。。。 ... 那么,Common Lisp中的eqvivalent是什么:

定义myplus-otherfun;setf符号函数“myplus otherfun” 定义myplus lambda参数。身体定义myplus参数。身体 定义myplus。阿格斯。身体定义myplus参数。身体 定义值10;DEF参数值10 这是我对宏的看法。但这对内部定义不起作用:

defmacro定义名称或ll正文表达式(&M) 弗雷特点着休息 让我们最后一次 如果上次为null cdr lst 追加最后一个lst 列出“最后一辆车”和“最后一辆休息cdr” cond consp name或ll;定义乐趣a b。。。 `程序 defun,car name或ll,点到rest cdr name或ll,@expressions defparameter,车名或ll',车名或ll 消费汽车用语;定义有趣的lambda a b。。。 eq caar表达式的lambda `定义、命名或ll、@cadar表达式、@cddar表达式 t`let值,cons'程序表达式 当函数P值 setf符号函数',名称或ll值 defparameter、名称或ll值 定义过程lambda x*x 定义myproc proc 定义myplus'+ 定义“测试”测试 定义testproc a b+a b 定义testproc2 lambda。b申请myplus a b 列表进程10 myproc 10 myplus 2 3测试程序2 3 testproc2 1函数所有testproc2 1 测试;=>100 5 5 5测试
你为什么要这么做?您尝试了什么?我想使用common lisp中的scheme代码,该代码与scheme代码一致。最大的问题是本地定义看起来与全局定义相同,但需要重写为标签。define可能是Scheme中最复杂的一种特殊形式,所以您实际上想用commonlisp编写一个Scheme解释器。看起来有点宽。哦,是的。我只是想知道是否有人用宏或其他东西来模拟CommonLisp中define的行为,所以我不需要手动更改代码。你为什么要这样做?您尝试了什么?我想使用common lisp中的scheme代码,该代码与scheme代码一致。最大的问题是本地定义看起来与全局定义相同,但需要重写为标签。define可能是Scheme中最复杂的一种特殊形式,所以您实际上想用commonlisp编写一个Scheme解释器。看起来有点宽。哦,是的。我只是想知道是否有人用宏或其他东西来模拟common lisp中define的行为,所以我不需要手动更改代码。必须将两个名称空间连接起来真的很麻烦。更糟糕的是,在lambdas中定义所有变量并将它们转换为标签。移植代码可能比生成宏更容易。@Sylvester完全同意。参数列表可能是最容易处理的事情之一。必须将两个名称空间连接起来,这真的很麻烦。更糟糕的是,在lambdas中定义所有变量并将它们转换为标签。移植代码可能比生成宏更容易。@Sylvester完全同意。参数列表可能是最容易处理的事情之一。是的,这就是我的想法。是的,这就是我的想法。