Vivasoft-logo

[১.৪] ডাটা টাইপ (Data Type)

ডাটা টাইপ প্রোগ্রামিং এর একটি গুরুত্বপূর্ণ বিষয়। ডাটা টাইপ ভ্যারিয়েবল মানগুলোর আকার এবং প্রকার নির্দিষ্ট করে।

Go-তে ডাটা টাইপগুলোকে দুটি শ্রেণীতে বিভক্ত করা যেতে পারেঃ

  • বেসিক টাইপ (Basic type)
  • কম্পোজিট টাইপ(Composite type)

[১.৪.১] বেসিক টাইপ (Basic type)

এই পাঁচটি ডাটা টাইপকে বেসিক টাইপ হিসেবে বিবেচনা করা হয় :

  • সংখ্যাসূচক (Numeric type)
  • স্ট্রিং(String )
  • বাইট (Byte)
  • Rune
  • Boolean

ফরম্যাট স্পেসিফায়ার

বেসিক টাইপগুলো নিয়ে আরো বিস্তারিত বলার আগে  চলুন আমরা  fmt.printf() ফাংশনের সাহায্যে কিভাবে String ফরম্যাট করে প্রিন্ট করা যায় সে সম্পর্কে জেনে নেই।

Go তে  fmt.printf() ফাংশন এর সাহায্যে আমরা ডাটাকে ফরম্যাট করে প্রিন্ট করতে পারি। ফাংশনটি একটি টেমপ্লেট String নেয় যা আমরা ফরম্যাট করতে চাই, এবং কিছু verb থাকে যা fmt ফাংশনকে বলে যে কীভাবে শেষের আর্গুমেন্টগুলোকে ফরম্যাট করতে হবে৷

				
					fmt.printf(“sample template string %s”,object arg(s))
				
			
  • টেমপ্লেট string – ফরম্যাট করার জন্য ফাংশনে যে String টি পাস করা হচ্ছে সেটাই টেমপ্লেট string। % সাইনের পরে কনভারশন ক্যারেক্টার গুলো দিয়ে আমরা প্রয়োজনমত String টিকে ফরম্যাট করতে পারি।
  • অবজেক্ট আর্গুমেন্ট – টেমপ্লেট string  কে যেসব ভ্যালু দিয়ে ফরম্যাট করা হয় সেগুলোই অবজেক্ট আরগুমেন্ট।
				
					package main
import "fmt"

func main() {
   str := "Hello, world!"
   num1 := 42
   num2 := 3.14159
   num3 := 1234567890
   boolean := true
   char := 'A'
    // %T: print type of the value
    fmt.Printf("%T\n", str)	
    // %v: print default format for each type
    fmt.Printf("%v\n", str)    
   // String format specifiers
   fmt.Printf("%s\n", str)         // %s: print string
   fmt.Printf("%q\n", str)         // %q: print quoted string
   fmt.Printf("%x\n", []byte(str)) // %x: print hex encoding of bytes
   fmt.Printf("%X\n", []byte(str)) // %X: print uppercase hex encoding of bytes
   // Integer format specifiers
   fmt.Printf("%d\n", num1) // %d: print decimal integer
   fmt.Printf("%b\n", num1) // %b: print binary integer
   fmt.Printf("%o\n", num1) // %o: print octal integer
   fmt.Printf("%x\n", num1) // %x: print hex encoding of integer
   fmt.Printf("%X\n", num1) // %X: print uppercase hex encoding of integer
   fmt.Printf("%c\n", char) // %c: print character
   // Floating-point format specifiers
   fmt.Printf("%f\n", num2) // %f: print floating-point number
   fmt.Printf("%e\n", num2) // %e: print scientific notation of floating-point       number
   fmt.Printf("%E\n", num2) // %E: print scientific notation of floating-point number with uppercase E
   fmt.Printf("%g\n", num2) // %g: print floating-point number in decimal or scientific notation, depending on the value
   fmt.Printf("%G\n", num2) // %G: print floating-point number in decimal or scientific notation, depending on the value with uppercase E
   // Width and precision
   fmt.Printf("|%5d|\n", num1)    // %5d: print decimal integer with minimum width of 5 characters
   fmt.Printf("|%-5d|\n", num1)   // %-5d: print decimal integer with minimum width of 5 characters, left-justified
   fmt.Printf("|%5.2f|\n", num2)  // %5.2f: print floating-point number with minimum width of 5 characters and 2 digits after the decimal point
   fmt.Printf("|%-5.2f|\n", num2) // %-5.2f: print floating-point number with minimum width of 5 characters and 2 digits after the decimal point, left-justified
   // Boolean format specifiers
   fmt.Printf("%t\n", boolean) // %t: print boolean value
   // Pointer format specifier
   fmt.Printf("%p\n", &num3) // %p: print pointer address
}

				
			

