۳۰ آذر ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

آموزش Golang: با زبان Go شروع کنید

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

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

Go یک زبان برنامه نویسی متن باز از Google است که ساختن نرم افزار ساده، قابل اعتماد و کارآمد را آسان می کند. این بخشی از سلسله زبان های برنامه نویسی است که با فرآیندهای متوالی ارتباط تونی هور آغاز شد و شامل Occam، Erlang، Newsqueak و برزخ. پروژه زبان Go در حال حاضر بیش از ۱۸۰۰ مشارکت کننده دارد و توسط راب پایک، یک مهندس برجسته در Google.

Go در ابتدا به عنوان جایگزینی برای C++ توسعه یافت. اساسا، Pike از مجموعه‌های طولانی C++ یک برنامه اصلی Google خسته شده است< /a>. با این حال، همانطور که مشخص شد، تعداد بسیار کمی از تبدیل‌های زبان Go از C++ دریافت شده‌اند. وقتی از او پرسیده شد که پس از عرضه Go در سال ۲۰۱۲ چه چیزی او را بیشتر شگفت زده کرد، Pike پاسخ داد: “اگرچه انتظار داشتیم برنامه نویسان ++C Go را به عنوان یک جایگزین ببینند، در عوض بیشتر برنامه نویسان Go از زبان هایی مانند Python و Ruby می آیند.”

در آن زمان، تقریباً همه کسانی که از C++ یا جاوا می‌آمدند، می‌خواستند Go کلاس‌ها و ژنریک داشته باشد. پایک و دیگران عقب نشینی کردند، اما در سال ۲۰۲۲، همه چیز تغییر کرد. از Go 1.18، کلیات در نهایت بخشی از زبان Go هستند.

این مقاله برخی از ویژگی‌های متمایز Go را نشان می‌دهد، از جمله الگوهای همزمانی بسیار سبک و انواع جدید.

برش ها

Go ایده آرایه‌ها را با برش‌ها گسترش می‌دهد که دارای اندازه متغیر هستند. یک برش به آرایه ای از مقادیر اشاره می کند و یک طول را شامل می شود. به عنوان مثال، [ ]T یک برش با عناصر از نوع T است. در کد زیر، ما از برش‌های برش بایت‌های بدون علامت برای نگهداری پیکسل‌های تصویری که تولید می‌کنیم استفاده می‌کنیم، جایی که مقادیر پیکسل از ۰ تا ۲۵۵ متغیر است. برنامه‌های Go با package main شروع به اجرا می‌کنند. . عبارت import یک نسخه توسعه یافته از عبارت include C و C++ است.


package main

import "code.google.com/p/go-tour/pic"

func Pic(dx, dy int) [][]uint8 {
	slice := make([][]uint8, dy)
	for i := range slice {
		slice[i] = make([]uint8, dx)
		for j := range slice[i] {
			slice[i][j] = uint8(i * j)
		}
	}
	return slice
}

func main() {
	pic.Show(Pic)
}

نحو := یک متغیر را اعلام و مقداردهی اولیه می کند، و کامپایلر هر زمان که بتواند یک نوع را استنتاج می کند. همچنین توجه داشته باشید که make برای ایجاد برش ها و انواع دیگر استفاده می شود. یک حلقه for...range معادل حلقه for...in سی شارپ است. الگوی نشان داده شده در شکل ۱ با عبارت موجود در حلقه داخلی بالا تعیین می شود: (i*j). به بسته عکس و کد منبع برای کسب اطلاعات بیشتر.

برش برو

شکل ۱. الگویی که برش ها را در Go نشان می دهد.

نقشه ها

عبارت Go map کلیدها را به مقادیر نگاشت می کند. مانند slice، شما یک نقشه با make ایجاد می کنید، نه new. در مثال زیر، کلیدهای رشته ای را به مقادیر صحیح نگاشت می کنیم. این کد درج، به‌روزرسانی، حذف و آزمایش عناصر نقشه را نشان می‌دهد.


package main

import "fmt"

func main() {
	m := make(map[string]int)

	m["Answer"] = 42
	fmt.Println("The value:", m["Answer"])

	m["Answer"] = 48
	fmt.Println("The value:", m["Answer"])

	delete(m, "Answer")
	fmt.Println("The value:", m["Answer"])

	v, ok := m["Answer"]
	fmt.Println("The value:", v, "Present?", ok)
}

