模拟方案在常见的lisp中定义

时间:2015-03-09 09:45:44

标签: scheme lisp common-lisp

我想知道如何在常见的lisp中模拟方案定义,并且想要编写一个用于模拟定义的宏。那么cl的defun deparameter defvar和scheme的定义有什么不同,以及我如何做到这一点?

2 个答案:

答案 0 :(得分: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中的eqvivalents是什么:

(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)

答案 1 :(得分:1)

虽然为什么

(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))

这有两个问题。

  1. 这允许更多而不仅仅是Scheme arglists。 (即,它"泄漏"一点。)如果您输入的变量如& rest & optional & key ,或者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中,它是任何正的非零数量参数的函数。

  2. 这不允许所有 Scheme arglists。特别是,它没有处理虚线的arglists,如:

    (define (list* x . xs)
      (cons x xs))
    
  3. 要处理这些类型的案例,您需要更复杂的参数列表转换。这是一个返回Common 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))))
    

    例如:

    CL-USER> (pprint (macroexpand-1 '(define (list . args)
                                      args)))
    
    ; (DEFUN LIST (&REST #:G1068)
    ;   (LET ((ARGS #:G1068))
    ;     ARGS))
    

    那就是说,你需要更多的东西来使Scheme代码运行而不需要修改Common Lisp。 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),你不会。