আউটপুটঃ

				
					 string
   Hello, world!
   Hello, world!
   "Hello, world!"
   48656c6c6f2c20776f726c6421
   48656C6C6F2C20776F726C6421
   42
   101010
   52
   2a
   2A
   A
   3.141590
   3.141590e+00
   3.141590E+00
   3.14159
   3.14159
   |   42|
   42   |
   | 3.14|
   3.14 |
   true
   0x488b30

				
			

[১.৪.১.১] সংখ্যাসূচক টাইপ(Numeric type)

নিউমেরিক ডাটা টাইপ মূলত সংখ্যাগুলোর প্রতিনিধিত্ব করে, যা হতে পারে পূর্ণ সংখ্যা , ভগ্নাংশ সংখ্যা অথবা জটিল সংখ্যা। সংখ্যাসূচক ডাটার প্রকারগুলো প্রোগ্রামিং ভাষা, ডাটাবেসে পরিমাণ ও পরিমাপ এবং অন্যান্য সাংখ্যিক মানগুলো উপস্থাপন করতে ব্যবহৃত হয়।

তিনটি সংখ্যাসূচক ডাটা টাইপ হল – 

  • Integers
  • Floats
  • Complex Numbers

[১.৪.১.১.১] Integers

Go তে অন্যতম নিউমেরিক ডাটা টাইপ হলো ইন্টিজার । ইন্টিজার এর প্ল্যাটফর্ম নির্ভর সাইজ রয়েছে । একটি ইন্টিজার ডাটা টাইপের ভ্যারিয়েবলে শুধুমাত্র পূর্ণ সংখ্যা রাখা যায় ।  ইন্টিজার আবার দুই ধরনের হতে পারে: 

  • Signed Integers (ধনাত্মক ও ঋণাত্বক)
    • int
    • int8
    • int16 
    • int32 
    • int64
  • Unsigned Integers (শুধু ধনাত্মক)
    • uint
    • uint8
    • uint16
    • uint32
    • uint64
    • uintptr
				
					package main

   import (
       "fmt"
   )

   func main() {
       // Signed integers
       var a int8 = 127
       var b int16 = 32767
       var c int32 = 2147483647
       var d int64 = 9223372036854775807
       // Unsigned integers
       var e uint8 = 255
       var f uint16 = 65535
       var g uint32 = 4294967295
       var h uint64 = 18446744073709551615
       // Print the values
       fmt.Println("Signed Integers:")
       fmt.Printf("int8: %d\n", a)
       fmt.Printf("int16: %d\n", b)
       fmt.Printf("int32: %d\n", c)
       fmt.Printf("int64: %d\n", d)
       fmt.Println("Unsigned Integers:")
       fmt.Printf("uint8: %d\n", e)
       fmt.Printf("uint16: %d\n", f)
       fmt.Printf("uint32: %d\n", g)
       fmt.Printf("uint64: %d\n", h)
   }

				
			

[১.৪.১.১.২] Floats

একটি ফ্লোট ডাটা টাইপের ভ্যারিয়েবলে শুধুমাত্র ডেসিমাল ডাটা রাখা যায়। ফ্লোট এ দুই ধরনের ডাটা টাইপ রয়েছে :

  • float32
  • float64
				
					 package main

   import "fmt"

   func main() {
       // float32 example
       var myFloat32 float32 = 3.14
       fmt.Printf("myFloat32 = %f, type = %T\n", myFloat32, myFloat32)
       // float64 example
       var myFloat64 float64 = 3.141592653589793238462643383279502
       fmt.Printf("myFloat64 = %f, type = %T\n", myFloat64, myFloat64)

   }

				
			

আউটপুট –

				
					 myFloat32 = 3.140000, type = float32
   myFloat64 = 3.141593, type = float64

				
			

[১.৪.১.১.৩] Complex Numbers

Go তে দুই প্রকারের কমপ্লেক্স নাম্বার রয়েছে ।

  • Complex64
  • Complex128

 

একটি Complex Numbers এর দুইটি অংশ থাকে । 

  • Real
  • Imaginary

 

উদাহরণ –  

				
					 package main

   import "fmt"

   func main() {
       c1 := complex(2, 3)
       c2 := 4 + 5i             // complex initializer syntax a + ib
       c3 := c1 + c2            // addition just like other variables
       fmt.Println("Add: ", c3) // prints "Add: (6+8i)"
       re := real(c3)           // get real part
       im := imag(c3)           // get imaginary part
       fmt.Println(re, im)      // prints 6 8
   }

				
			

