2016-11-17 2 views
0

Ich möchte ein Makro erstellen, um einen langen Ausdruck zu ersetzen, der in mehreren Funktionen verwendet wird, um die Lesbarkeit von Code zu verbessern. Jede Funktion hat den gleichen Namen für ein bestimmtes Argument, das im Makro verwendet wird, aber beim Kompilieren der Funktion sagt Julia, dass die im Makro verwendete Variable nicht definiert ist, selbst wenn es maskiert ist. Minimal Beispiel:Julia - UndefVarError in Makro, wenn in Funktion verwendet, aber nicht in REPL

julia> macro mtest() 
    esc(x) 
end 
@mtest (macro with 1 method) 

julia> ftest(x) = @mtest 
ERROR: UnderVarError: x not defined 

Jetzt wird es seltsam:

julia> x = 1 
1 
julia> @mtest 
1 
julia> ftest(x) = @mtest 
ftest (generic function with 1 method) 
julia> ftest(2) 
1 

Warum nicht einfach diese Funktionsdefinition ftest(x) = x bewerten? Wie kann ich dem Makro mitteilen, das x aus dem Bereich der aufrufenden Funktion statt von der REPL zu verwenden? Ich mag ein Makro verwenden, um einfach einen wörtlichen Textblock zu ersetzen, wie in der C-Bibliothek Ich verwende:

#define CHECK_STUFF \ 
    big \ 
    complicated \ 
    expression \ 
    involving x; 

void func(x, y, z) { 
    //stuff 
    CHECK_STUFF 
    //more stuff 
} 

In diesem Fall CHECK_STUFFmuss Makro sein, nicht eine Funktion, denn sie enthält a goto zu einem Etikett in func. Meine Aufgabe ist es, dies zu Julia zu übersetzen.

+0

Im Makro muss 'x' das Symbol': x' sein. Ihr Makro bezieht sich auf die globale Variable 'x', die nur existiert, nachdem Sie sie in der REPL definiert haben. Siehe auch http://stackoverflow.com/q/37358528/6172490. – tim

+0

Um genauer zu sein, beziehen sich Variablen, die in einem Makro verwendet werden, auf die globalen Variablen in dem Modul, in dem das Makro definiert ist. In Ihrem Fall ist es im Hauptmodul definiert und bezieht sich somit auf dort definiertes "x". Ihr Makro gibt dann den Ausdruck zurück, der nur aus dem Literalwert von "x" besteht. Das Escapen ändert in diesem Fall nichts. Siehe die Ausgabe von 'macroexpand', was sehr hilfreich beim Schreiben von Makros ist. – tim

Antwort

1

Ihr Makro berechnet einen Wert, keinen Ausdruck. Da Makros zur Kompilierungszeit und nicht zur Laufzeit ausgeführt werden, wird sie zur Kompilierungszeit im Wesentlichen auf esc(x) gesetzt, aber da x zur Kompilierungszeit 1 ist, kompiliert Ihre Funktion zu f(x)=1. Was Sie stattdessen tun möchten, ist, dass Ihr Makro den Ausdruck x zurückgibt, so dass Ihre Funktion zu f(x)=x kompiliert. Das ist:

macro mtest() 
    quote 
     x 
    end 
end 

wird funktionieren. Während Sie dies einfach als :x haben können, skaliert diese Syntax wirklich schön.

In der Tat tendiere ich dazu, Makros in einem "Zitat später" Weg zu programmieren. Sie können zuerst Ihr Makro schreiben und es in der REPL testen, ohne dass es einen Ausdruck zurückgibt und stattdessen den Wert zurückgibt. Das hast du hier gemacht. Es ist einfacher, es dann zum Laufen zu bringen. Damit es jedoch in einer Funktion nützlich ist, muss es einen Ausdruck zurückgeben. Also ist der einfachste Weg, das zu tun, was ich gerade gezeigt habe: nimm, was du hattest und setze quote end darum herum. Nun, wenn Sie Argumente für Ihr Makro hatten, müssen Sie dann Ihren Ausdruck so ändern, dass Sie die Werte richtig interpolieren. Aber wenn der allgemeine Algorithmus bereits funktioniert hat, wird dieser Teil nicht so schwierig sein (und ich habe eine Spur von Breadcrumbs hinterlassen, um viele seltsame Dinge auf SO zu interpolieren, also suche einfach herum, wenn du seltsame Dinge interpolieren willst).

Verwandte Themen