[১.৩] ভ্যারিয়েবল এবং কন্সট্যান্ট (Variable and Constant)

[১.৩.১] ভ্যারিয়েবল

সহজ ভাষায় বললে একটি ভ্যারিয়েবল একটি নির্দিষ্ট মেমোরি লোকেশন নির্দেশ করে যেখানে আমরা বিভিন্ন ভ্যালু স্টোর করার পাশাপাশি পরবর্তীতে পরিবর্তনও করতে পারি। প্রোগ্রামে এই লোকেশনগুলো সরাসরি ব্যবহার না করে আমরা বিভিন্ন বর্ণনামূলক নামের সাহায্যে (নির্দেশক) এদের ব্যবহার করে থাকি।

Go-তে প্রতিটি ভ্যারিয়েবলের একটি নির্দিষ্ট ধরন রয়েছে, যা ভ্যারিয়েবলের মেমরির আকার এবং বিন্যাস, সেই মেমরির মধ্যে সংরক্ষণ করা মানগুলির পরিসর এবং ভ্যারিয়েবলটির ক্ষেত্রে কি কি অপারেশন গ্রহণযোগ্য তা  নির্ধারণ করে।

ভ্যারিয়েবলগুলোকে আমরা ডাটা কন্টেইনার হিসাবে ভাবতে পারি, যাদের একমাত্র উদ্দেশ্য মেমরির ডাটাগুলো লেবেল করা এবং সংরক্ষণ করা, যা আমরা পরবর্তীতে প্রোগ্রামের বিভিন্ন জায়গা থেকে ব্যবহার করতে পারব।

[১.৩.২] ভ্যারিয়েবল ডিক্লেয়ারেশন এবং অ্যাসাইনমেন্ট

চলুন আমরা এখন দেখি, Go-তে কিভাবে ভ্যারিয়েবলগুলো ডিক্লেয়ার করা হয় – 

স্ট্যাটিক টাইপ ডিক্লেয়ারেশন – 

একটি স্ট্যাটিক টাইপ ভ্যারিয়েবল ডিক্লেয়ার করলে তা কম্পাইলারকে নিশ্চিত করে যে প্রদত্ত টাইপ এবং নামের শুধুমাত্র একটি ভ্যারিয়েবলই রয়েছে। এতে করে কম্পাইলার উক্ত ভ্যারিয়েবলের সব তথ্য ছাড়াই পরবর্তী লাইন কম্পাইলের কাজ শুরু করতে পারে –

				
					package main

   import "fmt"

   func main() {
       var x float64 = 20.0
       fmt.Println(x)
       fmt.Printf("x is of type %T\n", x)
       // Late Initialization
       var num int
       var amount float32
       var isValid bool
       var name string
       num = 20
       amount = 49.99
       isValid = true
       name = "Bappy"
       fmt.Println(num, amount, isValid, name)
   }

				
			

আউটপুট –

				
					 20
   x is of type float64
   20 49.99 true Bappy

				
			

Go-তে টাইপ ইনফারেন্স –

টাইপ না ডিক্লেয়ার করেও ডায়নামিক ভাবে ভ্যারিয়েবল ডিক্লেয়ার করা যায় Go-তে। এক্ষেত্রে আমাদেরকে ভ্যারিয়েবলের মান অ্যাসাইন করে দিতে হয় যার উপর ভিত্তি করে কম্পাইলার ভ্যারিয়েবলের টাইপ নির্ধারণ করে।

উদাহরণ –

				
					package main

   import "fmt"

   func main() {
       // Declare and Initialize Variables without DataType
       var num = 20
       var amount = 49.99
       var isValid = true
       var name = "Bappy"
       fmt.Println(num, amount, isValid, name) 
      //Using the shorthand syntax
       y := 42
       fmt.Println(y)
       fmt.Printf("y is of type %T\n", y) //y is of type int
       // Declare Multiple Variables
       num1, num2 := 20, 30          //int variables
       amount, name = 49.99, "Bappy" // float and string variables
       fmt.Println(num1, num2, amount, name)   
 }

				
			

