2016-08-21 1 views
4

En Common Lisp, si je veux deux fonctions à partager l'état, j'exécuterait un let sur lambda comme suit:let-over-lambda dans le régime?

(let ((state 1)) 
(defun inc-state() 
    (incf state)) 
(defun print-state() 
    (format t "~a~%" state)) 

Ces fonctions ne sont pas locales au let - ce sont des fonctions globales qui maintiennent un référence à un état partagé variable, qui elle-même n'est pas visible de l'extérieur. Par exemple, je pouvais faire ce qui suit ailleurs dans mon code:

(print-state)  => 1 
(inc-state)   => 2 
(print-state)  => 2 

Dans le schéma, cependant, une telle construction déclare fonctions locales, qui ne sont pas visibles de l'extérieur:

(let ((state 1)) 
(define (print-state) 
    (print state)) 

(print-state))  => 1 

(print-state)  => error, no such variable print-state 

La seule comme je peux penser pour réaliser ce genre de fonctionnalité (en dehors de l'utilisation de globals non exportés à l'intérieur d'un module), serait quelque chose comme ceci:

(define print-state #f) 
(define inc-state #f) 

(let ((state 1)) 
(set! print-state (lambda() (print state))) 
(set! inc-state (lambda() (inc! state)))) 

Existe-t-il un moyen dans Scheme d'écrire la forme let-over-lambda sans avoir recours à de telles solutions de contournement? Ou aurais-je besoin d'écrire une macro pour envelopper cette laideur? (Par ailleurs, je connais environ letrec, et ce n'est pas une solution à ce problème.)

Incidemment, j'utilise Chicken Scheme, mais ma question devrait être pertinente pour tous les schémas.

Répondre

3

Malheureusement haut niveau de liaison ne peut être plus haut niveau et define à l'intérieur d'une procédure est en réalité que du sucre syntaxique pour un letrec. Dans le schéma de poulet que vous avez un formulaire appelé define-values où vous pouvez faire ceci:

(define-values (print-state inc-state) 
    (let ((state 1)) 
    (values (lambda() (print state)) 
      (lambda() (inc! state))))) 

Notez que define-values ne fait pas partie d'une norme, même si il semble que sa forme commune d'avoir. Il serait facile de faire une macro qui utilise l'approche que vous avez utilisée pour l'implémenter. Pour une solution de rechange, vous pouvez renvoyer un répartiteur que vous appelez pour obtenir l'accès aux procédures:

(define dispatcher 
    (let ((state 1)) 
    (lambda (msg) 
     (case msg 
     ((print) (lambda() (print state))) 
     ((inc!) (lambda() (inc! state))))))) 

(define print-state (dispatcher 'print)) 
(define inc-state (dispatcher 'inc!)) 

En réalité, vous n'avez pas besoin de faire GLOBALS puisque vous pouvez simplement appeler directement le retour:

((dispatcher 'inc!)) 
((dispatcher 'inc!)) 
((dispatcher 'print)) ; ==> prints 3 
+0

Merci. 'define-values' est moins laid que ma solution. Je vais probablement écrire une macro pour l'envelopper quand même. –

+0

'define-values' est spécifié dans la section 5.3.3 de R7RS. –

3

Vous pourriez faire quelque chose comme ceci:

(define-values (inc-state print-state) 
    (let ((state 1)) 
    (values 
    (lambda() ; inc-state 
     (set! state (+ 1 state))) 
    (lambda() ; print-state 
     (display state) 
     (newline))))) 

> (print-state) 
1 
> (inc-state) 
> (print-state) 
2 
> state 
. . state: undefined; cannot reference an identifier before its definition 
> 

(sortie est de Racket, mais je l'ai testé dans le schéma de poulet ainsi)