2016-11-17 6 views
0

コードの可読性を向上させるために、いくつかの関数で使用されている長い式をマクロに置き換えたい。各関数はマクロで使用されている特定の引数と同じ名前ですが、関数をコンパイルするとき、マクロで使用される変数はエスケープされても定義されていません。最小限の例:Julia - UndefVarError関数で使用されていて、REPLで使用されていない

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

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

今では奇妙な取得します。

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

なぜ、この関数の定義は、単にftest(x) = xに評価されませんか? xをREPLからではなく呼び出し関数のスコープから使用するようにマクロに指示するにはどうすればよいですか?それが含まれているため、

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

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

この場合CHECK_STUFFマクロでなければならないない機能を:私は単に私が使用していますCライブラリのように、テキストのリテラルブロックを置換するマクロを使用しますgotoのラベルをfuncに貼り付けます。私の仕事はこれをジュリアに翻訳することです。

+0

マクロでは、 'x'はシンボル':x'である必要があります。あなたのマクロは、グローバル変数 'x'を参照します。これはREPLで定義した後にのみ存在します。 http://stackoverflow.com/q/37358528/6172490も参照してください。 – tim

+0

より具体的には、マクロで使用される変数は、マクロが定義されているモジュール内のグローバル変数を参照します。あなたの場合、それはメインモジュールで定義されており、したがってそこに定義されている 'x'を指します。あなたのマクロは 'x'のリテラル値だけからなる式を返します。エスケープしてもその場合は何も変わりません。 'macroexpand'の出力を見てください。これは、マクロを書くときに非常に役に立ちます。 – tim

答えて

1

マクロは式ではなく値を計算します。したがって、マクロは実行時ではなくコンパイル時に実行されるため、コンパイル時にはesc(x)になりますが、f(x)=1にはコンパイル時にxが1になるためコンパイル時にコンパイルされます。代わりに、あなたのマクロが式xを返すようにして、関数がf(x)=xにコンパイルされるようにします。つまり:

macro mtest() 
    quote 
     x 
    end 
end 

が動作します。 :xとすることができますが、この構文は本当にきれいです。

実際、私はマクロを "後で引用する"方法でプログラムする傾向があります。式を返さずに代わりに値を返すことなく、まずマクロを記述してREPLでテストできます。それがあなたがここでしたことです。それはそれを働かせる方が簡単です。しかし、実際に関数内で有用であるためには、式を返す必要があります。だから、最も簡単な方法は、私がちょうど示したことをすることです:あなたが持っていたものをとり、quote endの周りに入れてください。今、マクロの引数があった場合は、式を修正して値を正しく補間する必要があります。しかし、一般的なアルゴリズムが既にうまくいけば、この部分はそれほど難しくありません(と私は奇妙なものを補間する方法のためのブレッドクラムを残しています。

関連する問題