Julia एक सामान्य उद्देश्य की भाषा है, जिसका इस्तेमाल ज़्यादातर प्रोग्रामिंग कामों के लिए किया जा सकता है। लेकिन व्यवहार में इसका मुख्य इस्तेमाल इंजीनियरिंग और विज्ञान में ही होता है। तेज़, बहुमुखी और परिष्कृत संख्यात्मक गणनाएँ इसकी बनावट के केंद्र में हैं।
एक पूर्णांक एक ऐसी "गोल" संख्या है जिसमें दशमलव बिंदु नहीं होता।
Basics कॉन्सेप्ट में हमने देखा था कि बिना टाइप बताए भी एक पूर्णांक वैल्यू को वेरिएबल में असाइन किया जा सकता है।
पढ़ने में आसानी के लिए अंकों को अलग करने के लिए अंडरस्कोर का इस्तेमाल किया जा सकता है। कंपाइलर इन्हें नज़रअंदाज़ कर देता है।
julia> x = 3
3
julia> typeof(x)
Int64
julia> large_number = 1_234_567_890
1234567890
अंदर से कंपाइलर आपके CPU के लिए जो साइन्ड पूर्णांक टाइप सबसे उपयुक्त हो, वही चुनता है।
आधुनिक कंप्यूटरों पर यह आमतौर पर Int64 होता है, जो अधिकांश कामों के लिए पूरी तरह पर्याप्त है।
Types पर आगे किसी कॉन्सेप्ट में और विस्तार से चर्चा की जाएगी। फिलहाल हम उम्मीद करते हैं कि संख्यात्मक टाइप इतने सहज होंगे कि यह दस्तावेज़ समझ में आ जाए।
जैसा कि हम आगे किसी कॉन्सेप्ट में देखेंगे, Julia बहुत बड़े, बहुआयामी ऐरे को सीधे सपोर्ट करती है। ऐसे ऐरे उपलब्ध मेमोरी और डेटा ट्रांसफर की बैंडविड्थ, दोनों पर भार डाल सकते हैं।
प्रोग्रामर को बारीक नियंत्रण देने के लिए, मनचाहा साइन्ड या अनसाइन्ड पूर्णांक टाइप बताना संभव भी है और काफी आम भी। जिन Julia सीखने वालों ने पहले Python इस्तेमाल किया है, वे इसकी तुलना साधारण Python के बजाय NumPy से कर सकते हैं।
julia> y = Int8(42)
42
julia> typeof(y)
Int8
julia> z = UInt32(1024)
0x00000400
ध्यान दीजिए कि Julia अनसाइन्ड पूर्णांकों को डिफ़ॉल्ट रूप से हेक्साडेसिमल फॉर्मैट में प्रदर्शित करती है, क्योंकि अनसाइन्ड टाइप अक्सर निचले स्तर के बिट ऑपरेशनों के लिए इस्तेमाल होते हैं।
पूर्णांकों को बाइनरी, ऑक्टल या हेक्साडेसिमल रूप में भी लिखा जा सकता है, क्रमशः 0b, 0o और 0x उपसर्गों के साथ।
julia> a = 0x10
0x10
julia> Int(a)
16
julia> b = 0b1010 # displays as hexadecimal
0x0a
BigInt
हर पूर्णांक टाइप की एक अधिकतम और न्यूनतम वैल्यू होती है जिसे वह संग्रहीत कर सकता है:
julia> typemax(Int8)
127
julia> typemin(Int8)
-128
इस वैध सीमा से बाहर जाने पर पूर्णांक ओवरफ़्लो होता है, और नतीजे बहुत अजीब लगते हैं।
julia> c = Int8(126)
126
julia> c * c
4
शायद हम सब मानेंगे कि 126 * 126 का मान 4 नहीं होना चाहिए!
इसकी विस्तृत व्याख्या के लिए यह जानना ज़रूरी है कि CPU के रजिस्टर पूर्णांकों को कैसे संग्रहीत करते हैं, जो इस कॉन्सेप्ट के दायरे से बाहर है।
इस समस्या को कम करने के लिए "चौड़े" टाइप का उपयोग किया जा सकता है, जो Int128 या UInt128 तक जाते हैं ("चौड़ा" का मतलब नीचे समझाया गया है)। लेकिन सभी की एक कठोर सीमा होती है, जिसके बाद ओवरफ़्लो की समस्या आ जाती है।
एक हल यह है कि BigInt टाइप का उपयोग किया जाए, जो केवल आपके कंप्यूटर की मेमोरी तक सीमित है।
यह मनमानी परिशुद्धता वाले अंकगणित का Julia में कार्यान्वयन है।
julia> 2 ^ 70
0
julia> big = BigInt(2) ^ 70
1180591620717411303424
julia> UInt128(big)
0x00000000000000400000000000000000
चूँकि 2 की घात 70 एक Int64 में नहीं समाएगी, ऊपर के उदाहरण की पहली गणना विफल हो जाती है: हालाँकि ध्यान दीजिए कि कोई एरर संदेश नहीं आता।
प्रोग्रामर, सावधान!
कास्टिंग से 2 को BigInt बनाने पर सही उत्तर मिलता है, और इसे हेक्साडेसिमल में प्रदर्शित करने से साफ़ हो जाता है कि यह 2 की घात है।
यह कोई आश्चर्य की बात नहीं कि दशमलव संख्याओं में दशमलव बिंदु हो भी सकता है और नहीं भी, और बिंदु के बाद भिन्नात्मक भाग भी हो सकता है।
julia> f = 3.45
3.45
julia> typeof(f)
Float64
बेशक, वैज्ञानिक संकेतन भी काम करता है।
julia> avogadro = 6.02e23
6.02e23
पूर्णांकों की तरह, डिफ़ॉल्ट टाइप ज़्यादातर कामों के लिए ठीक रहता है, लेकिन दूसरे साइन्ड टाइप भी उपलब्ध हैं। अनसाइन्ड दशमलव संख्या टाइप नहीं होते।
एक शॉर्टकट के तौर पर, f0 प्रत्यय लगाकर Float32 वैल्यू बनाई जा सकती है।
julia> f32 = 4.56f0
4.56f0
julia> typeof(f32)
Float32
बाकी आकार की दशमलव संख्याओं के लिए, पूर्णांकों की तरह, स्पष्ट कास्ट की ज़रूरत होती है।
अधिकतम और न्यूनतम वैल्यू शायद चौंका दें:
julia> typemax(Float64)
Inf
julia> typemin(Float64)
-Inf
अनंत एक वैध वैल्यू है!
लेकिन दशमलव संख्याओं की उपयोगी सीमा सीमित है: बहुत बड़ी कोई भी संख्या Inf वैल्यू पा लेती है, और बहुत छोटी कोई भी संख्या 0.0 तक गोल कर दी जाती है।
इन सीमाओं को देखने के लिए हम दूसरे दो फंक्शन इस्तेमाल कर सकते हैं: मोटे तौर पर ± 10 ^ 308।
julia> floatmax(Float64)
1.7976931348623157e308
julia> floatmin(Float64)
2.2250738585072014e-308
साथ ही, परिशुद्धता भी सीमित है: सार्थक अंकों की संख्या बड़ी है पर सीमित (करीब 15, Float64 के लिए)।
जैसा कि Basics कॉन्सेप्ट में बताया गया था, अंकगणितीय ऑपरेटर ज़्यादातर वैसे ही काम करते हैं जैसे बच्चों को पढ़ाया जाने वाला सामान्य अंकगणित।
ध्यान दीजिए कि घात लगाने के लिए ^ इस्तेमाल होता है, ** नहीं (दोनों दूसरी भाषाओं में आम हैं)।
2 + 3 # 5 (addition)
2 - 3 # -1 (subtraction)
2 * 3 # 6 (multiplication)
8 / 2 # 4.0 (division)
8 % 3 # 2 (remainder)
2 ^ 3 # 8 (exponentiation)
लेकिन कुछ ऐसी बातें हैं जो खास तौर पर Julia की हैं और उन पर चर्चा करना बनता है।
julia> x = 4.2
4.2
julia> 2 * x
8.4
julia> 2x
8.4
julia> 2.4x
10.08
यह चौंकाने वाला लग सकता है।
दूसरी ज़्यादातर कंप्यूटर भाषाओं की तरह, * को इन्फिक्स ऑपरेटर की तरह इस्तेमाल करना हमेशा संभव है।
लेकिन Julia को उन लोगों ने बनाया है जो मानते हैं कि कोड जितना संभव हो गणितीय समीकरणों जैसा दिखना चाहिए।
चूँकि वेरिएबल के नाम अक्षर से शुरू होने चाहिए, इसलिए नाम से पहले कोई संख्या (पूर्णांक या दशमलव) लगाने को अंतर्निहित गुणा माना जाता है।
उदाहरण के लिए, अगर हमें किसी गोले का पृष्ठ क्षेत्रफल चाहिए, तो 4 * pi * r * r के बजाय हम यह कर सकते हैं :
julia> surface(r) = 4π * r^2
surface (generic function with 1 method)
julia> surface(3)
113.09733552923255
हालाँकि π एक अंतर्निहित कॉन्स्टेंट है, यह एक (यूनानी) अक्षर भी है।
इसलिए पार्सर को π को r से अलग करने के लिए फिर भी एक स्पष्ट * की ज़रूरत पड़ती है।
इन्फिक्स ऑपरेटर की तरह / इस्तेमाल करने पर हमेशा दशमलव संख्या वाला नतीजा मिलता है, चाहे इनपुट पूर्णांक ही हों।
पूर्णांक भाग के लिए कुछ और विकल्प हैं:
julia> 10 / 3
3.3333333333333335
julia> div(10, 3)
3
julia> 10 ÷ 3
3
julia> 10 // 3
10//3
div() फंक्शन पूर्णांक भाग के लिए है, और इसका नतीजा शून्य की ओर काटा जाता है: धनात्मक संख्याओं में नीचे की ओर, ऋणात्मक संख्याओं में ऊपर की ओर।
इसका पर्याय भी इन्फिक्स ऑपरेटर ÷ है, जो फिर से चीज़ों को अधिक गणितीय दिखाने के उद्देश्य से है।
अगर आप Julia को समझने वाले एडिटर में लिख रहे हैं, तो इसे \div टाइप करके <Tab> कुंजी दबाइए।
// ऑपरेटर के लिए पाठ्यक्रम में आगे एक अलग कॉन्सेप्ट चाहिए होगा।
फिलहाल इतना कह देना काफी है कि // का नतीजा एक "परिमेय" संख्या होती है, जो उस चीज़ का औपचारिक नाम है जिसे ज़्यादातर लोग भिन्न कहते हैं।
अंश और हर से सामान्य गुणनखंड हटा दिए जाते हैं, जिससे दो पूर्णांकों का अनुपात उस रूप में मिलता है जिसे "न्यूनतम रूप" कहते हैं।
julia> rationalnum = 22 // 6
11//3
julia> typeof(rationalnum)
Rational{Int64}
अब हमारे पास परिमेय संख्याएँ हैं। तो "अपरिमेय" संख्याओं का क्या?
julia> π
π = 3.1415926535897...
julia> typeof(π)
Irrational{:π}
एक अपरिमेय संख्या वह होती है जिसे दो पूर्णांकों के अनुपात में नहीं लिखा जा सकता।
आम उदाहरण हैं π, e (ऑयलर की संख्या), और कई मूल जैसे √2 (2 का वर्गमूल)।
Julia गणित को ठीक से करने की कोशिश करती है।
यह अक्सर अपने आप हो जाता है:
julia> x = 2 + 3.5
5.5
julia> typeof(x)
Float64
हमने एक Int64 में एक Float64 जोड़ा, और नतीजा Float64 मिला।
वास्तव में, जोड़ करने से पहले पूर्णांक चुपचाप Float64 में बदल दिया गया।
Julia में संख्यात्मक टाइपों की "चौड़ाई" की अवधारणा है।
Int64, Int16 से चौड़ा है।अगर किसी एक्सप्रेशन में कई टाइप मिले-जुले इस्तेमाल होते हैं, तो ज़रूरत के हिसाब से हर एक को सबसे चौड़े इस्तेमाल किए गए टाइप में "प्रोमोट" कर दिया जाता है।
रूपांतरण ज़बरदस्ती कराने के लिए हम किसी पूर्णांक को किसी खास टाइप में कास्ट कर सकते हैं, जैसे Float64(5)।
या फिर सिर्फ float(5) इस्तेमाल कीजिए और कंपाइलर को उपयुक्त टाइप चुनने दीजिए।
दशमलव से पूर्णांक में रूपांतरण अनिवार्य रूप से ज़्यादा उलझे हुए होते हैं। दशमलव बिंदु के बाद आने वाली चीज़ का आप क्या करना चाहते हैं?
round() फंक्शन निकटतम पूर्ण संख्या में बदलता है, और बराबर की स्थिति में, जैसे 4.5, निकटतम सम पूर्ण संख्या की ओर गोल होता है।floor() नीचे की ओर गोल करता है, ceil() ऊपर की ओर, trunc() शून्य की ओर।Int32() से, एक InexactError के साथ विफल हो जाती है।लेकिन डिफ़ॉल्ट रूप से ये फंक्शन वह पूर्णांक टाइप नहीं लौटाते जो शायद आप चाहते थे। मनचाहा आउटपुट टाइप बताया जा सकता है।
julia> round(4.5)
4.0
julia> round(Int64, 4.5)
4
julia> round(Int, 4.5) # => default integer type
4
julia> ceil(Int16, 4.3)
5
दशमलव बिंदु के बाद अंकों की तय संख्या तक गोल करना भी digits कीवर्ड से संभव है।
julia> round(π, digits=10)
3.1415926536
ज़्यादा जानकारी के लिए मैनुअल देखिए।
यह तो पक्का कोई एरर देगा, है ना? वास्तव में मामला इतना सीधा नहीं है।
÷ या // से पूर्णांक भाग देने पर एरर आती है, जैसी आप उम्मीद करेंगे।
/ से दशमलव संख्या का भाग एक ऐसा रवैया अपनाता है जिसे सामान्य कंप्यूटर विज्ञान के तरीके के बजाय इंजीनियरिंग का तरीका कहा जा सकता है:
julia> 2 / 0
Inf
julia> 0 / 0
NaN
जैसा पिछले हिस्से में बताया गया, अनंत Julia में एक वैध दशमलव संख्या है, जिसे Inf से दर्शाया जाता है।
जब अंश भी शून्य हो, तो नतीजा गणित की दृष्टि से अपरिभाषित होता है।
तब Julia इसे "संख्या नहीं" मानती है, जिसे NaN से दर्शाया जाता है।
अगर यह अजीब लगे, तो सोचिए कि आप वास्तविक दुनिया के (और इसलिए अक्सर काफी बिखरे हुए) डेटा के बड़े ऐरे पर काम कर रहे हैं। आगे बढ़ने के लिए सबसे अच्छा यही है कि परेशानी वाली वैल्यू को चिह्नित करके छोड़ दें और आगे बढ़ जाएँ।
वैल्यू की अंतहीन मैनुअल जाँच को प्रोग्राम करना उबाऊ होगा, और इससे रनटाइम परफॉर्मेंस पर भी निश्चित रूप से असर पड़ेगा।
हर छोटी-सी गड़बड़ पर एरर संदेश देकर रुक जाना आपके प्रोग्राम को उपयोगकर्ताओं की नज़र में बहुत नापसंद बना देगा!
जैसा Conditionals कॉन्सेप्ट में बताया गया, बराबरी जाँचना आमतौर पर == ऑपरेटर से किया जाता है।
यह पूर्णांकों, अक्षरों, स्ट्रिंग और ऐसी ही चीज़ों के लिए ठीक काम करता है।
लेकिन दशमलव संख्याओं की परिशुद्धता सीमित होती है, और एक ही नतीजा निकालने के अलग-अलग तरीकों से थोड़ी-थोड़ी अलग वैल्यू मिल सकती हैं।
Float64 के लिए यह आमतौर पर 15वें सार्थक अंक के आसपास होता है: फर्क छोटा है, पर "बराबरी" नहीं है।
परंपरा से प्रोग्रामरों को यही सलाह दी जाती है कि दशमलव संख्याओं के साथ कभी == इस्तेमाल न करें: नतीजों का अनुमान नहीं लगाया जा सकता।
एक बहुत इस्तेमाल किया जाने वाला विकल्प यह है कि अंतर के निरपेक्ष मान की तुलना किसी अनुमत सहनशीलता (जिसे अक्सर एप्सिलॉन या ϵ कहते हैं) से की जाए।
इसलिए a == b के बजाय abs(a - b) < epsilon इस्तेमाल कीजिए।
Julia isapprox() फंक्शन के साथ एक साफ़-सुथरा विकल्प देती है।
निरपेक्ष सहनशीलता के लिए सिंटैक्स है isapprox(a, b, atol=epsilon)।
इस मामले में atol कीवर्ड ज़रूरी है।
सापेक्ष सहनशीलता अक्सर ज़्यादा काम आती है।
यही डिफ़ॉल्ट है, इसलिए isapprox(a, b) rtol के लिए कोई समझदार वैल्यू चुनने की कोशिश करता है (नियम काफी उलझे हुए हैं)।
सापेक्ष सहनशीलता भी बताई जा सकती है, तुलना की जा रही वैल्यू के अनुपात के रूप में।
इसलिए isapprox(a, b, rtol=0.01 जाँचता है कि वैल्यू एक-दूसरे के 1% के भीतर हैं।
Julia के आम अंदाज़ में, डिफ़ॉल्ट मामले का पर्याय एक गणितीय ऑपरेटर भी है: a ≈ b (ऑपरेटर को \approx टाइप करके <tab> दबाकर लिखा जाता है)।
परिमेय संख्याओं के अलावा, पाठ्यक्रम के आगे के हिस्सों में इन पर चर्चा होगी:
2.3 + 4.5im।