模拟 scheme 在 common lisp 中定义
Simulate scheme define in common lisp
想知道如何在common lisp中模拟scheme define,想写一个宏来模拟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))
这有两个问题。
这允许 更多 而不仅仅是 Scheme arglists。 (即,它 "leaks" 一点。)如果你输入像 &rest、&optional、&key[ 这样的变量=56=],或者 Common Lisp 会特别对待的 &aux,你会得到奇怪的行为。例如,如果您这样做:
(pprint (macroexpand-1 '(define (foo a &rest b)
(list a b))))
; (DEFUN FOO (A &REST B) (LIST A &REST B))
在 Scheme 中,这将是三个参数的函数,但在 Common Lisp 中,它是任何正的非零参数的函数。
这不允许 所有 方案参数列表。特别是,它不处理带点的参数列表,如:
(define (list* x . xs)
(cons x xs))
要处理这些类型的情况,您需要稍微复杂一些的参数列表转换。这是一个 returns Common Lisp 函数可接受的新参数列表,它在函数体内重新建立原始 Scheme 参数列表中变量的绑定:
(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))))
例如:
CL-USER> (pprint (macroexpand-1 '(define (list . args)
args)))
; (DEFUN LIST (&REST #:G1068)
; (LET ((ARGS #:G1068))
; ARGS))
就是说,要在 Common Lisp 中不加修改地编写 Scheme 代码 运行,您需要的不仅仅是这些。 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
我不确定您是否可以执行任何代码分析来确定您需要将 (f 2 3) 转换为 (funcall f 2 3) 而你不会。
Scheme 中的 define
在 Common Lisp 中不容易实现。它也不容易在 Scheme 中实现。它在不同范围内的转换是不同的:
(define test value) ; defines test, value can be anything even (lambda ..) which makes a procedur object
(define (test arg ...) body ...) ; defines the procedure test
(define (somefun)
(define test ...)
(define (test2 x) ...)
...)
是一种奇特的写作方式:
(define (somefun)
(letrec ((test ...)
(test2 (lambda (x) ...))
...))
那么 Common Lisp 中的等价物是什么:
(define myplus otherfun) ; (setf (symbol-function 'myplus) otherfun)
(define myplus (lambda args . body)) ; (defun myplus args . body)
(define (myplus . args) . body ) ; (defun myplus args . body)
(define value 10) ; (defparameter value 10)
这是我对宏的看法。这仍然不适用于内部定义:
(defmacro define (name-or-ll &body expressions)
(flet ((dotted-to-rest (lst)
(let ((last (last lst)))
(if (null (cdr last))
lst
(append (butlast lst)
(list (car last) '&rest (cdr last)))))))
(cond ((consp name-or-ll) ; (define (fun a b) ...)
`(progn
(defun ,(car name-or-ll) ,(dotted-to-rest (cdr name-or-ll)) ,@expressions)
(defparameter ,(car name-or-ll) #',(car name-or-ll))))
((and (consp (car expressions)) ; (define fun (lambda (a b) ...))
(eq (caar expressions) 'lambda))
`(define (,name-or-ll ,@(cadar expressions)) ,@(cddar expressions)))
(t `(let ((value ,(cons 'progn expressions)))
(when (functionp value)
(setf (symbol-function ',name-or-ll) value))
(defparameter ,name-or-ll value))))))
(define proc (lambda (x) (* x x)))
(define myproc proc)
(define myplus #'+)
(define test 'test)
(define (testproc a b) (+ a b))
(define testproc2 (lambda (a . b) (apply myplus a b)))
(list (proc 10) (myproc 10)
(myplus 2 3) (testproc 2 3)
(testproc2 2 2 1) (funcall testproc2 2 2 1)
test) ; => (100 100 5 5 5 5 TEST)
想知道如何在common lisp中模拟scheme define,想写一个宏来模拟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))
这有两个问题。
这允许 更多 而不仅仅是 Scheme arglists。 (即,它 "leaks" 一点。)如果你输入像 &rest、&optional、&key[ 这样的变量=56=],或者 Common Lisp 会特别对待的 &aux,你会得到奇怪的行为。例如,如果您这样做:
(pprint (macroexpand-1 '(define (foo a &rest b) (list a b)))) ; (DEFUN FOO (A &REST B) (LIST A &REST B))
在 Scheme 中,这将是三个参数的函数,但在 Common Lisp 中,它是任何正的非零参数的函数。
这不允许 所有 方案参数列表。特别是,它不处理带点的参数列表,如:
(define (list* x . xs) (cons x xs))
要处理这些类型的情况,您需要稍微复杂一些的参数列表转换。这是一个 returns Common Lisp 函数可接受的新参数列表,它在函数体内重新建立原始 Scheme 参数列表中变量的绑定:
(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))))
例如:
CL-USER> (pprint (macroexpand-1 '(define (list . args)
args)))
; (DEFUN LIST (&REST #:G1068)
; (LET ((ARGS #:G1068))
; ARGS))
就是说,要在 Common Lisp 中不加修改地编写 Scheme 代码 运行,您需要的不仅仅是这些。 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
我不确定您是否可以执行任何代码分析来确定您需要将 (f 2 3) 转换为 (funcall f 2 3) 而你不会。
define
在 Common Lisp 中不容易实现。它也不容易在 Scheme 中实现。它在不同范围内的转换是不同的:
(define test value) ; defines test, value can be anything even (lambda ..) which makes a procedur object
(define (test arg ...) body ...) ; defines the procedure test
(define (somefun)
(define test ...)
(define (test2 x) ...)
...)
是一种奇特的写作方式:
(define (somefun)
(letrec ((test ...)
(test2 (lambda (x) ...))
...))
那么 Common Lisp 中的等价物是什么:
(define myplus otherfun) ; (setf (symbol-function 'myplus) otherfun)
(define myplus (lambda args . body)) ; (defun myplus args . body)
(define (myplus . args) . body ) ; (defun myplus args . body)
(define value 10) ; (defparameter value 10)
这是我对宏的看法。这仍然不适用于内部定义:
(defmacro define (name-or-ll &body expressions)
(flet ((dotted-to-rest (lst)
(let ((last (last lst)))
(if (null (cdr last))
lst
(append (butlast lst)
(list (car last) '&rest (cdr last)))))))
(cond ((consp name-or-ll) ; (define (fun a b) ...)
`(progn
(defun ,(car name-or-ll) ,(dotted-to-rest (cdr name-or-ll)) ,@expressions)
(defparameter ,(car name-or-ll) #',(car name-or-ll))))
((and (consp (car expressions)) ; (define fun (lambda (a b) ...))
(eq (caar expressions) 'lambda))
`(define (,name-or-ll ,@(cadar expressions)) ,@(cddar expressions)))
(t `(let ((value ,(cons 'progn expressions)))
(when (functionp value)
(setf (symbol-function ',name-or-ll) value))
(defparameter ,name-or-ll value))))))
(define proc (lambda (x) (* x x)))
(define myproc proc)
(define myplus #'+)
(define test 'test)
(define (testproc a b) (+ a b))
(define testproc2 (lambda (a . b) (apply myplus a b)))
(list (proc 10) (myproc 10)
(myplus 2 3) (testproc 2 3)
(testproc2 2 2 1) (funcall testproc2 2 2 1)
test) ; => (100 100 5 5 5 5 TEST)