در اینجا خروجی چاپ برنامه است:


The value: 42
The value: 48 
The value: 0
The value: 0 Present? false 

ساختارها و روش‌ها

زبان Go فاقد کلاس است اما دارای یک struct است که دنباله ای از عناصر نامگذاری شده است که fields نامیده می شوند. هر فیلد یک نام و یک نوع دارد. روش تابعی با گیرنده است. یک اعلان method یک شناسه (نام روش) را به یک متد متصل می کند و روش را با نوع پایه گیرنده مرتبط می کند.

فریم ورک Quarkus Java افزونه‌های توسعه‌دهنده را برای مشاهده‌پذیری اضافه می‌کند

در این مثال، ما یک راس struct را اعلام می‌کنیم که حاوی دو فیلد میزان شناور، X و Y و یک متد، Abs. فیلدهایی که با حروف بزرگ شروع می شوند عمومی هستند. فیلدهایی که با حروف کوچک شروع می شوند خصوصی هستند. فیلدها و روش‌ها از طریق علامت نقطه (.) و علامت‌گذاری (&) نشانگر نشانگر هستند، مانند C. این برنامه ۵ را چاپ می‌کند.


package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	v := &Vertex{3, 4}
	fmt.Println(v.Abs())
}

رابط ها

یک نوع رابط با مجموعه ای از روش ها تعریف می شود. مقداری از نوع interface می تواند هر مقداری را که آن متدها را پیاده سازی می کند، در خود جای دهد. در این مثال بعدی، یک رابط، Abser و یک متغیر (a) از نوع Abser:

تعریف می‌کنیم.


package main

type Abser interface {
	Abs() float64
}

func main() {
	var a Abser
	f: MyFloat(-math.Sqrt2
	v = Vertex{3, 4}

	a = f // a MyFloat implements Abser 
	a = &v // a *Vertex implements Abser
	// In the following line, v is a Vertex (not *Vertex) 
	// and does NOT implement Abser. 
	a = v

	fmt.Println(a.Abs())
}

type MyFloat float64

func (f MyFloat) Abs() float64 { 
	if f < 0 {
		return float64(-f)
	}
		return float64(f)
	}

type Vertex struct { 
	X, Y float64
}

توجه داشته باشید که تکالیف a=f و a=&v کار می‌کنند، اما تخصیص a=v حتی کامپایل نمی‌شود. روش Abs از Rertex که در بخش قبل مشاهده کردید، برای گیرنده خود نشانگر به نوع Vertex دارد. بنابراین، یک *Vertex Abser را پیاده‌سازی می‌کند، اما یک Vertex این کار را نمی‌کند.

سوئیچ

عبارت switch در Go مشابه عبارت switch در سایر زبان‌های مشابه C است، با این تفاوت که عبارت‌های case می‌توانند انواع باشند. یا عبارات علاوه بر مقادیر ساده. موارد به طور خودکار شکسته می شوند مگر اینکه با عبارت fallthrough به پایان برسند. موارد به ترتیبی که تعریف شده اند ارزیابی می شوند.


package main

import (
	"fmt"
	"runtime"
)

func main() {
	fmt.Print("Go runs on ")
	switch os = runtime.GOOS; os {
	case "darwin":
		fmt.Println("macOS.")
	case "linux":
		fmt.Println("Linux.")
	default:
		// freebsd, openbsd,
		// plan9, windows...
		fmt.Printf("%s.", os)
	}
}

مجموعه ها

گوروتین‌ها اساساً رشته‌های بسیار سبک وزنی هستند که بر اساس روح فرآیندهای متوالی ارتباطی تونی هور است. در مثال زیر، خط اول func main تابع say را به صورت ناهمزمان فراخوانی می کند، در حالی که خط دوم آن را به صورت همزمان فراخوانی می کند. تفاوت در استفاده از واجد شرایط go برای گوروتین ناهمزمان است:


package main

import (
	"fmt"
	"time"
)

func say(s string) {
	for i := 0; i < 5; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
	}
}

func main() {
	go say("world")
	say("hello")
}

مجموعه‌ها، کانال‌ها و عبارت‌های select هسته اصلی همزمانی بسیار مقیاس‌پذیر Go هستند که یکی از قوی‌ترین نقاط فروش زبان است. Go همچنین دارای اشیاء همگام سازی معمولی است، اما به ندرت مورد نیاز هستند. این برنامه خروجی می دهد:


