Julia Language
Metaprogramming
खोज…
वाक्य - विन्यास
- स्थूल नाम (पूर्व) ... अंत
- बोली ... अंत
- : (...)
- $ x
- Meta.quot (एक्स)
- QuoteNode (एक्स)
- esc (एक्स)
टिप्पणियों
जूलिया के मेटाप्रोग्रामिंग फीचर्स लिस्प जैसी भाषाओं से काफी प्रेरित हैं, और कुछ लिस्प बैकग्राउंड वाले लोगों से परिचित होंगे। Metaprogramming बहुत शक्तिशाली है। जब सही तरीके से उपयोग किया जाता है, तो यह अधिक संक्षिप्त और पठनीय कोड हो सकता है।
quote ... end
quasiquote सिंटैक्स है। मूल्यांकन किए जाने के भीतर अभिव्यक्तियों के बजाय, वे बस पार्स हैं। quote ... end
का मूल्य quote ... end
अभिव्यक्ति परिणामस्वरूप सार सिंटैक्स ट्री (एएसटी) है।
The :(...)
सिंटैक्स quote ... end
समान है quote ... end
सिंटैक्स quote ... end
है, लेकिन यह अधिक हल्का होता है। यह वाक्यविन्यास quote ... end
तुलना में अधिक संक्षिप्त है quote ... end
।
एक quasiquote अंदर, $
ऑपरेटर खास है और AST में अपने तर्क interpolates। तर्क को एक अभिव्यक्ति होने की उम्मीद है, जिसे सीधे एएसटी में उतारा गया है।
Meta.quot(x)
फ़ंक्शन इसके तर्क को उद्धृत करता है। यह अक्सर प्रक्षेप के लिए $
का उपयोग करने के साथ संयोजन में उपयोगी होता है, क्योंकि यह भाव और प्रतीकों को एएसटी में शाब्दिक रूप से उतारा जाता है।
@Show मैक्रो को लागू करना
जूलिया में, @show
मैक्रो अक्सर डिबगिंग उद्देश्यों के लिए उपयोगी है। यह मूल्यांकन किए जाने वाले अभिव्यक्ति और उसके परिणाम दोनों को प्रदर्शित करता है, अंत में परिणाम का मूल्य लौटाता है:
julia> @show 1 + 1
1 + 1 = 2
2
यह @show
का अपना संस्करण बनाने के लिए सीधा है:
julia> macro myshow(expression)
quote
value = $expression
println($(Meta.quot(expression)), " = ", value)
value
end
end
नए संस्करण का उपयोग करने के लिए, बस @myshow
मैक्रो का उपयोग करें:
julia> x = @myshow 1 + 1 1 + 1 = 2 2 julia> x 2
लूप तक
हम सब के लिए इस्तेमाल कर रहे हैं while
वाक्य रचना, कि अपने शरीर को निष्पादित करता है, जबकि हालत के लिए मूल्यांकन किया जाता है true
। क्या होगा यदि हम एक लूप until
लागू करना चाहते हैं, जो कि एक लूप को निष्पादित करता है जब तक कि स्थिति का true
मूल्यांकन नहीं किया जाता true
?
जूलिया में, हम एक @until
मैक्रो बनाकर कर सकते हैं, जो स्थिति पूरी होने पर अपने शरीर को निष्पादित करने के लिए बंद हो जाता है:
macro until(condition, expression) quote while !($condition) $expression end end |> esc end
यहां हमने फ़ंक्शन चाइनिंग सिंटैक्स |>
, जो कि पूरे quote
ब्लॉक पर esc
फ़ंक्शन को कॉल करने के बराबर है। esc
फ़ंक्शन मैक्रो स्वच्छता को मैक्रो की सामग्री पर लागू करने से रोकता है; इसके बिना, बाहरी चर के साथ टकराव को रोकने के लिए मैक्रो में स्कैन किए गए चर का नाम बदला जाएगा। अधिक विवरण के लिए मैक्रो हाइजीन पर जूलिया प्रलेखन देखें।
आप इस पाश में एक से अधिक अभिव्यक्ति का उपयोग कर सकते हैं, बस एक begin ... end
अंदर सब कुछ डालकर begin ... end
ब्लॉक:
julia> i = 0; julia> @until i == 10 begin println(i) i += 1 end 0 1 2 3 4 5 6 7 8 9 julia> i 10
QuoteNode, Meta.quot, और Expr (: उद्धरण)
एक जूलिया फ़ंक्शन का उपयोग करके कुछ उद्धरण करने के तीन तरीके हैं:
julia> QuoteNode(:x)
:(:x)
julia> Meta.quot(:x)
:(:x)
julia> Expr(:quote, :x)
:(:x)
"उद्धरण" का क्या अर्थ है, और यह किसके लिए अच्छा है? उद्धरण हमें जूलिया द्वारा विशेष रूपों के रूप में व्याख्या किए जाने से अभिव्यक्ति की रक्षा करने की अनुमति देता है। एक सामान्य उपयोग मामला तब होता है जब हम ऐसे भाव उत्पन्न करते हैं जिनमें प्रतीकों का मूल्यांकन करने वाली चीजें होनी चाहिए। (उदाहरण के लिए, इस मैक्रो को एक ऐसे प्रतीक को वापस करने की ज़रूरत है जो प्रतीक का मूल्यांकन करता है।) यह केवल प्रतीक को वापस करने के लिए काम नहीं करता है:
julia> macro mysym(); :x; end
@mysym (macro with 1 method)
julia> @mysym
ERROR: UndefVarError: x not defined
julia> macroexpand(:(@mysym))
:x
यहाँ क्या चल रहा है? @mysym
विस्तार होता है :x
, जिसे एक्सप्रेशन के रूप में वेरिएबल x
रूप में समझा जाता है। लेकिन अभी तक कुछ भी x
नहीं सौंपा गया है, इसलिए हमें एक x not defined
त्रुटि मिलती है।
इसके आसपास जाने के लिए, हमें अपने मैक्रो के परिणाम को उद्धृत करना होगा:
julia> macro mysym2(); Meta.quot(:x); end
@mysym2 (macro with 1 method)
julia> @mysym2
:x
julia> macroexpand(:(@mysym2))
:(:x)
यहां, हमने अपने प्रतीक को एक उद्धृत प्रतीक में बदलने के लिए Meta.quot
फ़ंक्शन का उपयोग किया है, जो कि वह परिणाम है जो हम चाहते हैं।
Meta.quot
और QuoteNode
बीच क्या अंतर है, और मुझे QuoteNode
उपयोग करना चाहिए? लगभग सभी मामलों में, अंतर वास्तव में मायने नहीं रखता है। QuoteNode
बजाय QuoteNode
का उपयोग करना कभी-कभी थोड़ा सुरक्षित Meta.quot
। हालांकि, जूलिया एक्सप्रेशन और मैक्रोज़ कैसे काम करते हैं, इस अंतर की खोज करना जानकारीपूर्ण है।
Meta.quot
और QuoteNode
बीच का अंतर, समझाया गया
यहाँ एक नियम है:
- यदि आपको प्रक्षेप का समर्थन करने की आवश्यकता है या करना चाहते हैं, तो
Meta.quot
उपयोगMeta.quot
; - यदि आप प्रक्षेप को अनुमति नहीं दे सकते हैं या नहीं चाहते हैं, तो
QuoteNode
उपयोगQuoteNode
।
संक्षेप में, अंतर यह है कि Meta.quot
उद्धृत चीज़ के भीतर प्रक्षेप की अनुमति देता है, जबकि QuoteNode
अपने तर्क को किसी भी प्रक्षेप से बचाता है। प्रक्षेप को समझने के लिए, $
अभिव्यक्ति का उल्लेख करना महत्वपूर्ण है। जूलिया में एक तरह की अभिव्यक्ति है जिसे $
अभिव्यक्ति कहा जाता है। ये भाव बचने की अनुमति देते हैं। उदाहरण के लिए, निम्नलिखित अभिव्यक्ति पर विचार करें:
julia> ex = :( x = 1; :($x + $x) )
quote
x = 1
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
जब मूल्यांकन किया जाता है, तो यह अभिव्यक्ति 1
का मूल्यांकन करेगा और इसे x
को असाइन करेगा, फिर फॉर्म की अभिव्यक्ति का निर्माण करेगा _ + _
जहां x
के मूल्य से _
को बदल दिया जाएगा। इस प्रकार, इसका परिणाम अभिव्यक्ति 1 + 1
होना चाहिए (जिसका अभी तक मूल्यांकन नहीं किया गया है, और मूल्य 2
से इतना अलग है)। वास्तव में, यह मामला है:
julia> eval(ex)
:(1 + 1)
आइए अब कहते हैं कि हम इस प्रकार के भाव बनाने के लिए एक मैक्रो लिख रहे हैं। हमारा मैक्रो एक तर्क लेगा, जो उपरोक्त ex
में 1
को बदल देगा। यह तर्क निश्चित रूप से कोई भी अभिव्यक्ति हो सकती है। यहाँ कुछ ऐसा है जो हम नहीं चाहते हैं:
julia> macro makeex(arg)
quote
:( x = $(esc($arg)); :($x + $x) )
end
end
@makeex (macro with 1 method)
julia> @makeex 1
quote
x = $(Expr(:escape, 1))
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
julia> @makeex 1 + 1
quote
x = $(Expr(:escape, 2))
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
दूसरा मामला गलत है, क्योंकि हमें 1 + 1
मूल्यांकन नहीं करना चाहिए। हम Meta.quot
साथ तर्क को उद्धृत करके इसे ठीक करते हैं:
julia> macro makeex2(arg)
quote
:( x = $$(Meta.quot(arg)); :($x + $x) )
end
end
@makeex2 (macro with 1 method)
julia> @makeex2 1 + 1
quote
x = 1 + 1
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
मैक्रो स्वच्छता किसी उद्धरण की सामग्री पर लागू नहीं होती है, इसलिए इस मामले में (और वास्तव में कानूनी नहीं) इस मामले में बचना आवश्यक नहीं है।
जैसा कि पहले उल्लेख किया गया है, Meta.quot
प्रक्षेप करने की अनुमति देता है। तो चलिए कोशिश करते हैं कि:
julia> @makeex2 1 + $(sin(1))
quote
x = 1 + 0.8414709848078965
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
julia> let q = 0.5
@makeex2 1 + $q
end
quote
x = 1 + 0.5
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
पहले उदाहरण से, हम देखते हैं कि प्रक्षेप हमें sin(1)
को रेखांकित करने की अनुमति देता है, इसके बजाय अभिव्यक्ति शाब्दिक sin(1)
। दूसरा उदाहरण दिखाता है कि यह प्रक्षेप मैक्रो इनवोकेशन दायरे में किया जाता है, न कि मैक्रो के स्वयं के दायरे में। ऐसा इसलिए है क्योंकि हमारे मैक्रो ने वास्तव में किसी भी कोड का मूल्यांकन नहीं किया है; यह सब कर रहा है कोड उत्पन्न कर रहा है। कोड का मूल्यांकन (जो अभिव्यक्ति में अपना रास्ता बनाता है) तब किया जाता है जब अभिव्यक्ति मैक्रो उत्पन्न करता है वास्तव में चलाया जाता है।
अगर हमने इसके बजाय QuoteNode
का उपयोग किया QuoteNode
तो क्या होता? जैसा कि आप अनुमान लगा सकते हैं, क्योंकि QuoteNode
को QuoteNode
होने से रोकता है, इसका मतलब है कि यह काम नहीं करेगा।
julia> macro makeex3(arg)
quote
:( x = $$(QuoteNode(arg)); :($x + $x) )
end
end
@makeex3 (macro with 1 method)
julia> @makeex3 1 + $(sin(1))
quote
x = 1 + $(Expr(:$, :(sin(1))))
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
julia> let q = 0.5
@makeex3 1 + $q
end
quote
x = 1 + $(Expr(:$, :q))
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
julia> eval(@makeex3 $(sin(1)))
ERROR: unsupported or misplaced expression $
in eval(::Module, ::Any) at ./boot.jl:234
in eval(::Any) at ./boot.jl:233
इस उदाहरण में, हम सहमत हो सकते हैं कि Meta.quot
अधिक लचीलापन देता है, क्योंकि यह प्रक्षेप की अनुमति देता है। तो हम कभी भी QuoteNode
का उपयोग करने पर विचार क्यों कर सकते हैं? कुछ मामलों में, हम वास्तव में प्रक्षेप की इच्छा नहीं कर सकते हैं, और वास्तव में शाब्दिक $
अभिव्यक्ति चाहते हैं। यह वांछनीय कब होगा? चलिए @makeex
एक सामान्यीकरण पर विचार @makeex
जहाँ हम अतिरिक्त तर्क पास कर सकते हैं जो यह निर्धारित करते हैं कि +
चिह्न के बाएँ और दाएँ पर क्या आता है:
julia> macro makeex4(expr, left, right)
quote
quote
$$(Meta.quot(expr))
:($$$(Meta.quot(left)) + $$$(Meta.quot(right)))
end
end
end
@makeex4 (macro with 1 method)
julia> @makeex4 x=1 x x
quote # REPL[110], line 4:
x = 1 # REPL[110], line 5:
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
julia> eval(ans)
:(1 + 1)
@makeex4
के हमारे कार्यान्वयन की एक सीमा यह है कि हम अभिव्यक्ति के बाएँ और दाएँ पक्ष के रूप में सीधे अभिव्यक्ति का उपयोग नहीं कर सकते, क्योंकि वे प्रक्षेपित होते हैं। दूसरे शब्दों में, अभिव्यक्तियों को प्रक्षेप के लिए मूल्यांकन किया जा सकता है, लेकिन हम उन्हें संरक्षित रख सकते हैं। (चूँकि यहाँ उद्धृत और मूल्यांकन के कई स्तर हैं, तो हम स्पष्ट करें: हमारा मैक्रो कोड उत्पन्न करता है जो एक अभिव्यक्ति का निर्माण करता है जब मूल्यांकन किया जाता है कि एक और अभिव्यक्ति का उत्पादन होता है!
julia> @makeex4 x=1 x/2 x
quote # REPL[110], line 4:
x = 1 # REPL[110], line 5:
$(Expr(:quote, :($(Expr(:$, :(x / 2))) + $(Expr(:$, :x)))))
end
julia> eval(ans)
:(0.5 + 1)
हमें उपयोगकर्ता को यह निर्दिष्ट करने की अनुमति देना चाहिए कि प्रक्षेप कब होना है, और कब नहीं होना चाहिए। सैद्धांतिक रूप से, यह एक आसान समाधान है: हम अपने आवेदन में केवल $
संकेतों में से एक को हटा सकते हैं, और उपयोगकर्ता को अपना योगदान दे सकते हैं। इसका मतलब यह है कि हम उपयोगकर्ता द्वारा दर्ज किए गए अभिव्यक्ति के एक उद्धृत संस्करण को प्रक्षेपित करते हैं (जो हमने पहले ही उद्धृत किया है और एक बार प्रक्षेपित किया है)। यह निम्न कोड की ओर जाता है, जो कि पहले से ही उद्धृत और unquoting के कई नेस्टेड स्तरों के कारण थोड़ा भ्रमित हो सकता है। प्रत्येक भागने के लिए क्या है, इसे पढ़ने और समझने की कोशिश करें।
julia> macro makeex5(expr, left, right)
quote
quote
$$(Meta.quot(expr))
:($$(Meta.quot($(Meta.quot(left)))) + $$(Meta.quot($(Meta.quot(right)))))
end
end
end
@makeex5 (macro with 1 method)
julia> @makeex5 x=1 1/2 1/4
quote # REPL[121], line 4:
x = 1 # REPL[121], line 5:
$(Expr(:quote, :($(Expr(:$, :($(Expr(:quote, :(1 / 2)))))) + $(Expr(:$, :($(Expr(:quote, :(1 / 4)))))))))
end
julia> eval(ans)
:(1 / 2 + 1 / 4)
julia> @makeex5 y=1 $y $y
ERROR: UndefVarError: y not defined
चीजें अच्छी शुरू हुईं, लेकिन कुछ गलत हो गया है। मैक्रो का जेनरेट किया गया कोड मैक्रो इनवोकेशन स्कोप में y
की कॉपी को इंटरपोल करने की कोशिश कर रहा है; लेकिन मैक्रो इनवोकेशन स्कोप में y
की कोई कॉपी नहीं है। हमारी त्रुटि मैक्रो में दूसरे और तीसरे तर्क के साथ प्रक्षेप की अनुमति दे रही है। इस त्रुटि को ठीक करने के लिए, हमें QuoteNode
उपयोग करना चाहिए।
julia> macro makeex6(expr, left, right)
quote
quote
$$(Meta.quot(expr))
:($$(Meta.quot($(QuoteNode(left)))) + $$(Meta.quot($(QuoteNode(right)))))
end
end
end
@makeex6 (macro with 1 method)
julia> @makeex6 y=1 1/2 1/4
quote # REPL[129], line 4:
y = 1 # REPL[129], line 5:
$(Expr(:quote, :($(Expr(:$, :($(Expr(:quote, :(1 / 2)))))) + $(Expr(:$, :($(Expr(:quote, :(1 / 4)))))))))
end
julia> eval(ans)
:(1 / 2 + 1 / 4)
julia> @makeex6 y=1 $y $y
quote # REPL[129], line 4:
y = 1 # REPL[129], line 5:
$(Expr(:quote, :($(Expr(:$, :($(Expr(:quote, :($(Expr(:$, :y)))))))) + $(Expr(:$, :($(Expr(:quote, :($(Expr(:$, :y)))))))))))
end
julia> eval(ans)
:(1 + 1)
julia> @makeex6 y=1 1+$y $y
quote # REPL[129], line 4:
y = 1 # REPL[129], line 5:
$(Expr(:quote, :($(Expr(:$, :($(Expr(:quote, :(1 + $(Expr(:$, :y)))))))) + $(Expr(:$, :($(Expr(:quote, :($(Expr(:$, :y)))))))))))
end
julia> @makeex6 y=1 $y/2 $y
quote # REPL[129], line 4:
y = 1 # REPL[129], line 5:
$(Expr(:quote, :($(Expr(:$, :($(Expr(:quote, :($(Expr(:$, :y)) / 2)))))) + $(Expr(:$, :($(Expr(:quote, :($(Expr(:$, :y)))))))))))
end
julia> eval(ans)
:(1 / 2 + 1)
QuoteNode
का उपयोग QuoteNode
, हमने अपने तर्कों को प्रक्षेप से सुरक्षित किया है। चूंकि QuoteNode
केवल अतिरिक्त सुरक्षा का प्रभाव होता है, इसलिए जब तक आप प्रक्षेप की इच्छा नहीं करते, तब तक QuoteNode
का उपयोग करना कभी भी हानिकारक नहीं होता है। हालांकि, अंतर को समझने से यह समझना संभव हो जाता है कि Meta.quot
कहां और क्यों एक बेहतर विकल्प हो सकता है।
यह लंबी कवायद एक उदाहरण के साथ है जो स्पष्ट रूप से किसी भी उचित अनुप्रयोग में दिखाने के लिए बहुत जटिल है। इसलिए, हमने पहले उल्लिखित अंगूठे के निम्नलिखित नियम को सही ठहराया है:
- यदि आपको प्रक्षेप का समर्थन करने की आवश्यकता है या करना चाहते हैं, तो
Meta.quot
उपयोगMeta.quot
; - यदि आप प्रक्षेप को अनुमति नहीं दे सकते हैं या नहीं चाहते हैं, तो
QuoteNode
उपयोगQuoteNode
।
Expr (: बोली) के बारे में क्या?
Expr(:quote, x)
Meta.quot(x)
Expr(:quote, x)
Meta.quot(x)
बराबर है। हालांकि, उत्तरार्द्ध अधिक मुहावरेदार है और पसंद किया जाता है। कोड है कि भारी metaprogramming का उपयोग करता है के लिए, एक using Base.Meta
लाइन अक्सर प्रयोग किया जाता है, जो की अनुमति देता है Meta.quot
बस के रूप में भेजा जा करने के लिए quot
।
मार्गदर्शक
& मेटाप्रोग्रामिंग बिट्स और बोब्स
लक्ष्य:
उपयुक्त लक्षित कार्यात्मक / उपयोगी / गैर-अमूर्त उदाहरणों (उदाहरण
@swap
या@assert
) के@swap
से@assert
जो उपयुक्त संदर्भों में अवधारणाओं को प्रस्तुत करते हैं।कोड को व्याख्या के पैराग्राफ के बजाय अवधारणाओं को दर्शाने / प्रदर्शित करने को प्राथमिकता दें
'आवश्यक पढ़ने' को अन्य पृष्ठों से जोड़ने से बचें - यह कथा को बाधित करता है
चीजों को एक समझदार क्रम में प्रस्तुत करें जो सीखने को आसान बना देगा
संसाधन:
julialang.org
विकीबूक (@Cormullion)
5 परतें (लिआह हैंसन)
SO-Doc उद्धरण (@TotalVerb)
SO-Doc - वे प्रतीक जो कानूनी पहचानकर्ता नहीं हैं (@TotalVerb)
SO: जूलिया में एक प्रतीक क्या है (@StefanKarpinski)
प्रवचन धागा (@ PI-) Metaprogramming
अधिकांश सामग्री प्रवचन चैनल से आई है, जिसमें से अधिकांश fcard से आई है ... कृपया मुझे ठेस पहुंचाएं अगर मैं भूल गया था।
प्रतीक
julia> mySymbol = Symbol("myName") # or 'identifier' :myName julia> myName = 42 42 julia> mySymbol |> eval # 'foo |> bar' puts output of 'foo' into 'bar', so 'bar(foo)' 42 julia> :( $mySymbol = 1 ) |> eval 1 julia> myName 1
कार्यों में झंडे पारित करना:
function dothing(flag) if flag == :thing_one println("did thing one") elseif flag == :thing_two println("did thing two") end end julia> dothing(:thing_one) did thing one julia> dothing(:thing_two) did thing two
एक हैशेक उदाहरण:
number_names = Dict{Symbol, Int}() number_names[:one] = 1 number_names[:two] = 2 number_names[:six] = 6
(उन्नत) (@fcard) :foo
aka :(foo)
एक प्रतीक देता है यदि foo
एक वैध पहचानकर्ता है, अन्यथा एक अभिव्यक्ति है।
# NOTE: Different use of ':' is: julia> :mySymbol = Symbol('hello world') #(You can create a symbol with any name with Symbol("<name>"), # which lets us create such gems as: julia> one_plus_one = Symbol("1 + 1") Symbol("1 + 1") julia> eval(one_plus_one) ERROR: UndefVarError: 1 + 1 not defined ... julia> valid_math = :($one_plus_one = 3) :(1 + 1 = 3) julia> one_plus_one_plus_two = :($one_plus_one + 2) :(1 + 1 + 2) julia> eval(quote $valid_math @show($one_plus_one_plus_two) end) 1 + 1 + 2 = 5 ...
मूल रूप से आप प्रतीकों को हल्के तारों के रूप में मान सकते हैं। ऐसा नहीं है कि वे क्या कर रहे हैं, लेकिन आप यह कर सकते हैं, तो क्यों नहीं। जूलिया का बेस खुद इसे करता है, print_with_color(:red, "abc")
एक लाल रंग का abc प्रिंट करता है।
Expr (एएसटी)
(लगभग) जूलिया में सब कुछ एक अभिव्यक्ति है, यानी Expr
का एक उदाहरण, जो एएसटी आयोजित करेगा।
# when you type ... julia> 1+1 2 # Julia is doing: eval(parse("1+1")) # i.e. First it parses the string "1+1" into an `Expr` object ... julia> ast = parse("1+1") :(1 + 1) # ... which it then evaluates: julia> eval(ast) 2 # An Expr instance holds an AST (Abstract Syntax Tree). Let's look at it: julia> dump(ast) Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol + 2: Int64 1 3: Int64 1 typ: Any # TRY: fieldnames(typeof(ast)) julia> :(a + b*c + 1) == parse("a + b*c + 1") == Expr(:call, :+, :a, Expr(:call, :*, :b, :c), 1) true
नेस्टिंग Expr
रों:
julia> dump( :(1+2/3) ) Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol + 2: Int64 1 3: Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol / 2: Int64 2 3: Int64 3 typ: Any typ: Any # Tidier rep'n using s-expr julia> Meta.show_sexpr( :(1+2/3) ) (:call, :+, 1, (:call, :/, 2, 3))
बहु Expr
s का उपयोग करके quote
julia> blk = quote x=10 x+1 end quote # REPL[121], line 2: x = 10 # REPL[121], line 3: x + 1 end julia> blk == :( begin x=10; x+1 end ) true # Note: contains debug info: julia> Meta.show_sexpr(blk) (:block, (:line, 2, Symbol("REPL[121]")), (:(=), :x, 10), (:line, 3, Symbol("REPL[121]")), (:call, :+, :x, 1) ) # ... unlike: julia> noDbg = :( x=10; x+1 ) quote x = 10 x + 1 end
... तो quote
कार्यात्मक रूप से समान है लेकिन अतिरिक्त डिबग जानकारी प्रदान करता है।
(*) टिप: उपयोग let
रखने के लिए x
ब्लॉक के भीतर
quote
एक quote
Expr(:quote, x)
का उपयोग उद्धरणों के भीतर उद्धरणों को दर्शाने के लिए किया जाता है।
Expr(:quote, :(x + y)) == :(:(x + y))
Expr(:quote, Expr(:$, :x)) == :(:($x))
QuoteNode(x)
Expr(:quote, x)
QuoteNode(x)
के समान है लेकिन यह प्रक्षेप को रोकता है।
eval(Expr(:quote, Expr(:$, 1))) == 1
eval(QuoteNode(Expr(:$, 1))) == Expr(:$, 1)
( जूलिया मेटाप्रोग्रामिंग में विभिन्न उद्धृत तंत्रों का खंडन करें
क्या $ और : (…) किसी तरह एक दूसरे के उलटफेर करते हैं?
:(foo)
अर्थ है "मूल्य को मत देखो, अभिव्यक्ति को देखो" $foo
अर्थ है "अभिव्यक्ति को उसके मूल्य में बदलें"
:($(foo)) == foo
। $(:(foo))
एक त्रुटि है। $(...)
एक ऑपरेशन नहीं है और अपने आप से कुछ भी नहीं करता है, यह एक "यह प्रक्षेप है!" साइन इन करें कि उद्धृत वाक्यविन्यास उपयोग करता है। यानी यह केवल एक उद्धरण के भीतर मौजूद है।
क्या $
foo
, eval(
foo
)
?
नहीं! $foo
का संकलन-समय मान eval(foo)
इसका मतलब है कि रनटाइम पर
वैश्विक स्कोप प्रक्षेपित होगा स्थानीय में eval
होगा
eval(:<expr>)
को वैसा ही लौटना चाहिए जैसा कि <expr>
(यह मानते हुए कि <expr>
वर्तमान वैश्विक स्थान में एक वैध अभिव्यक्ति है)
eval(:(1 + 2)) == 1 + 2
eval(:(let x=1; x + 1 end)) == let x=1; x + 1 end
macro
एस
तैयार? :)
# let's try to make this! julia> x = 5; @show x; x = 5
आइए बनाते हैं अपना खुद का @show
मैक्रो:
macro log(x) :( println( "Expression: ", $(string(x)), " has value: ", $x ) ) end u = 42 f = x -> x^2 @log(u) # Expression: u has value: 42 @log(42) # Expression: 42 has value: 42 @log(f(42)) # Expression: f(42) has value: 1764 @log(:u) # Expression: :u has value: u
एक Expr
को कम expand
लिए expand
5 लेयर्स (लिआह हैनसन) <- बताती है कि जूलिया कैसे एक स्ट्रिंग के रूप में सोर्स कोड लेती है, इसे एक Expr
टीटीआरटी (एएसटी) में बदल देती है, सभी मैक्रोज़ (अभी भी एएसटी) को बाहर निकालती है, कम करती है (एएसटी को कम करती है), फिर एलएलवीएम में परिवर्तित हो जाती है। (और आगे - फिलहाल हमें चिंता करने की जरूरत नहीं है कि इससे आगे क्या है!)
क्यू: code_lowered
कार्यों पर काम करता है। क्या Expr
को कम करना संभव है? A: हाँ!
# function -> lowered-AST julia> code_lowered(*,(String,String)) 1-element Array{LambdaInfo,1}: LambdaInfo template for *(s1::AbstractString, ss::AbstractString...) at strings/basic.jl:84 # Expr(i.e. AST) -> lowered-AST julia> expand(:(x ? y : z)) :(begin unless x goto 3 return y 3: return z end) julia> expand(:(y .= x.(i))) :((Base.broadcast!)(x,y,i)) # 'Execute' AST or lowered-AST julia> eval(ast)
यदि आप केवल मैक्रोज़ का विस्तार करना चाहते हैं, तो आप macroexpand
उपयोग कर सकते हैं:
# AST -> (still nonlowered-)AST but with macros expanded: julia> macroexpand(:(@show x)) quote (Base.println)("x = ",(Base.repr)(begin # show.jl, line 229: #28#value = x end)) #28#value end
... जो एक गैर-कम एएसटी देता है, लेकिन सभी मैक्रोज़ के विस्तार के साथ।
esc()
esc(x)
एक Expr देता है जो कहता है कि "इस पर स्वच्छता लागू न करें", यह Expr(:escape, x)
। स्वच्छता वह है जो एक मैक्रो को आत्म-निहित रखता है, और यदि आप उन्हें "लीक" करना चाहते हैं तो आप चीजों को esc
करते हैं। जैसे
उदाहरण: swap
मैक्रो को चित्रण esc()
macro swap(p, q) quote tmp = $(esc(p)) $(esc(p)) = $(esc(q)) $(esc(q)) = tmp end end x,y = 1,2 @swap(x,y) println(x,y) # 2 1
$
हमें ' quote
से बाहर निकलने' की अनुमति देता है। तो बस $p
और $q
क्यों नहीं? अर्थात
# FAIL! tmp = $p $p = $q $q = tmp
क्योंकि वह करने के लिए पहले विचार करेंगे macro
के लिए गुंजाइश p
, और यह एक स्थानीय मिलेगा p
यानी पैरामीटर p
(हाँ, यदि आप बाद में पहुँच p
बिना esc
आईएनजी, मैक्रो समझता है p
एक स्थानीय चर के रूप में पैरामीटर)।
तो $p = ...
केवल स्थानीय p
को असाइन करना है। कॉलिंग के संदर्भ में जो भी चर पारित किया गया था उसे प्रभावित नहीं कर रहा है।
ठीक है तो कैसे के बारे में:
# Almost! tmp = $p # <-- you might think we don't $(esc(p)) = $q # need to esc() the RHS $(esc(q)) = tmp
तो esc(p)
कॉलिंग संदर्भ में 'लीक' p
है। "वह चीज़ जो मैक्रो में पारित की गई थी जिसे हम p
रूप में प्राप्त करते हैं "
julia> macro swap(p, q) quote tmp = $p $(esc(p)) = $q $(esc(q)) = tmp end end @swap (macro with 1 method) julia> x, y = 1, 2 (1,2) julia> @swap(x, y); julia> @show(x, y); x = 2 y = 1 julia> macroexpand(:(@swap(x, y))) quote # REPL[34], line 3: #10#tmp = x # REPL[34], line 4: x = y # REPL[34], line 5: y = #10#tmp end
जैसा कि आप देख सकते हैं कि tmp
को हाइजीन ट्रीटमेंट #10#tmp
, जबकि x
और y
नहीं। जूलिया tmp
लिए एक विशिष्ट पहचानकर्ता बना रही है, ऐसा कुछ जिसे आप मैन्युअल रूप से gensym
साथ कर सकते हैं, अर्थात:
julia> gensym(:tmp) Symbol("##tmp#270")
लेकिन: एक गोत्र है:
julia> module Swap export @swap macro swap(p, q) quote tmp = $p $(esc(p)) = $q $(esc(q)) = tmp end end end Swap julia> using Swap julia> x,y = 1,2 (1,2) julia> @swap(x,y) ERROR: UndefVarError: x not defined
एक और बात जूलिया की मैक्रो हाइजीन है, अगर मैक्रो दूसरे मॉड्यूल से है, तो यह किसी भी वैरिएबल को बनाता है (जो कि मैक्रो की रिटर्निंग एक्सप्रेशन के अंदर नहीं दिया गया है, जैसे कि इस मामले में tmp
तरह) मौजूदा मॉड्यूल के ग्लोबल्स, इसलिए $p
Swap.$p
, इसी तरह $q
-> Swap.$q
।
सामान्य तौर पर, यदि आपको एक चर की आवश्यकता है जो कि मैक्रो के दायरे से बाहर है, तो आपको इसे esc(p)
करना चाहिए, इसलिए आपको esc(p)
और esc(q)
परवाह किए बिना अगर वे एक अभिव्यक्ति के एलएचएस या आरएचएस पर हैं, या यहां तक कि खुद से भी।
लोगों ने पहले से ही कुछ समय में gensym
sa का उल्लेख किया है और जल्द ही आप पूरी अभिव्यक्ति को दूर करने के अंधेरे पक्ष से gensym
कुछ gensym
s के साथ यहाँ और वहाँ peppered है, लेकिन ... यह समझने की कोशिश करें कि स्वच्छता कैसे काम करने से पहले काम करती है। इससे ज्यादा स्मार्ट! यह एक विशेष रूप से जटिल एल्गोरिथ्म नहीं है इसलिए इसे बहुत लंबा नहीं होना चाहिए, लेकिन इसे जल्दी मत करो! उस शक्ति का उपयोग तब तक न करें जब तक कि आप इसके सभी प्रभावों को नहीं समझ लेते ... (@fcard)
उदाहरण: मैक्रो until
(@ इस्माइल-वीसी)
"until loop" macro until(condition, block) quote while ! $condition $block end end |> esc end julia> i=1; @until( i==5, begin; print(i); i+=1; end ) 1234
(@fcard) |>
हालांकि विवादास्पद है। मुझे आश्चर्य है कि एक भीड़ अभी तक बहस करने नहीं आई है। (शायद हर कोई बस थक गया है)। अधिकांश होने की सिफारिश है यदि सभी मैक्रो सिर्फ एक फ़ंक्शन के लिए कॉल न हों, तो:
macro until(condition, block) esc(until(condition, block)) end function until(condition, block) quote while !$condition $block end end end
... एक सुरक्षित विकल्प है।
## @ fcard की सरल मैक्रो चुनौती
टास्क: ऑपरेंड्स को स्वैप करें, इसलिए swaps(1/2)
2.00
यानी 2/1
देता है
macro swaps(e) e.args[2:3] = e.args[3:-1:2] e end @swaps(1/2) 2.00
यहाँ @fcard से अधिक मैक्रो चुनौतियां
प्रक्षेप और स्थूल assert
http://docs.julialang.org/en/release-0.5/manual/metaprogramming/#building-an-advanced-macro
macro assert(ex) return :( $ex ? nothing : throw(AssertionError($(string(ex)))) ) end
क्यू: क्यों पिछले $
? उत्तर: यह जूलिया को उस string(ex)
को eval
लिए मजबूर करता है, क्योंकि निष्पादन इस मैक्रो के आह्वान से गुजरता है। यदि आप बस उस कोड को चलाते हैं तो यह किसी भी मूल्यांकन को बाध्य नहीं करेगा। लेकिन जैसे ही आप कर assert(foo)
जूलिया इस मैक्रो की जगह लागू करेगा अपनी 'एएसटी टोकन / Expr' के साथ जो कुछ भी यह देता है, और $
कार्रवाई में शुरू होगा।
ब्लॉक के लिए {} का उपयोग करने के लिए एक मजेदार हैक
(@fcard) मुझे नहीं लगता कि {}
को ब्लॉक के रूप में उपयोग किए जाने से कुछ भी तकनीकी रखना है, वास्तव में कोई इसे काम करने के लिए अवशिष्ट {}
सिंटैक्स पर भी सजा सकता है:
julia> macro c(block) @assert block.head == :cell1d esc(quote $(block.args...) end) end @c (macro with 1 method) julia> @c { print(1) print(2) 1+2 } 123
* (अगर {} सिंटैक्स का पुनर्भुगतान किया जाता है तो / अभी भी काम करने की संभावना नहीं है)
तो पहले जूलिया मैक्रो टोकन देखता है, इसलिए यह मिलान end
तक टोकन पढ़ेगा / पार्स करेगा, और क्या बनायेगा? के साथ एक Expr
.head=:macro
या कुछ और? क्या यह "a+1"
को एक स्ट्रिंग के रूप में संग्रहीत करता है या क्या इसे अलग करता है :+(:a, 1)
? कैसे देखें?
?
(@fcard) इस मामले में लेक्सिकल स्कोप के कारण, @M
स्कोप में अपरिभाषित है, इसलिए यह ग्लोबल वैरिएबल का उपयोग करता है ... मैं वास्तव में अपने गूंगे उदाहरण में फ़्लिपलिन की अभिव्यक्ति से बचना भूल गया था, लेकिन "केवल भीतर काम करता है" एक ही मॉड्यूल "इसका हिस्सा अभी भी लागू होता है।
julia> module M macro m() :(a+1) end end M julia> a = 1 1 julia> M.@m ERROR: UndefVarError: a not defined
इसका कारण यह है कि, यदि मैक्रो का उपयोग किसी अन्य मॉड्यूल में किया गया है, जो कि इसे परिभाषित किया गया था, के अलावा किसी भी चर को कोड-से-विस्तारित के भीतर परिभाषित नहीं किया गया है, मैक्रो के मॉड्यूल के ग्लोबल्स के रूप में माना जाता है।
julia> macroexpand(:(M.@m)) :(M.a + 1)
उन्नत
### @ इस्माइल-कुलपति
@eval begin "do-until loop" macro $(:do)(block, until::Symbol, condition) until ≠ :until && error("@do expected `until` got `$until`") quote let $block @until $condition begin $block end end end |> esc end end julia> i = 0 0 julia> @do begin @show i i += 1 end until i == 5 i = 0 i = 1 i = 2 i = 3 i = 4
स्कॉट का मैक्रो:
"""
Internal function to return captured line number information from AST
##Parameters
- a: Expression in the julia type Expr
##Return
- Line number in the file where the calling macro was invoked
"""
_lin(a::Expr) = a.args[2].args[1].args[1]
"""
Internal function to return captured file name information from AST
##Parameters
- a: Expression in the julia type Expr
##Return
- The name of the file where the macro was invoked
"""
_fil(a::Expr) = string(a.args[2].args[1].args[2])
"""
Internal function to determine if a symbol is a status code or variable
"""
function _is_status(sym::Symbol)
sym in (:OK, :WARNING, :ERROR) && return true
str = string(sym)
length(str) > 4 && (str[1:4] == "ERR_" || str[1:5] == "WARN_" || str[1:5] == "INFO_")
end
"""
Internal function to return captured error code from AST
##Parameters
- a: Expression in the julia type Expr
##Return
- Error code from the captured info in the AST from the calling macro
"""
_err(a::Expr) =
(sym = a.args[2].args[2] ; _is_status(sym) ? Expr(:., :Status, QuoteNode(sym)) : sym)
"""
Internal function to produce a call to the log function based on the macro arguments and the AST from the ()->ERRCODE anonymous function definition used to capture error code, file name and line number where the macro is used
##Parameters
- level: Loglevel which has to be logged with macro
- a: Expression in the julia type Expr
- msgs: Optional message
##Return
- Statuscode
"""
function _log(level, a, msgs)
if isempty(msgs)
:( log($level, $(esc(:Symbol))($(_fil(a))), $(_lin(a)), $(_err(a)) )
else
:( log($level, $(esc(:Symbol))($(_fil(a))), $(_lin(a)), $(_err(a)), message=$(esc(msgs[1]))) )
end
end
macro warn(a, msgs...) ; _log(Warning, a, msgs) ; end
कबाड़ / असंसाधित ...
एक मैक्रो देखें / डम्प करें
(@ pi-) मान लीजिए कि मैं सिर्फ macro m(); a+1; end
एक ताजा REPL में macro m(); a+1; end
। बिना a
परिभाषित के। मैं इसे 'कैसे' देख सकता हूं? जैसे, क्या किसी मैक्रो को 'डंप' करने का कोई तरीका है? वास्तव में इसे क्रियान्वित किए बिना
(@fcard) मैक्रोज़ के सभी कोड वास्तव में कार्यों में लगाए जाते हैं, इसलिए आप केवल उनके निचले या टाइप-इनफर्ड कोड को देख सकते हैं।
julia> macro m() a+1 end @m (macro with 1 method) julia> @code_typed @m LambdaInfo for @m() :(begin return Main.a + 1 end) julia> @code_lowered @m CodeInfo(:(begin nothing return Main.a + 1 end)) # ^ or: code_lowered(eval(Symbol("@m")))[1] # ouf!
मैक्रो फ़ंक्शन को प्राप्त करने के अन्य तरीके:
julia> macro getmacro(call) call.args[1] end @getmacro (macro with 1 method) julia> getmacro(name) = getfield(current_module(), name.args[1]) getmacro (generic function with 1 method) julia> @getmacro @m @m (macro with 1 method) julia> getmacro(:@m) @m (macro with 1 method)
julia> eval(Symbol("@M")) @M (macro with 1 method) julia> dump( eval(Symbol("@M")) ) @M (function of type #@M) julia> code_typed( eval(Symbol("@M")) ) 1-element Array{Any,1}: LambdaInfo for @M() julia> code_typed( eval(Symbol("@M")) )[1] LambdaInfo for @M() :(begin return $(Expr(:copyast, :($(QuoteNode(:(a + 1)))))) end::Expr) julia> @code_typed @M LambdaInfo for @M() :(begin return $(Expr(:copyast, :($(QuoteNode(:(a + 1)))))) end::Expr)
^ लगता है कि मैं इसके बजाय code_typed
उपयोग कर सकता हूं
eval(Symbol("@M"))
कैसे समझें?
(@fcard) वर्तमान में, प्रत्येक मैक्रो में इसके साथ एक फ़ंक्शन जुड़ा हुआ है। यदि आपके पास M
नामक एक मैक्रो है, तो मैक्रो के फ़ंक्शन को @M
कहा जाता है। आम तौर पर आप उदाहरण के लिए eval(:print)
साथ एक फ़ंक्शन का मान प्राप्त कर सकते हैं, लेकिन मैक्रो के फ़ंक्शन के साथ आपको Symbol("@M")
, बस :@M
एक Expr(:macrocall, Symbol("@M"))
और मूल्यांकन कि एक मैक्रो-विस्तार का कारण बनता है।
code_typed
डिस्प्ले code_typed
क्यों नहीं करता है?
(@ PI-)
julia> code_typed( x -> x^2 )[1] LambdaInfo for (::##5#6)(::Any) :(begin return x ^ 2 end)
^ यहाँ मैं एक देखता हूं ::Any
पैराम, लेकिन यह टोकन x
साथ जुड़ा हुआ नहीं लगता है।
julia> code_typed( print )[1] LambdaInfo for print(::IO, ::Char) :(begin (Base.write)(io,c) return Base.nothing end::Void)
^ यहां भी; वहाँ io
कनेक्ट करने के लिए कुछ भी नहीं है ::IO
तो निश्चित रूप से यह उस विशेष print
विधि के एएसटी प्रतिनिधित्व का एक पूरा डंप नहीं हो सकता है ...?
(@fcard) print(::IO, ::Char)
केवल आपको बताता है कि यह किस पद्धति का है, यह एएसटी का हिस्सा नहीं है। यह अब मास्टर में भी मौजूद नहीं है:
julia> code_typed(print)[1] CodeInfo(:(begin (Base.write)(io,c) return Base.nothing end))=>Void
(@ p-) मुझे समझ नहीं आ रहा है कि आपका क्या मतलब है। ऐसा लगता है कि उस विधि के शरीर के लिए एएसटी को डंप करना, नहीं? मैंने सोचा था कि code_typed
फ़ंक्शन के लिए एएसटी देता है। लेकिन यह पहला कदम याद आ रहा है, यानी परम के लिए टोकन स्थापित करना।
(@fcard) code_typed
का मतलब केवल शरीर की एएसटी दिखाना है, लेकिन अब यह LambdaInfo
(0.5) या CodeInfo
(0.6) के रूप में विधि का पूरा एएसटी देता है, लेकिन बहुत सारी जानकारी छोड़ दी जाती है जब जवाब के लिए मुद्रित। सभी विवरण प्राप्त करने के लिए आपको LambdaInfo
क्षेत्र का निरीक्षण करना होगा। dump
आपके उत्तर को भरता जा रहा है, इसलिए आप कोशिश कर सकते हैं:
macro method_info(call) quote method = @code_typed $(esc(call)) print_info_fields(method) end end function print_info_fields(method) for field in fieldnames(typeof(method)) if isdefined(method, field) && !(field in [Symbol(""), :code]) println(" $field = ", getfield(method, field)) end end display(method) end print_info_fields(x::Pair) = print_info_fields(x[1])
जो किसी विधि के AST के नामित क्षेत्रों के सभी मान देता है:
julia> @method_info print(STDOUT, 'a') rettype = Void sparam_syms = svec() sparam_vals = svec() specTypes = Tuple{Base.#print,Base.TTY,Char} slottypes = Any[Base.#print,Base.TTY,Char] ssavaluetypes = Any[] slotnames = Any[Symbol("#self#"),:io,:c] slotflags = UInt8[0x00,0x00,0x00] def = print(io::IO, c::Char) at char.jl:45 nargs = 3 isva = false inferred = true pure = false inlineable = true inInference = false inCompile = false jlcall_api = 0 fptr = Ptr{Void} @0x00007f7a7e96ce10 LambdaInfo for print(::Base.TTY, ::Char) :(begin $(Expr(:invoke, LambdaInfo for write(::Base.TTY, ::Char), :(Base.write), :(io), :(c))) return Base.nothing end::Void)
Lil ' def = print(io::IO, c::Char)
? तुम वहाँ जाओ! (यह भी slotnames = [..., :io, :c]
भाग) इसके अलावा, हाँ, आउटपुट में अंतर इसलिए है क्योंकि मैं मास्टर पर परिणाम दिखा रहा था।
???
(@ इस्माइल-वीसी) आप इस तरह से मतलब है? प्रतीक के साथ सामान्य प्रेषण
आप इसे इस तरह से कर सकते हैं:
julia> function dispatchtest{alg}(::Type{Val{alg}}) println("This is the generic dispatch. The algorithm is $alg") end dispatchtest (generic function with 1 method) julia> dispatchtest(alg::Symbol) = dispatchtest(Val{alg}) dispatchtest (generic function with 2 methods) julia> function dispatchtest(::Type{Val{:Euler}}) println("This is for the Euler algorithm!") end dispatchtest (generic function with 3 methods) julia> dispatchtest(:Foo) This is the generic dispatch. The algorithm is Foo julia> dispatchtest(:Euler)
यह Euler एल्गोरिथम के लिए है! मुझे आश्चर्य है कि जेनेरिक प्रतीक प्रेषण के बारे में @fcard क्या सोचता है! --- ^: परी:
मॉड्यूल गोथा
@def m begin a+2 end @m # replaces the macro at compile-time with the expression a+2
अधिक सटीक रूप से, केवल मॉड्यूल के टॉपवेल के भीतर काम करता है जिसमें मैक्रो को परिभाषित किया गया था।
julia> module M macro m1() a+1 end end M julia> macro m2() a+1 end @m2 (macro with 1 method) julia> a = 1 1 julia> M.@m1 ERROR: UndefVarError: a not defined julia> @m2 2 julia> let a = 20 @m2 end 2
esc
ऐसा होने से रोकता है, लेकिन हमेशा इसका उपयोग करने में चूक करना भाषा के डिजाइन के खिलाफ जाता है। इसके लिए एक अच्छा बचाव यह है कि मैक्रों के भीतर नामों का उपयोग करने और उन्हें शुरू करने से एक को बनाए रखा जाए, जिससे उन्हें एक मानव पाठक को ट्रैक करना मुश्किल हो जाता है।
अजगर 'तानाशाह' / JSON सिंटैक्स की तरह `डिक्ट 'शाब्दिक।
परिचय
जूलिया शब्दकोशों के लिए निम्नलिखित वाक्यविन्यास का उपयोग करता है:
Dict({k₁ => v₁, k₂ => v₂, …, kₙ₋₁ => vₙ₋₁, kₙ => vₙ)
जबकि पायथन और JSON ऐसा दिखता है:
{k₁: v₁, k₂: v₂, …, kₙ₋₁: vₙ₋₁, kₙ: vₙ}
उदाहरण के प्रयोजनों के लिए हम जूलिया में इस वाक्यविन्यास का उपयोग कर सकते हैं और इसमें नए शब्दार्थ जोड़ सकते हैं (जूलिया में Dict
सिंटैक्स एक मुहावरेदार तरीका है, जिसे अनुशंसित किया गया है)।
पहले देखते हैं कि यह किस प्रकार की अभिव्यक्ति है:
julia> parse("{1:2 , 3: 4}") |> Meta.show_sexpr
(:cell1d, (:(:), 1, 2), (:(:), 3, 4))
इसका मतलब है कि हमें इसे लेने की आवश्यकता है :cell1d
अभिव्यक्ति और इसे बदलने या एक नई अभिव्यक्ति लौटाने के लिए जो इस तरह दिखना चाहिए:
julia> parse("Dict(1 => 2 , 3 => 4)") |> Meta.show_sexpr
(:call, :Dict, (:(=>), 1, 2), (:(=>), 3, 4))
मैक्रो की परिभाषा
निम्नलिखित मैक्रो, जबकि सरल, ऐसी कोड पीढ़ी और परिवर्तन को प्रदर्शित करने की अनुमति देता है:
macro dict(expr)
# Check the expression has the correct form:
if expr.head ≠ :cell1d || any(sub_expr.head ≠ :(:) for sub_expr ∈ expr.args)
error("syntax: expected `{k₁: v₁, k₂: v₂, …, kₙ₋₁: vₙ₋₁, kₙ: vₙ}`")
end
# Create empty `:Dict` expression which will be returned:
block = Expr(:call, :Dict) # :(Dict())
# Append `(key => value)` pairs to the block:
for pair in expr.args
k, v = pair.args
push!(block.args, :($k => $v))
end # :(Dict(k₁ => v₁, k₂ => v₂, …, kₙ₋₁ => vₙ₋₁, kₙ => vₙ))
# Block is escaped so it can reach variables from it's calling scope:
return esc(block)
end
आइए परिणामी मैक्रो विस्तार की जाँच करें:
julia> :(@dict {"a": :b, 'c': 1, :d: 2.0}) |> macroexpand
:(Dict("a" => :b,'c' => 1,:d => 2.0))
प्रयोग
julia> @dict {"a": :b, 'c': 1, :d: 2.0}
Dict{Any,Any} with 3 entries:
"a" => :b
:d => 2.0
'c' => 1
julia> @dict {
"string": :b,
'c' : 1,
:symbol : π,
Function: print,
(1:10) : range(1, 10)
}
Dict{Any,Any} with 5 entries:
1:10 => 1:10
Function => print
"string" => :b
:symbol => π = 3.1415926535897...
'c' => 1
अंतिम उदाहरण इसके बराबर है:
Dict(
"string" => :b,
'c' => 1,
:symbol => π,
Function => print,
(1:10) => range(1, 10)
)
Misusage
julia> @dict {"one": 1, "two": 2, "three": 3, "four": 4, "five" => 5}
syntax: expected `{k₁: v₁, k₂: v₂, …, kₙ₋₁: vₙ₋₁, kₙ: vₙ}`
julia> @dict ["one": 1, "two": 2, "three": 3, "four": 4, "five" => 5]
syntax: expected `{k₁: v₁, k₂: v₂, …, kₙ₋₁: vₙ₋₁, kₙ: vₙ}`
ध्यान दें कि जूलिया के बृहदान्त्र के लिए अन्य उपयोग हैं :
जैसे कि आपको कोष्ठक के साथ श्रेणी शाब्दिक अभिव्यक्तियों को लपेटना होगा या उदाहरण के लिए range
फ़ंक्शन का उपयोग करना होगा।