Domanda

Molti esempi di macro sembrano riguardare il nascondere i lambda, ad es. con-open-file in CL. Sto cercando alcuni usi più esotici delle macro, in particolare nello schema PLT. Mi piacerebbe avere un'idea di quando considerare l'utilizzo di una macro anziché l'utilizzo delle funzioni.

È stato utile?

Soluzione

Uso solo le macro Scheme (define-syntax) per cose minuscole come una migliore sintassi lambda:

(define-syntax [: x]
  (syntax-case x ()
    ([src-: e es ...]
     (syntax-case (datum->syntax-object #'src-: '_) ()
       (_ #'(lambda (_) (e es ...)))))))

Che ti permette di scrivere

[: / _ 2]  ; <-- much better than (lambda (x) (/ x 2))

Dan Friedman ha un'implementazione strabiliante di OO usando le macro: http: // www.cs.indiana.edu/~dfried/ooo.pdf

Ma onestamente, tutte le utili macro che ho definito sono state rubate da Paul Graham's On Lisp e sono generalmente più facili da scrivere con defmacro (define-macro nello schema PLT). Ad esempio, aif è abbastanza brutto con syntax-rules.

(define-syntax (aif x)
  (syntax-case x ()
    [(src-aif test then else)
     (syntax-case (datum->syntax-object (syntax src-aif) '_) ()
       [_ (syntax (let ([_ test]) (if (and _ (not (null? _))) then else)))])]))

syntax-case è strano in quanto è facile da usare solo per macro molto semplici, dove sei felice dell'incapacità di acquisire variabili; e macro DSL molto complicati, dove sei felice dell'incapacità di catturare le variabili facilmente . Nel primo caso vuoi scrivere il codice senza pensarci, e nel secondo caso hai pensato abbastanza al DSL che sei disposto a scriverne una parte nel linguaggio <=> / <=> che non è Schema al fine di evitare bug mistificanti.


Ma non uso molto le macro in Scheme. Schema idiomatico è così funzionale che molte volte vuoi solo scrivere un programma funzionale e poi nascondere alcuni lambda. Sono salito sul treno funzionale e ora credo che se hai un linguaggio pigro o una buona sintassi per lambda, anche questo non è necessario, quindi le macro non sono poi così utili in uno stile puramente funzionale.

Quindi consiglierei Pratica Lisp comune e Su Lisp . Se vuoi usare lo schema PLT, penso che la maggior parte dei loro <=> macro funzionerà con <=>. O semplicemente usa Common Lisp.

Altri suggerimenti

Le macro sono necessarie per implementare nuove strutture di controllo e nuovi costrutti di associazione.

Cerca quindi questo tipo di costrutti in http://planet.plt-scheme.org . Su PLaneT sfogli entrambi la documentazione e il codice.

Esempi per nuove strutture di controllo:

http://planet.plt-scheme.org/package-source/soegaard/control.plt/2/0/planet-docs/manual/index.html

Per trovare esempi di nuovi moduli di rilegatura, cerca le macro che iniziano con " con - " ;. Un utile esempio si trova in math.plt anche da PLaneT.

  ; Within a (with-modulus n form1 ...) the return values of
  ; the arithmetival operations +, -, * and ^ are automatically
  ; reduced modulo n. Furthermore (mod x)=(modulo x n) and
  ; (inv x)=(inverse x n).

  ; Example: (with-modulus 3 (^ 2 4)) ==> 1

  (define-syntax (with-modulus stx)
    (syntax-case stx ()
      [(with-modulus e form ...)
       (with-syntax ([+   (datum->syntax-object (syntax with-modulus) '+)]
                     [-   (datum->syntax-object (syntax with-modulus) '-)]
                     [*   (datum->syntax-object (syntax with-modulus) '*)]
                     [^   (datum->syntax-object (syntax with-modulus) '^)]
                     [mod (datum->syntax-object (syntax with-modulus) 'mod)]
                     [inv (datum->syntax-object (syntax with-modulus) 'inv)])
         (syntax (let* ([n e]
                        [mod    (lambda (x)   (modulo x n))]
                        [inv    (lambda (x)   (inverse x n))]
                        [+      (compose mod +)]
                        [-      (compose mod -)]
                        [*      (compose mod *)]
                        [square (lambda (x) (* x x))]
                        [^      (rec ^ (lambda (a b)
                                         (cond
                                           [(= b 0)   1]
                                           [(even? b) (square (^ a (/ b 2)))]
                                           [else      (* a (^ a (sub1 b)))])))])
                   form ...)))]))

Inizierò a rispondere all'ultima domanda. Quando utilizzare una macro anziché una funzione. Le macro fanno cose che le funzioni non possono fare e le funzioni fanno cose che le macro non possono fare, quindi sarà difficile mescolarle, ma andiamo più in profondità.

Le funzioni vengono utilizzate quando si desidera valutare gli argomenti e le macro quando si desidera non valutare gli argomenti. Non è molto utile, vero? Usi le macro quando vuoi scrivere qualcosa in un modo diverso, quando vedi uno schema e vuoi astrarre. Ad esempio: definisco tre funzioni chiamate foo-create, foo-process e foo-destro per diversi valori di foo e con corpi simili in cui l'unico cambiamento è foo. C'è un modello ma un livello troppo alto per una funzione, quindi crei una macro.

Nella mia modesta esperienza, le macro in Scheme devono essere usate tanto quanto in altre Lisps, come Common Lisp o Clojure . Suppongo sia la prova che forse le macro igieniche non sono una buona idea, e qui non sarei d'accordo con Paul Graham sul perché. Non è perché a volte vuoi essere sporco (non igienico) ma perché le macro igieniche finiscono per essere complesse o contorte.

Il pratico Lisp comune, di Peter Seibel, ha una buona introduzione alle macro. Su Lisp, di Paul Graham, potrebbe essere una buona fonte di esempi più complicati. Inoltre, dai un'occhiata alle macro integrate in, diciamo, Common Lisp.

Gli Automata via Macro paper presenta una perla di programmazione funzionale sull'implementazione di macchine a stati finiti tramite macro in Scheme.

Il libro The Reasoned Schemer termina con un implementazione completa basata su macro di miniKanren, il linguaggio di programmazione logica utilizzato nel libro. Questo documento presenta miniKanren e la sua implementazione in modo più formale e conciso rispetto al libro.

Un esempio di una macro più avanzata che non è una forma lambda sotto mentite spoglie è la macro di Common Lisp con-slot , che rende l'accesso agli slot degli oggetti simile al normale accesso variabile:

(with-slots (state door) car
  (when (eq state :stopped)
    (setq state :driving-around)
    (setq door :closed)))

Nota che questo non è lo stesso che associare i valori degli slot alle variabili locali e accedervi, poiché con slot ti consente di modificare gli slot tramite SETQ e vedere immediatamente le modifiche esterne.

Avevo una curry macro quando facevo molto schema sul palmo della mano. È stato molto utile.

Le macro dello schema consentono di aggiungere funzionalità che gli autori della lingua originale non hanno incluso se stessi; questa è l'intera filosofia dietro le macro.

Ecco un piccolo esempio: lo schema PLT fornisce un linguaggio per scrivere presentazioni chiamato Slideshow. Ho usato le macro per associare un numero di diapositiva a una diapositiva in modo da poterle gestire più facilmente.

I & # 8217; ho scritto una macro che fornisce la sintassi infix. Niente di troppo elegante; nessuna precedenza. Mentre & # 8217; m generalmente bene con la sintassi del prefisso, preferisco infix per & Lt; e > ;.

Li uso quando le procedure non sono sufficienti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top