আউটপুট –

				
					20 49.99 true Bappy
42
 y is of type int
 20 30 49.99 Bappy

				
			

উপরের উদাহরনে ভ্যারিয়েবলগুলো কোন টাইপ নির্ধারণ করা ছাড়াই ডিক্লেয়ার করা হয়েছে। টাইপ ইনফারেন্সের ক্ষেত্রে, আমরা y ভ্যারিয়েবলকে := অপারেটর দিয়ে ডিক্লেয়ার করেছি।

মিক্সড ভ্যারিয়েবল ডিক্লেয়ারেশন- 

টাইপ ইনফারেন্স ব্যবহার করে একাধিক ও বিভিন্ন প্রকারের ভ্যারিয়েবল এক সাথে ডিক্লেয়ার করা যেতে পারে। যেমন –

				
					package main

   import "fmt"
   
   func main() {
       var a, b, c = 3, 4, "foo"
       fmt.Println(a)
       fmt.Println(b)
       fmt.Println(c)
       fmt.Printf("a is of type %T\n", a)
       fmt.Printf("b is of type %T\n", b)
       fmt.Printf("c is of type %T\n", c)
   }

				
			

আউটপুট –

				
					 3
   4
   foo
   a is of type int
   b is of type int
   c is of type string
				
			

এছাড়া মান নির্ধারণ না করেও ভ্যারিয়েবল ডিক্লেয়ার  করা যেতে পারে, সেক্ষেত্রে প্রাথমিক মানটি ডেটাটাইপের ডিফল্ট মান হিসাবে সেট করা হয়ে থাকে, যেমনটি নীচে দেখানো হয়েছে –

				
					 package main
 
   import "fmt"
   
   func main() {
       var num int
       var amount float32
       var isValid bool
       var name string
       fmt.Println(num, amount, isValid, name) // output: 0 0 false
   }
   /* The string value is not clear in the output because this
   is showing an empty string ("") without the double quotation */

				
			

[১.৩.৩] ভ্যারিয়েবল রিডিক্লেয়ারেশন এবং রি-অ্যাসাইনমেন্ট

নিচের কোডটি লক্ষ করুন –

				
					 package main

   import "fmt"

   func main() {
       var_1, var_2 := 1, "hi" //declare var_1 int and var_2 string
       fmt.Println(var_1, var_2)
       var_3, var_2 := 2, "hello" //var_3 is declared, but var_2 is just reassigned
       fmt.Println(var_3, var_2)
   }

				
			

আউটপুট –

				
					  1 hi
   2 hello
				
			

লক্ষ্য করুন যে উভয় স্টেটমেন্টেই  “var_2” দেখা যাচ্ছে, এই ডুপ্লিকেশনটি Go-তে পুরোপুরি বৈধ। এখানে প্রথম স্টেটমেন্টে, ভ্যারিয়েবলটি ডিক্লেয়ার করা হয়েছে, আর দ্বিতীয় স্টেটমেন্টে এটি শুধুমাত্র পুনরায় অ্যাসাইন করা হয়েছে।

বিষয়টা সহজ মনে হলেও ভ্যারিয়েবল নতুনভাবে ডিক্লেয়ার এবং রি-অ্যাসাইনমেন্ট কিছু কিছু ক্ষেত্রে বিভ্রান্তিকর মনে হতে পারে। যেমন,

আমরা যদি এই উদাহরনটি খেয়াল করি –

				
					 package main

   import "fmt"

   func main() {
       var_1, var_2 := 1, "hi"
       //declaring for the first time
       fmt.Println(var_1, var_2)
       var_3, var_2 := 2, "hello"
       //same scope, the variable is reassigned
       fmt.Println(var_3, var_2)
       if var_4, var_2 := 3, "hey"; var_4 > var_1 {
           // variable is declared again in the scope of the if condition
           fmt.Println(var_4, var_2)
       }
       fmt.Println(var_2)
       //fmt.Println(var_4) //uncommenting this should give an compilation error
   }

				
			

