Dans "The Scheme Programming Language 4ème édition" section 3.3 Continuations l'exemple suivant est donné:
(let ([f some-value]) ...)
Je peux confirmer que cela fonctionne dans chezscheme comme écrit:
syntax: (let ((var expr) ...) body1 body2 ...)
Qu'est-ce que « f
» dans le let
ci-dessus? Pourquoi le ls
donné est-il attribué à lui-même? Cela ne semble pas correspondre à ce que je comprends de (let ...)
comme décrit dans 4.4 liaison locale :
> (product '(1 2 3 4 5)) 120
Si ' f
' est défini ici, je l'attendrait entre parenthèses / crochets:
(define product (lambda (ls) (call/cc (lambda (break) (let f ([ls ls]) (cond [(null? ls) 1] [(= (car ls) 0) (break 0)] [else (* (car ls) (f (cdr ls)))]))))))
3 Réponses :
f
est lié à une procédure qui a le corps de let
comme corps et ls
comme paramètre.
http: //www.r6rs .org / final / html / r6rs / r6rs-ZH-14.html # node_sec_11.16
Ceci est 'nommé let', et c'est une commodité syntaxique.
(defmacro binding (name/bindings &body bindings/decls/forms) ;; let / named let (typecase name/bindings (list `(let ,name/bindings ,@bindings/decls/forms)) (symbol (unless (not (null bindings/decls/forms)) (error "a syntax")) (destructuring-bind (bindings . decls/forms) bindings/decls/forms (unless (listp bindings) (error "another syntax")) (unless (listp decls/forms) (error "yet another syntax")) (multiple-value-bind (args inits) (loop for binding in bindings do (unless (and (listp binding) (= (length binding) 2) (symbolp (first binding))) (error "a more subtle syntax")) collect (first binding) into args collect (second binding) into inits finally (return (values args inits))) `(labels ((,name/bindings ,args ,@decls/forms)) (,name/bindings ,@inits))))) (t (error "yet a different syntax"))))
est plus ou moins équivalent à
(define (outer ...) (define (inner x ...) ... (inner ...) ...) (inner y ...))
ou, dans des contextes appropriés, à un define
local suivi d'un appel:
(define (outer ...) (let inner ([x y] ...) ... (inner ...) ...))
est plus ou moins équivalent à
XXX
La bonne chose à propos de let
nommé est qu'il met la définition et l'appel initial de la fonction locale au même endroit.
Cavemen comme moi qui utilise CL utilise parfois des macros comme binding
, ci-dessous, pour implémenter cela (notez que ce n'est pas du code de production: tous ses messages d'erreur sont des blagues obscures):
(letrec ([f (λ (x ...) ... (f ...) ...)]) (f y ...))
TWIMC: "CL" == "Common Lisp". comme nous savons tous , Lisp n'a pas de syntaxe! ;) Common Lisp d'autant plus. Toutes ses constructions, commençant par prog
, ne sont que des S-expressions! ;)
@WillNess: Lisp a beaucoup de syntaxe. En effet, puisque sa syntaxe est extensible par l'utilisateur, elle en a une quantité illimitée. Ce qu'il n'a pas habituellement, c'est beaucoup de syntaxe lexicale .
@tfb Merci, votre réponse a bien clarifié cela. Puisque je suis un amateur de stratagèmes au mieux, cette forme particulière a échappé à mon attention parce que je n’ai pas lu toutes les informations sur la récursivité. J'aurais aimé qu'il soit expliqué dans la section let normale, ou bien référencé clairement dans cette section avec le nom de la section dans laquelle il est réellement défini, ou qu'il ait un autre nom comme "letrec-and-execute"
Pensez à cette procédure:
(define (sum lst-arg) (let helper ((lst lst-arg) (acc 0)) (if (null? lst) acc (helper (cdr lst) (+ (car lst) acc)))))
Nous pouvons utiliser let
nommé au lieu de définir une procédure locale et ensuite l'utiliser comme ceci:
(define (sum lst) (define (helper lst acc) (if (null? lst) acc (helper (cdr lst) (+ (car lst) acc)))) (helper lst 0)) (sum '(1 2 3)) ; ==> 6
Ce sont exactement le même code à l'exception de certaines situations de dénomination en double. lst-arg
peut avoir le même nom lst
et ce n'est jamais le même que lst
dans le laissez
.
Le nom let
est facile à comprendre. call / cc
prend généralement un peu de maturation. Je n'ai pas reçu call / cc
avant de commencer à créer mes propres implémentations.