نگاهی عمیق به تایپ های گولنگ

توسط mrbardia72
زمان خواندن 3 دقیقه
نگاهی عمیق به تایپ های گولنگ

🎯نگاهی عمیق به تایپ های گولنگ🎯

Type Aliases

Basic Types

Strings

Pointers

Pointers

Interfaces

Structs

Type Assertions

Reflection

Functions

Channels

Collections

Arrays

Slices

Maps

Zero Values

🔹گولنگ سیستم بسیار جالبی دارد. این امر به نفع رابط ها و ترکیبات از کلاس ها و وراثت قرار می گیرد ، اما از طرف دیگر  جنریک ندارد.

🔹در این آموزش با جزئیات تایپ سیستم Go و چگونگی استفاده موثر از آن برای نوشتن کد آشنا خواهید شد.

🔹تایپ سیستم Go از الگوی های رویه ای ، شی گرا و عملکردی پشتیبانی می کند. پشتیبانی بسیار محدودی از برنامه نویسی عمومی دارد. در حالی که Go یک زبان ثابت است ، اما انعطاف پذیری کافی را برای تکنیک های پویا از طریق رابط ها ، توابع کلاس  فراهم می کند. 

🔹تایپ سیستم Go فاقد قابلیت هایی است که در اکثر زبان های مدرن رایج است:

🔺هیچ نوع استثنائی وجود ندارد زیرا مدیریت خطاهای Go بر اساس کدهای بازگشت و رابط خطا است.

🔺بیش از حد عملگر وجود ندارد.

🔺هیچ بار اضافی در عملکرد وجود ندارد (همان نام عملکرد با پارامترهای مختلف).

🔺پارامترهای عملکرد اختیاری یا پیش فرض وجود ندارد.

🔺این موارد حذف شده همه با توجه به طراحی انجام می شود تا Go هرچه ساده تر شود.

❌Type Aliases❌

package main
 
 
type Age int
 
func main() {
    var a Age = 5
    var b int = a
}
 
 
Output:
 
tmp/sandbox547268737/main.go:8: cannot use a (type Age) as
type int in assignment

می توانید alias types را در Go ایجاد کرده با تایپ های مختلف

 اما بدون تبدیل ، نمی توانید مقداری از نوع اساسی را به یک نوع مستعار اختصاص دهید. 

به عنوان مثال  در تصویر فوق ، انتساب var b int = a در برنامه زیر باعث خطای تلفیقی می شود 

زیرا نوع Age نام مستعار int است ، اما int نیست:

[ Photo ]

می توانید type declarations را گروه بندی کنید یا از یک  declarations در هر سطر استفاده کنید:

type IntIntMap map[int]int
StringSlice []string
 
type (
    Size   uint64
    Text  string
    CoolFunc func(a int, b bool)(int, error)
)

❌Basic Types❌

bool
string
int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32, represents a Unicode code point
float32 float64
complex64 complex128

❌Strings❌

یرای نمایش رشته های UTF8 encoded شده به Unicode character می توان از کتابخانه stringاستفاده کرد

در تصویر فوق یه مثال ساده رو مشاده می کنید که تمام کلمه رو میگیره و به حروف کوجک تبدیل می کنه و در آخر یه جمله رو نمایش میده

package main
 
import (
    "fmt"
    "strings"
)
 
