Je veux donc savoir s'il existe un moyen standard d'avoir un code comme celui-ci:
(let ((x 10)) (define (add1) (+ x 1)) (define (add2) (+ x 2)) (define (add3) (+ x 3)))
Je connais:
(let ((x 10)) (macro x))
mais cela ne fonctionnera pas si je veux définir plusieurs fonctions, j'ai besoin de connaître la méthode standard pour pouvoir écrire une macro qui définira des fonctions. où vous pouvez appeler la macro à l'intérieur let:
(define add10 (let ((x 10)) (lambda (a) (+ x a))))
et par exemple la macro créera une liste de fonctions:
(let ((x 10)) (define (add10 a) (+ x a)))
Y a-t-il manière standard de définir les fonctions add1..add3? Dans le schéma que je testais, les fonctions seront locales à l'intérieur de let
et non accessibles à l'extérieur.
Si vous affichez le code de macro, je ne suis intéressé que par les macros lisp avec define -macro
et quasiquote
, s'il vous plaît pas de define-syntax
, car c'est principalement pour une utilisation dans mon propre lisp (basé sur un schéma) où je n'ai que des macros lisp .
Si le schéma ne prend pas en charge quelque chose comme ça, est-ce qu'un autre dialecte comme Common Lisp autorise quelque chose comme ça?
3 Réponses :
(let ((x 10)) () (define (add1) (+ x 1)))
Le dernier paragraphe de clhs.lisp.se/Body/s_eval_w.htm dit que laissez over eval-when pourrait être utile pour defun / defmacro, alors que la description dit que les effets secondaires à la compilation ne se produisent que lorsque eval-when est un formulaire de premier niveau. Peut-être qu'il y a un moyen d'atteindre ce que OP veut en enveloppant le tout dans eval-when (et je suis d'accord que c'est un mauvais style, btw)
La fonction étendue aura (+ x 1)
ou (+ 10 1)
ce dont j'ai besoin est (+ x 1)
. J'essaie de simplifier le problème réel que j'ai, où j'ai un objet qui doit être en fermeture non évalué et mis dans le code lisp, car il ne peut pas avoir de représentation lèvres comme le numéro 10.
@jcubic: la fonction développée a x
.
Ce dernier exemple fonctionne dans certains schémas et pas dans d'autres, je pense que cela fonctionnait dans la guile précédente ou dans Kawa (schéma en Java).
Pouvez-vous expliquer ce que cette liste vide fait dans le schéma? Cela semble fonctionner dans biwascheme mais je ne sais pas pourquoi. où sans liste vide si ne fonctionne pas.
@jcubic: la liste vide correspond à la liste vide. ;-)
mais quelle est la différence si vous l'avez et ne l'avez pas. vérifiez biwascheme.org si vous l'utilisez add1 est défini globalement et sans lui, il est local. C'est bizarre cela fonctionne (let ((x 10)) 1 (define (add2) (+ x 2)))
C'est bizarre, c'est peut-être un bogue dans l'implémentation, est-ce dans la spécification du schéma?
C'est dans la spécification Scheme. «Les définitions peuvent apparaître au début d'un« corps ». Maintenant, ce qui pourrait ne pas être dans la spécification: que faire avec les DEFINE non locaux de niveau supérieur à l'intérieur d'un LET.
Cela ne fonctionne pas par ruse, lancez une erreur que la définition est dans un contexte d'expression où ils ne sont pas autorisés.
C'est pourquoi j'ai écrit «pourrait». Cela pourrait être une chose spécifique à la mise en œuvre. On peut également vérifier si une implémentation a d'autres moyens de créer une variable de niveau supérieur.
()
n'est pas une syntaxe de schéma valide et bien sûr define
doit être au début d'un corps lambda. Le dernier code est donc en violation des rapports Scheme et est un comportement indéfini et non portable comme la mutation des listes citées dans Scheme et CL. Cela pourrait fonctionner, cela pourrait ne pas fonctionner, cela pourrait signaler une erreur.
@Sylwester ne l'est pas, et c'est le cas. :) Cela ne fonctionne pas, et cela signale une erreur - dans Racket, à la fois sous #lang r5rs
et #lang racket
que j'ai vérifié.
Je pense qu'aucune solution qui encapsule une liaison autour de define
ne peut fonctionner du tout de manière portative ou sûre, car les define
enveloppés construiront des liaisons locales (formes principales dans le corps) ou être illégale (formes non principales dans le corps), même si je serais heureux qu'une personne aux normes de Scheme indique où je me trompe.
Au lieu de cela, quelque chose comme ce hack légèrement méchant devrait fonctionner Je pense.
> (define-list (inc dec) (let ([i 0]) (list (lambda () (set! i (+ i 1)) i) (lambda () (set! i (- i 1)) i)))) > inc #<procedure> > (inc) 1 > (dec) 0 > (dec) -1 >
Ici, je me suis appuyé sur une constante appelée undefined
qui signifie «pas encore défini correctement»: Racket fournit cela dans raquette / non défini
mais cela peut en général être n'importe quoi: vous pourriez juste, quelque part, dire
(require compatibility/defmacro racket/undefined) (define-macro (define-list names expr) `(begin ,@(let loop ([ntail names] [defs '()]) (if (null? ntail) (reverse defs) (loop (cdr ntail) (cons `(define ,(car ntail) undefined) defs)))) (let ([results ,expr]) (unless (= (length results) (length ',names)) (error "wrong number of values")) ,@(let loop ([ntail names] [i 0] [assignments '()]) (if (null? ntail) (reverse assignments) (loop (cdr ntail) (+ i 1) (cons `(set! ,(car ntail) (list-ref results ,i)) assignments)))))))
par exemple.
L'astuce consiste à définir les choses que vous voulez au plus haut niveau avec des valeurs d'espace réservé, puis à leur attribuer dans le let
.
Je suis sûr qu'une macro peut être définie qui se développe en quelque chose comme (c'est pourquoi j'ai le tout dans un begin
): je ne l'ai pas fait parce que c'est fi ddly et j'utilise Racket donc je ne peux pas facilement y écrire des macros Lisp à l'ancienne.
Notez que l'approche évidente dans un schéma moderne est d'utiliser define-values code>:
(require racket/undefined) (define-syntax define-list (syntax-rules () [(define-list () expr) (let ((results expr)) (unless (zero? (length results)) (error "need an empty list")) (void))] [(define-list (name ...) expr) (begin (define name undefined) ... (let ([results expr]) (unless (= (length results) (length '(name ...))) (error "wrong number of values")) (set! name (begin0 (car results) (set! results (cdr results)))) ...))]))
Fait ce que vous voulez. Comme mentionné dans une autre réponse, vous pouvez implémenter define-values
en tant que macro si vous n'avez que plusieurs valeurs. Mais si vous n'avez pas du tout plusieurs valeurs, vous pouvez utiliser quelque chose qui définit les choses en fonction d'une liste de résultats:
(define-list (x y) (let (...) (list ...)))
Ici sont deux variantes approximatives de cette macro: la première utilise les macros natives de Racket:
(define-values (x y) (let (...) (values ...)))
tandis que la seconde utilise des macros non hygiéniques dans Racket:
(define undefined 'undefined)
N'est-il pas étonnant que l'on ne puisse pas définir une variable globale depuis l'intérieur d'un LET selon le standard? Est-ce une fonctionnalité inhabituelle?
@RainerJoswig Eh bien, je déteste l'équivalent CL (let (...) (defun ...) ...)
, donc je suis partial. Si nous sommes autorisés à Racket, alors (define-values (...) (let (...) ... (values ...)))
fonctionne bien, et je pense que define-values
peut être implémenté comme une macro, probablement.
Dans R7RS, le dernier rapport Scheme, nous avons define-values
. Il peut être utilisé de cette manière:
(define-values (add1 add2 add3) (let ((x 10)) (values (lambda () (+ x 1)) (lambda () (+ x 2)) (lambda () (+ x 3)))))
Bien sûr, pour des blocs plus grands, on peut vouloir faire une définition locale et la référencer dans valeurs
à la place.
Dans le rapport R7RS, vous trouverez une règle de syntaxe pour define-values
qui fonctionnera pour R6RS et R5RS. Il utilise call-with-values
où les valeurs sont passées à list
puis définissent
à partir de cela. Je parie que cela fonctionne aussi à l'intérieur de lambdas
, car l'implémentation du schéma peut en fait transformer cela en un letrec
donc même s'il n'est pas très élégant, il fait le sale boulot.
C'est cool: j'essayais de déterminer si define-values
pouvait être écrit comme une macro, et il s'avère que c'est possible. Merci!
Est-ce que define-values
peut être utilisé dans let, ou est-ce la même chose qu'avec define, let et lambda unique? Si c'est la même chose, alors il est utilisé avec le cas où let est en dehors de la macro qui génère des fonctions.
@jcubic cela fonctionne de la même manière que define
. si vous faites (let () (définir le nom ...))
alors nom
devient local probablement transformé en un letrec
. Ainsi, vous laissez
doit entrer dans l'expression comme je l'ai démontré. define-values
est également utile lorsqu'il s'agit de procédures qui renvoient plusieurs valeurs, par exemple. (define-values (q r) (quotient-and-reste 15 7))
et ceci est un cas d'utilisation pour les liaisons locales.