আউটপুট –

				
					 1 hi
   2 hello
   3 hey
   hello

				
			

এখানে কি ঘটছে?

↳ দ্বিতীয় স্টেটমেন্টটি ভ্যারিয়েবলে একটি নতুন মান পুনরায় অ্যাসাইন করেছে কারণ এটি এবং আগের ভ্যারিয়েবলটি একই স্কোপে রয়েছে। কিন্তু, যদি আমরা if কন্ডিশন বিবেচনা করি, তাহলে দেখবো যে  একটি নতুন ভ্যারিয়েবল var_2 ডিক্লেয়ার করা হয়েছে যা শুধুমাত্র if কন্ডিশনের স্কোপের মধ্যেই সীমাবদ্ধ তাই ভ্যারিয়েবলের মান if কন্ডিশনের আগে এবং পরে hello।

শেষ লাইনে কমেন্ট আউট না করলে একটি কম্পাইল এরর দেখা দিতো কারণ var_4 শুধুমাত্র if কন্ডিশন ব্লকেই সীমাবদ্ধ এর বাইরে এর কোনো অস্তিত্ব নেই।

				
					Error: undefined: var_4
				
			

অর্থাৎ রি-অ্যাসাইনমেন্টের ক্ষেত্রে  দ্বিতীয় ডিক্লেয়ারেশনটি বিদ্যমান ডিক্লেয়ারেশনের মতো একই স্কোপে হওয়া উচিত, (যদি ভ্যারিয়েবলটি ইতিমধ্যে একটি বাইরের স্কোপে ডিক্লেয়ার করা হয়ে থাকে তবে পরের ডিক্লেয়ারেশনটি একটি নতুন ভ্যারিয়েবল তৈরি করবে)

আবার নতুন করে অ্যাসাইনের ক্ষেত্রে যে মান টি অ্যাসাইন করা হচ্ছে খেয়াল রাখতে হবে নতুন মানের টাইপ যাতে পূর্বের মানের টাইপের মতোই হয়। যেমন এই উদাহরনটি দেখুন –

				
					 package main

   import "fmt"

   func main() {
       var_1, var_2 := 1, "hi" //declaring for the first time
       fmt.Println(var_1, var_2)
       var_3, var_2 := 2, "hello" //same type so, the variable is reassigned
       fmt.Println(var_3, var_2)
       var_4, var_2 := 4, 3.14 //floating point cannot be assigned to a string variable
       fmt.Println(var_4, var_2)
   }

				
			

এখানে প্রথম স্টেটমেন্টটি ভ্যারিয়েবলটিকে প্রথমবার ডিক্লেয়ার করেছে, দ্বিতীয় স্টেটমেন্টটি এটিতে একটি নতুন মান পুনরায় অ্যাসাইন করেছে, তৃতীয় স্টেটমেন্টটি ভ্যারিয়েবলের জন্য একটি ভিন্ন টাইপের মান অ্যাসাইন করার চেষ্টা করছে (এই ক্ষেত্রে, একটি string ভ্যারিয়েবলে  একটি ফ্লোটিং-পয়েন্ট মান রি-অ্যাসাইন  করা ) যার ফলে একটি কম্পাইলেশন এরর হবে।

				
					Error: cannot use 3.14 (type float64) as type string in assignment
				
			

[১.৩.৪] ভ্যারিয়েবল স্কোপ