hello
world
hello
world
hello
world
hello
world
hello

کانال‌ها

کانال‌ها در Go مکانیزمی برای اجرای همزمان توابع برای برقراری ارتباط با ارسال و دریافت مقادیر از یک نوع عنصر مشخص ارائه می‌کنند. این یک مثال است:


package main

import "fmt"

func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // send sum to c
}

func main() {
	s := []int{7, 2, 8, -9, 4, 0}

	c := make(chan int)
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c // receive from c

	fmt.Println(x, y, x+y)
}

توجه داشته باشید که مقدار کانال بدون مقدار اولیه صفر است. c = make(chan int) یک کانال دو طرفه از اعداد صحیح ایجاد می کند. همچنین می‌توانیم کانال‌های ارسال یک طرفه (<-c) و دریافت (c<-) ایجاد کنیم. پس از آن، sum را به صورت ناهمزمان با برش هایی از نیمه اول و دوم a فراخوانی می کنیم. سپس، متغیرهای عدد صحیح x و y این دو جمع را از کانال دریافت می کنند. در عبارت "for _, v range a"، زیرخط (_)، شناسه خالی، به معنای نادیده گرفتن اولین مقدار نتیجه از برای است. حلقه ..range که ایندکس است. خروجی برنامه ۱۷ -۵ ۱۲ است.

شروع به کار با Avalonia UI

محدوده و بستن

در این مثال، می‌بینیم که چگونه یک فرستنده می‌تواند یک کانال را بسته تا نشان دهد که هیچ مقدار دیگری ارسال نخواهد شد. گیرنده ها می توانند با تخصیص پارامتر دوم به عبارت دریافت، بسته شدن کانال را بررسی کنند.


package main

import (
	"fmt"
)

func fibonacci(n int, c chan int) {
	x, y := 0, 1
	for i := 0; i < n; i++ {
		c <- x
		x, y = y, x+y
	}
	close(c)
}

func main() {
	c := make(chan int, 10)
	go fibonacci(cap(c), c)
	for i := range c {
		fmt.Println(i)
	}
}

حلقه for در خط سوم main (for i := محدوده c) به طور مکرر مقادیر را از کانال دریافت می کند تا زمانی که بسته cap ظرفیت کانال است که اندازه بافر در کانال است. هنگام ایجاد کانال، مانند خط اول main، cap به عنوان آرگومان دوم اختیاری تنظیم می شود. به شکل فشرده عبارات انتساب در تابع fibonacci توجه کنید. خروجی برنامه ۱۰ مقدار اول سری فیبوناچی، ۰ تا ۳۴ است.

انتخاب کنید

یک عبارت select انتخاب می‌کند که کدام یک از مجموعه‌ای از عملیات احتمالی ارسال یا دریافت ادامه یابد. به نظر شبیه یک عبارت switch است، اما همه موارد مربوط به عملیات ارتباطی است. یک select مسدود می‌شود تا زمانی که یکی از موارد آن اجرا شود، سپس آن مورد را اجرا می‌کند. اگر چند مورد آماده باشد، یکی را به‌طور تصادفی انتخاب می‌کند.


package main

import "fmt"

func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci(c, quit)
}

در اینجا، تابع main تابع fibonacci را با دو کانال بافر نشده، یکی برای نتایج و دیگری برای سیگنال خروج فراخوانی می‌کند. تابع fibonacci از عبارت select برای منتظر ماندن در هر دو کانال استفاده می کند. تابع go ناشناس و ناهمزمان که از خط سوم main شروع می‌شود، منتظر می‌ماند تا مقادیر (<-c) را دریافت کند، سپس آنها را چاپ می‌کند. پس از ۱۰ مقدار، کانال quit را تنظیم می کند، بنابراین تابع fibonacci متوقف می شود.

الگوهای همزمانی در Go

ما چند ویژگی متمایزکننده زبان Go را بررسی کرده‌ایم. حال، بیایید ببینیم که آنها چگونه با هم در مثال های برنامه نویسی کار می کنند. ما با چند الگوی همزمانی در Go شروع می‌کنیم که هر دو از سخنرانی راب پایک در سال ۲۰۱۲ در مورد الگوهای همزمانی گرفته شده‌اند. در Go.

الگوی همزمانی شماره ۱: پنکه به داخل