[১.৪.১.২]  Byte

গো-তে বাইট হলো একটি unsigned 8-bit ইন্টিজার। এর মানের সীমা 0-255। সাধারনত ASCII অক্ষর সংরক্ষণ করতে বাইট টাইপ ব্যবহার করা হয়ে থাকে। উদাহরণস্বরূপ, নিচের চিত্রটি একটি বাইট ভ্যারিয়েবলের Z অক্ষরটিকে উপস্থাপন করা হয়েছে –

একটি বাইট ভ্যারিয়েবল ডিক্লেয়ার করার জন্য, তিনটি ভিন্ন ভিন্ন উপায় আছে:

  • 0 এবং 255 এর মধ্যে একটি দশমিক পূর্ণসংখ্যা অ্যাসাইন করে
  • একটি ASCII অক্ষর অ্যাসাইন করে
  • বিভিন্ন বেসের পূর্ণসংখ্যা অ্যাসাইন করে (বাইনারী, অক্টাল, হেক্স)
				
					package main

   import (
       "fmt"
   )

   func main() {
       // assign by decimal integer (base 10)
       var ch byte = 90
       fmt.Printf("%08b %02x %v\n", ch, ch, ch)
       // assign an ASCII character
       var ch0 byte = 'Z'
       fmt.Printf("%08b %02x %v\n", ch0, ch0, ch0)
       // assign by different bases:
       var ch1 byte = 0b01011010 // Binary
       var ch2 byte = 0o132      // Octal
       var ch3 byte = 0x5a       // heX
       fmt.Printf("%08b %02x %v\n", ch2, ch2, ch2)
       fmt.Printf("%08b %02x %v\n", ch1, ch1, ch1)
       fmt.Printf("%08b %02x %v\n", ch3, ch3, ch3)
   }

   // Result
   // 01011010 5a 90
   // 01011010 5a 90
   // 01011010 5a 90
   // 01011010 5a 90
   // 01011010 5a 90

				
			

[১.৪.১.৩] Rune

Go তে Rune হল একটি ডাটা টাইপ যা দ্বারা ইউনিকোড character গুলোকে প্রকাশ করা হয়। ইউনিকোড হলো সমগ্র বিশ্বে উপস্থিত সম্ভাব্য সকল character এর সংগ্রহ।

				
					  // Simple Go program to illustrate
   // how to create a rune
   package main

   import (
       "fmt"
       "reflect"
   )

   func main() {

       // Creating a rune
       rune1 := 'B'
       rune2 := 'g'
       rune3 := '\a'
       // Displaying rune and its type
       fmt.Printf("Rune 1: %c; Unicode: %U; Type: %s", rune1, rune1, reflect.TypeOf(rune1))
       fmt.Printf("\nRune 2: %c; Unicode: %U; Type: %s", rune2, rune2, reflect.TypeOf(rune2))
       fmt.Printf("\nRune 3: Unicode: %U; Type: %s", rune3, reflect.TypeOf(rune3))

   }

				
			

আউটপুট – 

				
					

   Rune 1: B; Unicode: U+0042; Type: int32
   Rune 2: g; Unicode: U+0067; Type: int32
   Rune 3: Unicode: U+0007; Type: int32

				
			

[১.৪.১.৪] String

Go-তে string হচ্ছে character এর অ্যারে যারা একসাথে হয়ে শব্দ এবং বাক্য গঠন করে । String সব সময় দুইটা ডাবল কোটেশনের মধ্যে থাকে। উদাহরণ :”Hello, World!”

				
					package main

   import (
       "fmt"
       "strings"
   )

   func main() {
       // string example
       var myString string = "Hello, World!"
       fmt.Println(myString)
       // concatenation
       str1 := "Hello, "
       str2 := "World!"
       result := str1 + str2
       fmt.Println(result) // Output: Hello, World!
       // slicing
       myString = "Hello, World!"
       fmt.Println(myString[0:5]) // Output: Hello
       // indexing
       fmt.Println(myString[7]) // Output: W
       // length of a string
       fmt.Println(len(myString)) // Output: 13
       // converting a string to uppercase
       fmt.Println(strings.ToUpper(myString)) // Output: HELLO, WORLD!
       // converting a string to lowercase
       fmt.Println(strings.ToLower(myString)) // Output: hello, world!
       // splitting a string
       str := "one,two,three,four,five"
       splitStr := strings.Split(str, ",")
       fmt.Println(splitStr) // Output: [one two three four five]
   }

				
			

[১.৪.১.৫] Booleans

Boolean টাইপ হলো এক ধরনের ডাটা টাইপ যে টাইপের শুধুমাত্র দুইটি মান থাকে true অথবা false । Go তে Boolean-type ভ্যারিয়েবল তৈরি করতে bool কিওয়ার্ড ব্যবহার করা হয় ।