func main() {
    words := []string{"i", "LikE", "the ColORS:", "RED,", 
                      "bLuE,", "AnD", "GrEEn"}
    properCase := []string{}
     
    for i, w := range words {
        if i == 0 {
            properCase = append(properCase, strings.Title(w))
        } else {
            properCase = append(properCase, strings.ToLower(w))
        }
    }
     
    sentence := strings.Join(properCase, " ") + "."
    fmt.Println(sentence)

❌Pointers❌

اشاره گر در گولنگ با * شروع میشه و برای دریافت مقدار از & استفاده میشه طبق کد فوق و اینکه مقدار zero value اشارگره در گو برابر nil هست

package main
 
import (
    "fmt"
)
 
 
type S struct {
    a float64
    b string
}
 
func main() {
    x := 5
    px := &x
    *px = 6
    fmt.Println(x)
    ppx := &px
    **ppx = 7
    fmt.Println(x)
}

❌Interfaces❌

اینترفیس ها  مجموعه ای ازmethod signatures است.

رابط های خالی  {} با هر نوع  داده ای سازگار است  و اغلب برای تایپ پویا استفاده می شود. اینترفیس ها همیشه اشاره گر هستند و همیشه به یک شی concrete مشخص اشاره می کنند.

type Shape interface {
    GetPerimeter() uint
    GetArea() uint
}
 
type Square struct {
   side  uint
}
 
func (s *Square) GetPerimeter() uint {
    return s.side * 4
}
 
func (s *Square) GetArea() uint {
    return s.side * s.side
}

❌Structs❌

درواقع Structs  یک ساختار شامل فیلدهای نامگذاری شده است که ممکن است انواع اساسی ، انواع نشانگر یا انواع دیگر ساختارها باشد. 

package main
 
import (
    "fmt"
)
 
 
type S1 struct {
    f1 int
}
 
type S2 struct {
    f2 int
}
 
type S3 struct {
    S1
    S2
    f3 int
    f4 *S3
}
 
 
func main() {
    s := &S3{S1{5}, S2{6}, 7, nil}
     
    fmt.Println(s)
}
 
Output:
 
&{{5} {6} 7 }

❌Type Assertions❌

توی این روش در خط شیش ما نوع تایپ خود را نمی دانیم چیه پس برای این کار از interface استفاده می کنیم که خود جزو تایپ های گو هست و تمام تایپ ها رو توی خود جا می دهد

package main
 
import "fmt"
 
func main() {
    things := []interface{}{"hi", 5, 3.8, "there", nil, "!"}
    strings := []string{}
     
    for _, t := range things {
        s, ok := t.(string)
        if ok {
            strings = append(strings, s)
        }
    }
     
    fmt.Println(strings)
     
}
 
Output:
 
[hi there !]

❌Reflection❌

بسته Go reflect به شما امکان می دهد نوع رابط را بدون استفاده از type assertions مستقیماً بررسی کنید. در صورت تمایل می توانید مقدار یک رابط را استخراج کنید

در اینجا یک مثال مشابه با مثال قبلی وجود دارد ، اما به جای چاپ رشته ها ، آنها را فقط می شمارد ، بنابراین نیازی به تبدیل از رابط {} به رشته نیست. کلید فراخوانی Reflect.Type () برای بدست آوردن یک نوع شی است که دارای متد Kind () است که به ما امکان می دهد تشخیص دهیم که آیا با یک رشته روبرو هستیم یا نه.

package main
 
import (
    "fmt"
    "reflect"
)
 
 
func main() {
    things := []interface{}{"hi", 5, 3.8, "there", nil, "!"}
    stringCount := 0
     
    for _, t := range things {
        tt := reflect.TypeOf(t)
        if tt != nil && tt.Kind() == reflect.String {
            stringCount++
        }
    }
     
    fmt.Println("String count:", stringCount)
}

❌Functions❌

در برنامه نویسی گولنگ در زمینه توابع نویسی شما می توانید توابع را به متغیرها اختصاص دهید ، و حتی توابع را به عنوان آرگومان به توابع دیگر منتقل کنید یا آنها را به عنوان نتیجه برگردانید. این زبان شما را قادر می سازد تا از سبک برنامه نویسی کاربردی با Go استفاده کنید. در این مثال به کاملی روشن هست

package main
 
import (
    "fmt"
    "math/rand"
)
 
type UnaryOp func(a int) int
type BinaryOp func(a, b int) int
 
 
func GetBinaryOp() BinaryOp {
    if rand.Intn(2) == 0 {
        return func(a, b int) int { return a + b }
    } else {
        return func(a, b int) int { return a - b }
    }
}
 
func GetUnaryOp() UnaryOp {
    if rand.Intn(2) == 0 {
        return func(a int) int { return -a }
    } else {
        return func(a int) int { return a * a }
    }
}
 
 
func main() {
    arguments := [][]int{{4,5},{6},{9},{7,18},{33}}
    var result int
    for _, a := range arguments {
        if len(a) == 1 {
            op := GetUnaryOp()
            result = op(a[0])            
        } else {
            op := GetBinaryOp()
            result = op(a[0], a[1])                    
        }
        fmt.Println(result)                
    }
}

❌Channels❌

😜Channels are an unusual data type😉

کانال ها یک نوع داده غیرمعمول هستند. شما می توانید آنها را به عنوان صف های پیام در نظر بگیرید که برای ارسال پیام بین گورتین استفاده می شود. درضمن کانال ها strongly typed می باشند. 

آنها همگام سازی شده اند و از پشتیبانی از نحو اختصاصی برای ارسال و دریافت پیام برخوردارند. هر کانال می تواند فقط دریافت ، ارسال و یا دو جهته باشد.

در اینجا یک مثال معمولی وجود دارد که در آن مجموع مربعات لیستی از اعداد صحیح به طور موازی توسط دو گورتین محاسبه می شود ، هر کدام نیمی از لیست را تشکیل می دهند. عملکرد اصلی منتظر نتیجه هر دو برنامه  هست و سپس  نتیجه  این دو را نمایش می دهد.

در واقع Strong typing معنی کلی‌اش این میشه که شما در زبان برنامه نویسی موردنظرتون باید مشخص کنید که متغیرها، ورودی/خروجی توابع، آرگومان‌ها و ... دقیقاً از چه نوعی هستند؛ یعنی کامپایلر در چنین زبان‌های برنامه‌نویسی، کدهای برنامه شما رو قبل از اینکه به مرحله Runtime برسه، بررسی می‌کنه و مطمئن میشه که شما نوع‌ها رو بصورت کامل مشخص کردید و نمی‌ذاره شما دادۀ بدون نوع داشته باشید

➖➖➖➖➖➖➖➖➖

در واقع  Weak typing با خیال راحت، هرجوری حال می‌کنید! می‌تونید از تعریف متغیر بدون نوع استفاده کنید، تو این نوع زبان‌ها، کامپایلر همه کار رو خودش می‌کنه و اصلاً کار نداره که شما برای متغیری نوع تعریف کنید، برای همین بسیار تنبل‌پرور هستند؛

package main
import "fmt"
 
 
func main() {
    a1 := [3]int{1, 2, 3}
    var a2 [3]int
    a2 = a1 
 
    fmt.Println(a1)
    fmt.Println(a2)
     
    a1[1] = 7
 
    fmt.Println(a1)
    fmt.Println(a2)
     
    a3 := [2]interface{}{3, "hello"}
    fmt.Println(a3)
}

❌Arrays❌

آرایه ها مجموعه هایی با اندازه ثابت از عناصر یک نوع هستند.

❌generic collections❌

یعنی هر نوع دادهای را می توانید ذخیره کنید

arrays

slices

maps

Channels

package main
 
import "fmt"
 
 
 
func main() {
    s1 := []int{1, 2, 3}
    var s2 []int
    s2 = s1 
 
    fmt.Println(s1)
    fmt.Println(s2)
 
    // Modify s1    
    s1[1] = 7
 
    // Both s1 and s2 point to the same underlying array
    fmt.Println(s1)
    fmt.Println(s2)
     
    fmt.Println(len(s1))
     
    // Slice s1
    s3 := s1[1:len(s1)]
     
    fmt.Println(s3)
}

❌ Slices ❌

آرایه ها به دلیل اندازه ثابت بسیار محدود هستند. اما Slices ها خیلی جالب ترند. شما می توانید Slices ها را به عنوان آرایه های پویا در نظر بگیرید.

بهتره بجای array از Slices استفاده کنید توی پروژه هاتون به دلیل کنترل gerber collector

👆🏻👆🏻توضیحات بیشتر👆🏻👆🏻

از طرف دیگر ، Slices ها بسیار انعطاف پذیر ، قدرتمند و راحت تر از آرایه ها هستند. بر خلاف آرایه ها ، می توان Slices ها را با استفاده از تابع ضمیمه داخلی تعبیه کرد. بعلاوه ، Slices از نوع مرجع یا refrence هستند ، بدین معنی که اختصاص آنها کم هزینه است و می توانند بدون نیاز به ایجاد نسخه جدیدی از آرایه اصلی آن ، به سایر توابع منتقل شوند. سرانجام ، توابع موجود در کتابخانه استاندارد Go ، همه از Slices به جای آرایه ها در API های عمومی خود استفاده می کنند.

👆🏻👆🏻👆🏻👆🏻

❌ این Slices ها را می توان با استفاده از نحوهای زیر ایجاد کرد:

make([]Type, length, capacity)

make([]Type, length)

[]Type{}

[]Type{value1, value2, ..., valueN}

👆🏻👆🏻👆🏻👆🏻

❌ این Arrays ها را می توان با استفاده از نحوهای زیر ایجاد کرد:

[N]Type

[N]Type{value1, value2, ..., valueN}

[...]Type{value1, value2, ..., valueN}

package main
 
import (
    "fmt"
)
 
func main() {
    // Create map using a map literal
    m := map[int]string{1: "one", 2: "two", 3:"three"}
     
    // Assign to item by key
    m[5] = "five"
    // Access item by key
    fmt.Println(m[2])
     
    v, ok := m[4]
    if ok {
        fmt.Println(v)
    } else {
        fmt.Println("Missing key: 4")
    }
     
     
    for k, v := range m {
        fmt.Println(k, ":", v)
    }
}
 
Output:
 
two
Missing key: 4
5 : five
1 : one
2 : two
3 : three

❌Maps❌

درواقع map ها مجموعه ای از جفت های مقدار-کلید هستند. 

🔴کلید نمیتونه هر نوعی باشه

❌ zero values: ❌

 0 for all integer types,

 0.0 for floating point numbers,

 false for booleans,

 "" for strings,

 nil for interfaces, slices, channels, maps, pointers and functions.