در این مثال، ما از select برای ایجاد یک گوروتین fan-in استفاده می کنیم که دو کانال ورودی رشته، input1 و input2، در یک کانال خروجی بافر نشده، c. عبارت select به fanIn اجازه می‌دهد تا به هر دو کانال ورودی به طور همزمان گوش دهد و هر کدام را که آماده است به کانال خروجی ارسال کند. مهم نیست که هر دو مورد از یک نام متغیر موقت برای نگه داشتن رشته از کانال های ورودی مربوطه خود استفاده می کنند.


package main

func fanIn(input1, input2 <-chan string) <-chan string {
	c := make(chan string)
	go func() {
	    for {
		 select {
		 case s := <-input1: <- s 				
		 case s := <-input2: c <- s
			}
		}
	}()
	return c
}

الگوی همزمانی شماره ۲: جستجوی موازی

این مثال همزمانی یک جستجوی موازی در اینترنت را پیاده‌سازی می‌کند، چیزی شبیه به آنچه که موتور جستجوی Google واقعاً انجام می‌دهد. برای شروع، replicas …Search یک پارامتر متغیر برای تابع است. هر دو نوع جستجو و نتیجه در جای دیگری تعریف شده اند:


package main

func First(query string, replicas ...Search) Result {
	c := make(chan Result)
	searchReplica := func(i int) { c <- replicas[i](query) }
	for i := range replicas {
		go searchReplica(i)
	}
	return <-c
}

تماس گیرنده تعداد معینی از (N) توابع سرور جستجو را به تابع First ارسال می کند. تابع First یک کانال به نام c برای نتایج ایجاد می کند و تابعی را برای پرس و جو از سرور i تعریف می کند، سپس آن را در ذخیره می کند. searchReplica. ابتدا سپس searchReplica را به صورت ناهمزمان برای همه سرورهای N فراخوانی می‌کند و همیشه پاسخ را در کانال c برمی‌گرداند. اولین نتیجه را که از سرورها باز می گردد برمی گرداند.

بسته‌ها در Go

بعد، به چند بسته نگاه می کنیم.

بسته http

بسته Go net/http، اجرای سرویس گیرنده و سرور HTTP را ارائه می‌کند. این مثال یک وب سرور ساده را پیاده سازی می کند که محتویات دایرکتوری /usr/share/doc را به یک سرویس گیرنده وب برمی گرداند:


package main

import (
	"log"
	"net/http"
)

func main() {
	// Simple static webserver:
	log.Fatal(http.ListenAndServe(":8080", http.FileServer(http.Dir("/usr/share/doc"))))
}

این مثال در محیط آنلاین Go Playground به درستی کار نمی کند. با این حال، اگر آن را روی خط فرمان Mac اجرا کنید، موارد زیر را به یک مرورگر وب باز می‌گرداند که http://localhost:8080/ را درخواست می‌کند. :

bash/
ccid/
فنجان/
groff/
ntp/
postfix/

بسته الگو

بسته html/template الگوهای مبتنی بر داده را برای تولید خروجی HTML اجرا می کند که در برابر تزریق کد ایمن است. به عنوان مثال:


package main

import "html/template"

func main() {
	t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
	err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
}

کد بالا خروجی HTML ایمن و فراری تولید می کند:


Hello, &lt;script&gt; alert(&#39; you have been pwned&#39;)&lt;/script&gt;!

بدون فرار اضافه شده توسط بسته html/template در این مثال، می‌توانستیم رشته جاوا اسکریپت قابل اجرا زیر را تولید کنیم:


Hello, <script> alert(' you have been pwned')</script>!

عمومی در Go

بله، بالاخره، Go ژنریک دارد. آنها عمیقاً در زبان ادغام شده‌اند و ایده محدودیت‌های نوع را شامل می‌شوند. این بدان معناست که پارامترهای نوع یک تابع در بین براکت ها، قبل از آرگومان های تابع ظاهر می شوند. به طور کلی، یک تابع پیاده‌سازی شده با انواع عمومی کارآمدتر از تابعی است که با نوع any پیاده‌سازی می‌شود، و نسبت به اجرای مجدد تابع برای هر نوع مورد علاقه، ضایعات کمتری دارد. دیدن گروهی از توابع در کد شما که فقط در انواع آرگومان‌هایشان متفاوت است، باید یک پرچم قرمز باشد تا بجای آن، یک نسخه عمومی بنویسید.

دو مثال زیر مربوط به بخش جدیدی از صفحه برنامه نویسی Tour of Go است.< /p>