উদাহরণ –

				
					package main

   import (
       "fmt"
   )

   func main() {
       var bVal bool // default is false
       fmt.Printf("bVal: %v\n", bVal)
   }

				
			

[১.৪.২] কম্পোজিট টাইপ (Composite type)

Go- তে কম্পোজিট টাইপ সাপোর্ট করে। সাধারনত তিন ধরনের কম্পোজিট টাইপ রয়েছে – 

  • সমষ্টি টাইপ বা নন-রেফারেন্স টাইপ (Aggregate type/Non-Reference type)
  • রেফারেন্স টাইপ (Reference type)
  • ইন্টারফেস টাইপ (Interface type)

[১.৪.২.১] সমষ্টি টাইপ বা নন-রেফারেন্স টাইপ (Aggregate type/Non-Reference type)

সমষ্টি টাইপ বা নন-রেফারেন্স টাইপকে আবার দুই ধরনের : 

  • অ্যারে( Array)  
  • স্ট্রাকচার(Structure)

[১.৪.২.১.১] অ্যারে (Array)

Go তে অ্যারে হলো একই টাইপের একাধিক ভ্যারিয়েবল এর সংগ্রহ যার দৈর্ঘ্য এটি ডিক্লেয়ার করার সময়ই নির্দিষ্ট করে দিতে হয় এবং পরবর্তীতে আর বদলানো যায় না। 

একটি অ্যারের প্রত্যেক উপাদান নিজস্ব একটি ইনডেক্স দ্বারা আলাদাভাবে চিহ্নিত করা যায়। এই ইনডেক্সটি হলো একটি ইন্টিজার ভ্যালু, যা 0 থেকে শুরু । অ্যারে ডিক্লেয়ার করার পদ্ধতি নিম্নরূপ – 

				
					var array [size]type
				
			

নিচের উদাহরণে অ্যারের ব্যবহার আরেকটু পরিষ্কার হবে –

				
					func main() {
   var a [5]int
   a[4] = 100
   fmt.Println("set:", a)
   fmt.Println("get:", a[4])
}

				
			

আউটপুট- 

				
					 set: [0 0 0 0 100]
   get: 100
				
			

আমরা চাইলে একের অধিক ডাইমেনশন সম্বলিত অ্যারে তৈরি করতে পারি –

				
					 func main() {
       var twoD [2][3]int
       for i := 0; i < 2; i++ {
           for j := 0; j < 3; j++ {
               twoD[i][j] = i + j
           }
       }
       fmt.Println("2d: ", twoD)
   }

				
			

আউটপুট- 

				
					2d:  [[0 1 2] [1 2 3]]
				
			

লক্ষ্য করলে দেখতে পাবো, fmt.Println() দিয়ে প্রিন্ট করা হলে অ্যারেগুলো [ [v1] [v2] [v3] … ] আকারে প্রদর্শিত হয়। অর্থাৎ আমাদের দুই ডাইমেনশনের অ্যারে আসলে একটি অ্যারের অ্যারে। 

এছাড়াও অ্যারের অনেক নিজস্ব ফাংশন রয়েছে, যেমন, len(), cap(), copy(), append() ইত্যাদি, যেগুলো প্রয়োজন অনুসারে বিভিন্ন সময় ব্যবহার করব আমরা। 

[১.৪.২.২.২] স্ট্রাকচার (struct)

Struct, Go প্রোগ্রামিং এ খুবই গুরুত্বপূর্ণ  একটা বিষয়। আমরা ডাটা টাইপ সম্পর্কে জানি, ইন্টিজার, string, ফ্লোট ইত্যাদি। Struct হলো ইউজারের নিজ প্রয়োজন অনুসারে বানিয়ে নেয়া একটা কাস্টম ডাটা টাইপ যেখানে ভিন্ন ভিন্ন ডাটা টাইপকে একটা এনটিটির ভেতরে একত্র করা যায়। 

অ্যারে হচ্ছে একটা ডাটা স্ট্র্যাকচার। যেখানে আমরা শুধু  একই ডাটা টাইপ এর ডাটা রাখতে পারি। কিন্তু Struct তৈরি করে আমরা এক সাথে ইন্টিজার, ক্যারেক্টার, ফ্লোট ইত্যাদি ভিন্ন ভিন্ন ডাটা এক সাথে রাখতে পারি।


একটি Struct গঠন করা হয় নিম্নলিখিত সিনট্যাক্সের মাধ্যমে –

				
					 type StructName struct {
       Field1 datatype
       Field2 datatype
       //................
   }
				
			

