📄 optargs.scm
字号:
;; paramater list, but before any dotted rest argument. For example,;; (lambda* (a b #:optional c d . e) '());; creates a procedure with fixed arguments a and b, optional arguments c;; and d, and rest argument e. If the optional arguments are omitted;; in a call, the variables for them are bound to `#f'.;;;; lambda* can also take keyword arguments. For example, a procedure;; defined like this:;; (lambda* (#:key xyzzy larch) '());; can be called with any of the argument lists (#:xyzzy 11);; (#:larch 13) (#:larch 42 #:xyzzy 19) (). Whichever arguments;; are given as keywords are bound to values.;;;; Optional and keyword arguments can also be given default values;; which they take on when they are not present in a call, by giving a;; two-item list in place of an optional argument, for example in:;; (lambda* (foo #:optional (bar 42) #:key (baz 73)) (list foo bar baz));; foo is a fixed argument, bar is an optional argument with default;; value 42, and baz is a keyword argument with default value 73.;; Default value expressions are not evaluated unless they are needed;; and until the procedure is called.;;;; lambda* now supports two more special parameter list keywords.;;;; lambda*-defined procedures now throw an error by default if a;; keyword other than one of those specified is found in the actual;; passed arguments. However, specifying #:allow-other-keys;; immediately after the keyword argument declarations restores the;; previous behavior of ignoring unknown keywords. lambda* also now;; guarantees that if the same keyword is passed more than once, the;; last one passed is the one that takes effect. For example,;; ((lambda* (#:key (heads 0) (tails 0)) (display (list heads tails)));; #:heads 37 #:tails 42 #:heads 99);; would result in (99 47) being displayed.;;;; #:rest is also now provided as a synonym for the dotted syntax rest;; argument. The argument lists (a . b) and (a #:rest b) are equivalent in;; all respects to lambda*. This is provided for more similarity to DSSSL,;; MIT-Scheme and Kawa among others, as well as for refugees from other;; Lisp dialects.(defmacro lambda* (ARGLIST . BODY) (parse-arglist ARGLIST (lambda (non-optional-args optionals keys aok? rest-arg) ; Check for syntax errors. (if (not (every? symbol? non-optional-args)) (error "Syntax error in fixed argument declaration.")) (if (not (every? ext-decl? optionals)) (error "Syntax error in optional argument declaration.")) (if (not (every? ext-decl? keys)) (error "Syntax error in keyword argument declaration.")) (if (not (or (symbol? rest-arg) (eq? #f rest-arg))) (error "Syntax error in rest argument declaration.")) ;; generate the code. (let ((rest-gensym (or rest-arg (gensym "lambda*:G"))) (lambda-gensym (gensym "lambda*:L"))) (if (not (and (null? optionals) (null? keys))) `(let ((,lambda-gensym (lambda (,@non-optional-args . ,rest-gensym) ;; Make sure that if the proc had a docstring, we put it ;; here where it will be visible. ,@(if (and (not (null? BODY)) (string? (car BODY))) (list (car BODY)) '()) (let-optional* ,rest-gensym ,optionals (let-keywords* ,rest-gensym ,aok? ,keys ,@(if (and (not rest-arg) (null? keys)) `((if (not (null? ,rest-gensym)) (error "Too many arguments."))) '()) (let () ,@BODY)))))) (set-procedure-property! ,lambda-gensym 'arglist '(,non-optional-args ,optionals ,keys ,aok? ,rest-arg)) ,lambda-gensym) `(lambda (,@non-optional-args . ,(if rest-arg rest-arg '())) ,@BODY))))))(define (every? pred lst) (or (null? lst) (and (pred (car lst)) (every? pred (cdr lst)))))(define (ext-decl? obj) (or (symbol? obj) (and (list? obj) (= 2 (length obj)) (symbol? (car obj)))))(define (parse-arglist arglist cont) (define (split-list-at val lst cont) (cond ((memq val lst) => (lambda (pos) (if (memq val (cdr pos)) (error (with-output-to-string (lambda () (map display `(,val " specified more than once in argument list."))))) (cont (reverse (cdr (memq val (reverse lst)))) (cdr pos) #t)))) (else (cont lst '() #f)))) (define (parse-opt-and-fixed arglist keys aok? rest cont) (split-list-at #:optional arglist (lambda (before after split?) (if (and split? (null? after)) (error "#:optional specified but no optional arguments declared.") (cont before after keys aok? rest))))) (define (parse-keys arglist rest cont) (split-list-at #:allow-other-keys arglist (lambda (aok-before aok-after aok-split?) (if (and aok-split? (not (null? aok-after))) (error "#:allow-other-keys not at end of keyword argument declarations.") (split-list-at #:key aok-before (lambda (key-before key-after key-split?) (cond ((and aok-split? (not key-split?)) (error "#:allow-other-keys specified but no keyword arguments declared.")) (key-split? (cond ((null? key-after) (error "#:key specified but no keyword arguments declared.")) ((memq #:optional key-after) (error "#:optional arguments declared after #:key arguments.")) (else (parse-opt-and-fixed key-before key-after aok-split? rest cont)))) (else (parse-opt-and-fixed arglist '() #f rest cont))))))))) (define (parse-rest arglist cont) (cond ((null? arglist) (cont '() '() '() #f #f)) ((not (pair? arglist)) (cont '() '() '() #f arglist)) ((not (list? arglist)) (let* ((copy (list-copy arglist)) (lp (last-pair copy)) (ra (cdr lp))) (set-cdr! lp '()) (if (memq #:rest copy) (error "Cannot specify both #:rest and dotted rest argument.") (parse-keys copy ra cont)))) (else (split-list-at #:rest arglist (lambda (before after split?) (if split? (case (length after) ((0) (error "#:rest not followed by argument.")) ((1) (parse-keys before (car after) cont)) (else (error "#:rest argument must be declared last."))) (parse-keys before #f cont))))))) (parse-rest arglist cont));; define* args . body;; define*-public args . body;; define and define-public extended for optional and keyword arguments;;;; define* and define*-public support optional arguments with;; a similar syntax to lambda*. They also support arbitrary-depth;; currying, just like Guile's define. Some examples:;; (define* (x y #:optional a (z 3) #:key w . u) (display (list y z u)));; defines a procedure x with a fixed argument y, an optional agument;; a, another optional argument z with default value 3, a keyword argument w,;; and a rest argument u.;; (define-public* ((foo #:optional bar) #:optional baz) '());; This illustrates currying. A procedure foo is defined, which,;; when called with an optional argument bar, returns a procedure that;; takes an optional argument baz.;;;; Of course, define*[-public] also supports #:rest and #:allow-other-keys;; in the same way as lambda*.(defmacro define* (ARGLIST . BODY) (define*-guts 'define ARGLIST BODY))(defmacro define*-public (ARGLIST . BODY) (define*-guts 'define-public ARGLIST BODY));; The guts of define* and define*-public.(define (define*-guts DT ARGLIST BODY) (define (nest-lambda*s arglists) (if (null? arglists) BODY `((lambda* ,(car arglists) ,@(nest-lambda*s (cdr arglists)))))) (define (define*-guts-helper ARGLIST arglists) (let ((first (car ARGLIST)) (al (cons (cdr ARGLIST) arglists))) (if (symbol? first) `(,DT ,first ,@(nest-lambda*s al)) (define*-guts-helper first al)))) (if (symbol? ARGLIST) `(,DT ,ARGLIST ,@BODY) (define*-guts-helper ARGLIST '())));; defmacro* name args . body;; defmacro*-public args . body;; defmacro and defmacro-public extended for optional and keyword arguments;;;; These are just like defmacro and defmacro-public except that they;; take lambda*-style extended paramter lists, where #:optional,;; #:key, #:allow-other-keys and #:rest are allowed with the usual;; semantics. Here is an example of a macro with an optional argument:;; (defmacro* transmorgify (a #:optional b)(defmacro defmacro* (NAME ARGLIST . BODY) (defmacro*-guts 'define NAME ARGLIST BODY))(defmacro defmacro*-public (NAME ARGLIST . BODY) (defmacro*-guts 'define-public NAME ARGLIST BODY));; The guts of defmacro* and defmacro*-public(define (defmacro*-guts DT NAME ARGLIST BODY) `(,DT ,NAME (,(lambda (transformer) (defmacro:transformer transformer)) (lambda* ,ARGLIST ,@BODY))));;; optargs.scm ends here
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -