2012-04-08 6 views
2

A mesure que j'apprends le schéma et la raquette, je me retrouve à répéter ce schéma encore et encore. Où j'ai une fonction récursive où certains paramètres de la fonction changent mais pas certains paramètres. Je construis une fonction externe qui prend tous les paramètres et à l'intérieur de celle-ci définit une fonction interne qui ne prend que les paramètres changeants et se répète sur cela.Macro pour simplifier la syntaxe de la fonction récursive

Comme exemple concret Heres un cas basé un peu sur un exercice de fonction dans « The Little Schemer »

;inserts an item to the right of an element in a list 
(define (insert-to-right new old lat)  
    (define (insert-to-right lat) 
    (cond 
     [(null? lat) lat] 
     [(eq? old (car lat)) (cons old (cons new (cdr lat)))] 
     [else (cons (car lat) (insert-to-right (cdr lat)))])) 
    (insert-to-right lat)) 

Est-il possible de construire une macro définir * et un opérateur (par exemple une barre verticale) telle que je taper:

(define* (insert-to-right new old | lat)  
    (cond 
     [(null? lat) lat] 
     [(eq? old (car lat)) (cons old (cons new (cdr lat)))] 
     [else (cons (car lat) (insert-to-right (cdr lat)))])) 

et ce serait alors se développer dans la première forme de tous les paramètres étant transmis à la fonction extérieure, mais seulement les paramètres après la barre verticale étant transmis à la boucle intérieure.

Répondre

3

Après avoir joué, je l'ai construit une macro qui fait ce que je veux.

(define-syntax-rule 
    (define* (function-name (outer-var ...) (inner-var ...)) expr ...) 
    (define (function-name outer-var ... inner-var ...) 
    (define (function-name inner-var ...)expr ...) 
    (function-name inner-var ...))) 


(define* (insert-to-right [new old] [lat])   
    (cond 
     [(null? lat) lat] 
     [(eq? old (car lat)) (cons old (cons new (cdr lat)))] 
     [else (cons (car lat) (insert-to-right (cdr lat)))])) 

> (insert-to-right 11 3 '(1 2 3 4 5 6)) 
'(1 2 3 11 4 5 6) 

Dans la define de * ne pas utiliser un séparateur entre les paramètres internes et externes (comme je l'origine essayé de le faire), mais met les paramètres internes et externes dans la définition * déclaration dans des listes séparées que je pense est plus idiomatique Scheme/Racket.

8

Vous pouvez écrire un macro, mais vous pouvez aussi utiliser leur nom respectif:

(define (insert-to-right new old lat) 
    (let loop ([lat lat]) 
    (cond 
     [(null? lat)   lat] 
     [(eq? old (car lat)) (cons old (cons new (cdr lat)))] 
     [else    (cons (car lat) (loop (cdr lat)))]))) 
+0

Merci Matthias. Je me demande comment je peux le faire sans une fonction interne ou un let nommé. –

0

Vous ne devez pas utiliser de macros pour cela. Ceci est un cas d'école pour les fonctions d'ordre supérieur; en particulier, je crois que votre exemple peut être écrit avec pair-fold-right from SRFI-1. Code non testé (j'espère que c'est correct):

(define (insert-to-right new old lat) 
    (pair-fold-right (lambda (pair rest) 
        (if (eq? (car pair) old) 
         (cons (car pair) 
           (cons new rest)) 
         pair)) 
        '() 
        lat)) 

;;; Example implementation of pair-fold-right, just for one list—your Scheme system 
;;; probably has this as a library function somewhere 
(define (pair-fold-right fn init list) 
    (if (null? list) 
     init 
     (fn list (pair-fold-right fn init (cdr list))))) 
+0

Merci Sacundim, mais j'utilisais seulement insert-to-right comme exemple. La question était de simplifier la syntaxe du cas général où je suis récurrent sur une fonction où certains des paramètres sont invariants et d'autres ne le sont pas. –

+0

Eh bien, mon conseil est juste ne le fais pas. Les macros rendent le code plus difficile à lire et à résoudre, et il y a vraiment quelques petites choses qui les nécessitent absolument. Par exemple, (a) ajouter des formes d'expression qui n'évaluent pas toutes leurs sous-expressions, ou (b) ajouter de nouveaux types de formes de liaison (expressions qui lient de nouvelles variables). –

+0

Qu'en est-il toute forme avec des effets secondaires tels que: (définir-règle de syntaxe (incrément X) (X est réglé (ADD1 X))!) Ce qui vous permet de faire ce classique plus clair: (define (faire -counter) (définir le compteur 0) (compteur lambda() (incrément! compteur))) (définir counter1 (make-counter)) –

Questions connexes