এখানে person struct এর ভেতরে name(string) এবং age(int) ফিল্ড আছে –

				
					type person struct {
       name string
       age  int
   }
				
			

Struct ডিক্লেয়ার করার ভিন্ন ভিন্ন পদ্ধতি, Struct লিটেরালের ব্যবহার, নেস্টেড Struct, Struct এর পয়েন্টার ইত্যাদি বিষয়ে বিস্তারিত পরবর্তিতে “Structs and Interfaces” চ্যাপ্টারে জানতে পারব।

[১.৪.২.২] রেফারেন্স টাইপ (Reference type)

[১.৪.২.২.১] পয়েন্টারস (Pointers)

Go পয়েন্টার সাপোর্ট করে, যা প্রোগ্রামের মধ্যে কোন ভারিয়েবলের মান এবং তার রেফারেন্স পাস করার অনুমতি দেয়।

আমরা দেখাব কিভাবে ফাংশন, পয়েন্টার নিয়ে কাজ করে । আমরা দুইটি ফাংশন ডিফাইন করেছি যা হল  zeroval() এবং zeroptr()। zeroval() ফাংশনে একটি ইন্টিজার প্যারামিটার রয়েছে এবং zeroptr() ফাংশনে একটি ইন্টিজার পয়েন্টার প্যারামিটার রয়েছে। আমরা main() ফাংশন থেকে যদি কোনো মান পাঠাই তাহলে zeroval ফাংশনটি সেই ভ্যালুর একটি কপি রিসিভ করবে ival ভ্যারিয়েবলের সাহায্যে এবং zeroptr ফাংশনটি সেই ভ্যালুর একটি রেফারেন্স রিসিভ করবে iptr পয়েন্টার ভ্যারিয়েবলের সাহায্যে –

				
					 func zeroval(ival int) {
       ival = 0
   }

   func zeroptr(iptr *int) {
       *iptr = 0
   }
				
			

অর্থাৎ ival ভারিয়াবলের মানের কোন পরিবর্তন করলে সেটার main() ফাংশনে থাকা কপির কোন পরিবর্তন হবে না কারণ সে শুধু তার মানের একটা কপি পাঠিয়ে দিয়েছে। 

অন্য দিকে  *iptr ভারিয়াবলের মানের কোন পরিবর্তন করলে main() ফাংশনে থাকা ওই ভ্যারিয়েবলের মানেরও পরিবর্তন হয়ে যাবে কারণ পয়েন্টার ভ্যারিয়েবলটির অ্যাড্রেস পাঠিয়ে দেয় এবং  পরিবর্তনতাও উক্ত মেমোরি অ্যাড্রেসে সরাসরি হয় –

				
					func main() {
       i := 1
       fmt.Println("initial:", i)
       zeroval(i)
       fmt.Println("zeroval:", i)
       zeroptr(&i)
       fmt.Println("zeroptr:", i)
       fmt.Println("pointer:", &i) // i এর অ্যাড্রেস প্রিন্ট হবে
   }

   // Output :
   // initial: 1
   // zeroval: 1
   // zeroptr: 0
   // pointer: 0x42131100

				
			

[১.৪.২.২.২] স্লাইস (slices)

স্লাইস Go-তে একটি গুরুত্বপূর্ণ ডাটা টাইপ। আমরা চাইলেই অ্যারেতে একই ধরণের  ডেটা আইটেম স্টোর করতে পারি, তবে আমরা চাইলেই অ্যারের আকার ডাইন্যামিক্যালি বাড়াতে পারি  না। স্লাইস এই সীমাবদ্ধতা দূর করে, যার সাহায্যে এর আকার ডাইন্যামিক্যালি বাড়ানো যায়। এজন্যই স্লাইসকে বলা হয় যে অ্যারের তুলনায় আরও বেশি শক্তিশালী, ফ্লেক্সিবল এবং কনভেনিয়েন্ট। স্লাইস তার ভ্যারিয়েবল লেন্থের সিকুয়েন্সে একই টাইপের ডাটা স্ট্রোর করতে পারে কিন্তু অন্য টাইপের ডাটা অ্যালাও করে না। একটি স্লাইসের প্রথম ইনডেক্সের মান সর্বদা শুন্য এবং শেষ ইন্ডেক্সের মান হবে (স্লাইসের দৈর্ঘ্য – 1) –

				
					package main

   import "fmt"

   func main() {
       array := [5]int{1, 2, 3, 4, 5}
       slice := array[1:4]
       fmt.Println("Array: ", array)
       fmt.Println("Slice: ", slice)
   }

   // Output :
   // Array: [1 2 3 4 5]
   // Slice: [2 3 4]
				
			