Go তে ভ্যারিয়েবল স্কোপ হল কোডের একটি অংশ যে সীমার মধ্যে একটি নির্দিষ্ট ভ্যারিয়েবলের মান অ্যাক্সেস বা পরিবর্তন করা যেতে পারে। Go তে ভ্যারিয়েবল কোথায় ডিক্লেয়ার করা হয়েছে তার উপর ভিত্তি করে দুভাগে বিভক্ত করা যেতে পারে – 

  • লোকাল ভ্যারিয়েবলঃ যে ভ্যারিয়েবলগুলোকে একটি ফাংশন বা কোড ব্লকের ভিতরে ডিক্লেয়ার করা হয় তাকে লোকাল ভ্যারিয়েবল বলে। এই ভ্যারিয়েবল ঐ নির্দিষ্ট কোড ব্লকের বাইরে দৃশ্যমান নয়। লোকাল ভ্যারিয়েবলগুলো একটি ফাংশন, লুপ এবং নেস্টেড কোডের ভিতরে ডিক্লেয়ার করা যেতে পারে। নেস্টেড লুপগুলোতে ঘোষিত ভ্যারিয়েবলগুলোর জন্য উচ্চ ক্রমের লুপ, নিম্ন ক্রমের লুপগুলোর ভ্যারিয়েবলগুলো অ্যাক্সেস করতে পারে না, তবে নিম্ন ক্রমের লুপগুলো প্যারেন্ট লুপগুলো থেকে সমস্ত ভ্যারিয়েবল অ্যাক্সেস করতে পারে। একবার ফাংশন বা কোডের ব্লক এক্সিকিউট করা হয়ে গেলে ঐ ভ্যারিয়েবলগুলো আর অ্যাক্সেসযোগ্য থাকে নাহ। লোকাল ভ্যারিয়েবল একই স্কোপে দুবার ডিক্লেয়ার করা যায় না। লোকাল ভ্যারিয়েবলের উদাহরণ –
				
					 package main

   import "fmt"

   func main() {
       // Local scope for the main method starts here.
       // Declaring local variable
       var scalar string = "Welcome to scalar"
       // Printing local variable
       fmt.Printf(scalar)
   }

				
			
  • গ্লোবাল ভ্যারিয়েবলঃ গ্লোবাল ভ্যারিয়েবল একটি ফাংশন বা কোড ব্লকের বাইরে সংজ্ঞায়িত করা হয়। সাধারণত, এগুলো আমাদের Go ফাইলের একদম শুরুতেই ডিক্লেয়ার করা হয়। কোনো প্যাকেজের মধ্যে থাকা যেকোনো ফাইলে যদি  গ্লোবাল ভ্যারিয়েবলগুলো ডিক্লেয়ার করা হয়ে থাকে তাহলে সেগুলো ঐ ফাইলের যেকোনো  ফাংশন থেকে এবং ঐ প্যাকেজের সব ফাইলগুলো থেকে অ্যাক্সেস করা যাবে।
    গ্লোবাল ভ্যারিয়েবলের উদাহরণ –
				
					 package main

   import "fmt"
   // Declaring Global variable
   var scalar string = "Welcome to scalar"

   func main() {
       // Printing Global variable in main
       fmt.Println(scalar + " from main")
       //function call
       displayGreeting()
   }

   func displayGreeting() {
       // Printing Global variable from a random function
       fmt.Println(scalar + " from function")
   }

				
			

আউটপুট –

				
					  Welcome to scalar from the main
   Welcome to scalar from function

				
			

[১.৩.৫] কন্সট্যান্ট বা ধ্রুবক

কন্সট্যান্ট এমন একটি মান, যা প্রোগ্রাম এক্সিকিউশনের সময় পরিবর্তন করা যায় না। কন্সট্যান্টগুলো const কিওয়ার্ড ব্যবহার করে ডিক্লেয়ার করা হয়, এবং ডিক্লেয়ার করার সময় অবশ্যই একটি মান অ্যাসাইন করতে হয় –

				
					package main

   import "fmt"

   func main() {
       const myConst = 10
       fmt.Println(myConst)
       // cannot assign a new value to a constant
       // myConst = 20 // this will cause a compile-time error
   }

				
			

এই উদাহরণে, myConst একটি কন্সট্যান্ট, যা ডিক্লেয়ার করার সময় এর মান 10 নির্ধারণ করা হয়েছে। একবার ডিক্লেয়ার করা হলে, myConst-এর মান পরিবর্তন করা যাবে না এবং করার চেষ্টা করলে কম্পাইল-টাইম এরর হবে।