আমরা চাইলে অ্যারের মত এখানেও ভ্যালু সেট  করতে পারি – 

স্লাইস নিয়ে কাজ করার ক্ষেত্রে শুরুতে এদের সাইজ, ক্যাপাসিটি কত হবে তা আমরা জানলেও, পরে তা পরিবর্তন হতে পারে (অর্থাৎ এদের সাইজ, ক্যাপাসিটি ডাইনামিক্যালি পরির্তন হতে পারে) এরকম অবস্থায় আমরা বিল্ট ইন make()  ফাংশন ব্যবহার করে স্লাইস ডিক্লেয়ার করতে পারি,  make() ফাংশন সম্পর্কে আমরা বিস্তারিত  ডাটা ও ফাইল নিয়ে কাজ অধ্যায়ে জানতে পারবো।

				
					 package main

   import "fmt"

   func main() {
       s := make([]string, 3)
       s[0] = "a"
       s[1] = "b"
       s[2] = "c"
       fmt.Println("set:", s)
       fmt.Println("get:", s[2])
   }

   // Output :
   // set: [a b c]
   // get: c

				
			

len() ফাংশনের সাহায্যে  আমরা স্লাইসের দৈর্ঘ্য পেতে পারি –

				
					 fmt.Println("len:", len(s))
   Output : 3
				
			

কিছু মৌলিক কাজ ছাড়াও, স্লাইস আরও অনেককিছু সমর্থন করে যা একে অ্যারের চেয়েও বেশি ডাইন্যামিক করে তুলে। append() ব্যবহার করে এক বা একাধিক নতুন মান সম্বলিত একটি নতুন স্লাইস তৈরি করতে পারি।

				
					 s = append(s, "d")
   s = append(s, "e", "f")
   fmt.Println("apd:", s)

   //  Output : apd: [a b c d e f]
				
			

আমরা চাইলে স্লাইসকে কপি করতে পারি তার জন্য আমাদের মেইন স্লাইসের দৈর্ঘ্যের সমান একটা এম্পটি স্লাইস তৈরি করতে হবে। 

উদাহরণে আমরা s এর সমান দৈর্ঘ্যের একটি খালি স্লাইস c তৈরি করেছি এবং s স্লাইস c তে কপি করয়েছি – 

				
					 func main() {
       s := make([]string, 3)
       s[0] = "a"
       s[1] = "b"
       s[2] = "c"
       s = append(s, "d")
       s = append(s, "e", "f")
       c := make([]string, len(s))
       copy(c, s)
       fmt.Println("cpy:", c)
   }

   // Output : cpy: [a b c d e f]

				
			

স্লাইসে নিম্নের অপারেশনগুলোও করা যায়  –

				
					 copySlice:= s[2:5] // copying the value from array index 2 to 4
 fmt.Println("s1:",copySlice) 
 copySlice = s[:5] // copying the value from array index 0 to 4
 fmt.Println("s2:", copySlice)
 copySlice = s[2:] // copying the value from array index 2 to 5
 fmt.Println("s3:", copySlice)


 Output :
    s1: [c d e]
    s2: [a b c d e]
    s3: [c d e f]

				
			

অ্যারের মতো স্লাইসও একাধিক ডাইমেনশনের হতে পারে –

				
					 func main() {
       twoD := make([][]int, 3)
       for i := 0; i < 3; i++ {
           innerLen := i + 1
           twoD[i] = make([]int, innerLen)
           for j := 0; j < innerLen; j++ {
               twoD[i][j] = i + j
           }
       }
       fmt.Println(twoD)
   }


   // Output :
   // [[0] [1 2] [2 3 4]]

				
			

[১.৪.২.২.৩] ম্যাপস (maps)

Go-তে Map হলো একটি কালেকশনের মতো যেখানে অনেকগুলো unordered ডাটা কি-ভ্যালু জোড়া হিসেবে থাকে। আমরা দুইভাবে ম্যাপ তৈরি করতে পারি,  make() ফাংশন ছাড়া এবং  make() ফাংশন ব্যবহার করে।  

সিন্ট্যাক্স-

				
					 // make() ফাংশন ছাড়া
   map[Key_Type]Value_Type{} // An Empty map
   map[Key_Type]Value_Type{key1: value1, ..., keyN: valueN} // Map with key-value pair
   // make() ফাংশনে সাহায্যে 
   make(map[Key_Type]Value_Type, initial_Capacity)
   make(map[Key_Type]Value_Type)

				
			

উদাহরণ –

				
					 package main


   import "fmt"


   func main() {
       var map1 map[int]int
       if map1 == nil {
           fmt.Println("True")
       } else {
           fmt.Println("False")
       }
       map2 := map[int]string{
           90: "Dog",
           91: "Cat",
           92: "Cow",
           93: "Bird",
           94: "Rabbit",
       }
       fmt.Println("Map-2: ", map2)
   }


   // Output :
   // True
   // Map-2:  map[90:Dog 91:Cat 92:Cow 93:Bird 94:Rabbit]
				
			

fmt.Println() এর সাহায্যে একটি map  প্রিন্ট করলে কি-ভ্যালুর জোড়া হিসেবে দেখাবে। যা আমরা উপরের আউটপুটে দেখতে পাচ্ছি।

Maps_name[key] দিয়ে আমরা একটি key এর জন্য একটি ভ্যালু পাবো –

				
					fmt.Println( map2[90])
   Output : Dog

				
			

len() ফাংশন ব্যাবহার করে আমরা একটি ম্যাপের কী/ভ্যালুর জোড়ার সংখ্যা পেতে পারি –

				
					fmt.Println("len:", len(map2))
   Output : 5

				
			

delete() ফাংশন ব্যবহার করে map থেকে একটি কি-ভ্যালুর জোড়া সরিয়ে দিতে পারি –

				
					 delete(map2,91)
   fmt.Println("Map-2 : ", map2)
   Output : Map-2 : map[90:Dog   92:Cow 93:Bird 94:Rabbit

				
			

আমরা চাইলে নিচের এই সিনট্যাক্সের সাহায্যে একই লাইনে একটি নতুন Map ডিক্লেয়ার এবং ইনিশিয়ালাইজ ও করতে পারি –

				
					 func main() {
       n := map[string]int{"foo": 1, "bar": 2}
       fmt.Println("map:", n)
   }
   Output : map[bar:2 foo:1]

				
			
<নোট: আমাদের মনে রাখতে হবে  যে fmt.Println দিয়ে প্রিন্ট করা হলে ম্যাপ map[k:v k:v] আকারে প্রদর্শিত হয়>

[১.৪.২.২.৪] ফাংশনস (functions)

সহজ ভাষায় ফাংশন হল কিছু কোড বা অপারেশনের সমষ্টি, যা কোন নির্দিষ্ট কাজ সম্পন্ন করে চাহিদামতো রেজাল্ট রিটার্ন করে এবং এই কোড ব্লকটি প্রোগ্রামের এর বিভিন্ন জায়গা থেকে কল করা যায়। ফাংশনের সিনট্যাক্স হল –
				
					func function_name(Parameter-list)(Return_type){
       // function body.....
   }

				
			
নিচের উদাহরণের দিকে লক্ষ করি, আমরা plus নামে একটা ফাংশন তৈরি করেছি যাতে দুইটি প্যারামিটার a এবং b ইন্টিজার নেওয়া হয়েছে যার রিটার্ন টাইপও ইন্টিজার। এই ফাংশনটির কাজ হল দুইটি নাম্বার যোগ করে রিটার্ন করা।
				
					 func plus(a int, b int) int {
       return a + b
   }

				
			

যখন একই টাইপের একাধিক প্যারামিটার থাকে, তখন আমরা সবগুলো প্যারামিটারের জন্য একটি টাইপ ব্যবহার করতে পারি-

				
					  func plusPlus(a, b, c int) int {
       return a + b + c
   }

				
			
				
					package main

   import "fmt"

   func main() {
       res := plus(1, 2)
       fmt.Println("1+2 =", res)
       res = plusPlus(1, 2, 3)
       fmt.Println("1+2+3 =", res)
   }

   // Output :
   // 1+2 = 3
   // 1+2+3 = 6

				
			

Go-তে  একটি ফাংশন একাধিক  ভ্যালু রিটার্ন করতে পারে –

				
					func vals() (int, string) {
       return 3, "Another variable"
   }

				
			

এখানে আমরা দুটি ভিন্ন রিটার্ন মান ব্যবহার করেছি-

				
					 func main() {
       val, str := vals()
       fmt.Println(val)
       fmt.Println(str)
   }

				
			

আবার, যদি আমরা কোন ভ্যালু না নিতে চাই সেটাও পারব। শুধু আমাদের ভ্যারিয়েবলের জায়গায় ( _ ) এই চিহ্ন ব্যবহার করতে হবে। একে অ্যানোনিমাস ভ্যারিয়েবল ও বলা হয়।

				
					   func main() {
       _, str := vals()
       fmt.Println(str)
   }
   Output : Another variable

				
			

Go ফাংশনগুলোর আরও কয়েকটি বৈশিষ্ট্য রয়েছে। যেমন, একটি অনির্দিষ্ট সংখ্যক আর্গুমেন্ট গ্রহণ করা Go ফাংশনের আরেকটি চমৎকার বৈশিষ্ট্য। আমরা পরবর্তী অধ্যায়ে এগুলো বিস্তারিত দেখব।

[১.৪.২.২.৫] চ্যানেলস (channels)

Go তে চ্যানেল হল পাইপ লাইনের মতো যাকে ব্যবহার করে ডাটা সেন্ড এবং রিসিভ করা হয়। বাস্তবে যদি আমরা পাইপ লাইনের দিকে তাকাই তাহলে দেখতে পাবো পাইপ লাইনের মাধ্যমে পানি এক প্রান্ত থেকে অন্য প্রান্তে প্রবাহিত হচ্ছে, একইভাবে চ্যানেল ব্যবহার করে এক প্রান্ত থেকে ডাটা পাঠানো হয় এবং অন্য প্রান্ত থেকে ডাটা গ্রহণ করা হয়। চ্যানেলস সম্পর্কে আমরা কনকারেন্সি সেকশন থেকে আরও বিস্তারিত জানব। “<-” অপারেটরের সাহায্যে চ্যানেল ডাটা সেন্ড এবং রিসিভ করে –

				
					ch <- v    // Send v to channel ch.
   v := <-ch  // Receive from ch, and assign value to v.
				
			

Map এবং স্লাইস-এর  মতো, ব্যবহারের আগে চ্যানেলগুলো ডিক্লেয়ার করতে হবে – বিস্তারিত দেখব।

				
					 ch := make(chan int)
				
			

নিচের উদাহরণটি লক্ষ করি –

				
					package main

   import (
       "fmt"
   )

   func main() {
       numberCh := make(chan int, 10)
       for i := 1; i <= 10; i++ {
           numberCh <- i
       }
       for j := 1; j <= 10; j++ {
           fmt.Println(<-numberCh)
       }
   }

				
			

এখানে numberCh <-i মাধ্যমে আমরা চ্যানেলে ডেটা সেন্ড করেছি এবং  <- numberCh এর মাধ্যমে পাঠানো ডেটা রিসিভ করেছি। আমরা চাইলে পাঠানো ডেটা রিসিভ করে যে কোনো ভ্যারিয়েবলেও স্টোর করে রাখতে পারতাম (newNumber := <- numberCh)।

আউটপুট –

				
					 1
   2
   3
   4
   5
   6
   7
   8
   9
   10
				
			

[১.৪.২.৩] ইন্টারফেস টাইপ (Interface type)

ইন্টারফেস হলো কাস্টম ডাটা টাইপ সেখানে অনেকগুলো মেথডের সিগনেচার থাকে এবং কোনো Struct বা Non-struct ডাটা টাইপ এগুলো মেথড হিসেবে ইমপ্লিমেন্ট করলে ইন্টারফেসটি ইমপ্লিমেন্ট হয়ে যায়। –

				
					 type geometry interface {
       area() float64
       perim() float64
   }

				
			

উদাহরণে, আয়তক্ষেত্র এবং বৃত্তের Struct এ ইন্টারফেস geometry ইমপ্লিমেন্ট করা হল –

				
					type rect struct {
       width, height float64
   }
   type circle struct {
       radius float64
   }
   
   func (r rect) area() float64 {
       return r.width * r.height
   }
   
   func (r rect) perim() float64 {
       return 2*r.width + 2*r.height
   }
   
   func (c circle) area() float64 {
       return math.Pi * c.radius * c.radius
   }
   
   func (c circle) perim() float64 {
       return 2 * math.Pi * c.radius
   }

				
			

যদি একটি ভ্যারিয়েবল একটি ইন্টারফেস ইমপ্লিমেন্ট করে থাকে, তাহলে আমরা ইন্টারফেসে থাকা মেথডগুলোকে কল করতে পারি। যেকোন geometry ইন্টারফেস ইমপ্লিমেন্টকারীর মেথড গুলো রান করার জন্য এখানে একটি measure() ফাংশন রয়েছে।

				
					func measure(g geometry) {
       fmt.Println(g)
       fmt.Println(g.area())
       fmt.Println(g.perim())
   }

				
			

আয়তক্ষেত্র এবং বৃত্ত উভয় struct , geometry ইন্টারফেস প্রয়োগ করেছে। এখন আমরা পরিমাপ করার জন্য আর্গুমেন্ট হিসাবে এই Struct গুলো ব্যবহার করতে পারি –

				
					 func main() {
       r := rect{width: 3, height: 4}
       c := circle{radius: 5}
       measure(r)
       measure(c)
   }

				
			

আউটপুট –

				
					{3 4}
   12
   14
   {5}
   78.53981633974483
   31.41592653589793