บทช่วยสอน Golang: เรียนรู้ภาษาการเขียนโปรแกรม Go สำหรับผู้เริ่มต้น

ไปคืออะไร?

Go (เรียกอีกอย่างว่า Golang) เป็นภาษาโปรแกรมโอเพ่นซอร์สที่พัฒนาโดย Google เป็นภาษาคอมไพล์ที่มีการกำหนดประเภทแบบคงที่ Go รองรับการเขียนโปรแกรมพร้อมกัน กล่าวคือ ช่วยให้สามารถรันกระบวนการหลาย ๆ กระบวนการพร้อมกันได้ ซึ่งทำได้โดยใช้แชนเนล โกรูทีน เป็นต้น ภาษา Go มีการรวบรวมขยะซึ่งทำหน้าที่จัดการหน่วยความจำและอนุญาตให้เรียกใช้ฟังก์ชันที่รอดำเนินการ

เราจะเรียนรู้พื้นฐานทั้งหมดของ Golang ในบทช่วยสอนภาษา Learn Go นี้

วิธีดาวน์โหลดและติดตั้ง GO

ขั้นตอน 1) ไปที่ https://golang.org/dl/- ดาวน์โหลดไบนารีสำหรับระบบปฏิบัติการของคุณ

ขั้นตอน 2) Double คลิกที่ตัวติดตั้งแล้วคลิกเรียกใช้

ขั้นตอน 3) คลิกถัดไป

ขั้นตอน 4) เลือกโฟลเดอร์การติดตั้งแล้วคลิกถัดไป

ขั้นตอน 5) คลิก Finish เมื่อการติดตั้งเสร็จสมบูรณ์

ขั้นตอน 6) เมื่อการติดตั้งเสร็จสมบูรณ์ คุณสามารถตรวจสอบได้โดยเปิดเทอร์มินัลแล้วพิมพ์

go version

นี่จะแสดงเวอร์ชันของ go ที่ติดตั้ง

โปรแกรม First Go ของคุณ – Go Hello World!

สร้างโฟลเดอร์ชื่อ studyGo ในบทช่วยสอนภาษา Go นี้ เราจะสร้างโปรแกรม go ของเราภายในโฟลเดอร์นี้ ไฟล์ Go ถูกสร้างขึ้นพร้อมกับนามสกุล .ไป- คุณสามารถรันโปรแกรม Go ได้โดยใช้ไวยากรณ์

go run <filename>

สร้างไฟล์ชื่อ first.go แล้วเพิ่มโค้ดด้านล่างลงไปแล้วบันทึก

package main
import ("fmt")

func main() {
	fmt.Println("Hello World! This is my first Go program\n")
}

นำทางไปยังโฟลเดอร์นี้ในเทอร์มินัลของคุณ รันโปรแกรมโดยใช้คำสั่ง

ไปวิ่งก่อนไป

คุณสามารถดูการพิมพ์เอาท์พุตได้

Hello World! This is my first Go program

ตอนนี้เรามาหารือเกี่ยวกับโปรแกรมข้างต้น

package main – โปรแกรม Go Language ทุกโปรแกรมควรขึ้นต้นด้วยชื่อแพ็คเกจ Go ช่วยให้เราใช้แพ็คเกจในโปรแกรม go อื่นได้ และด้วยเหตุนี้จึงรองรับการนำโค้ดกลับมาใช้ใหม่ได้ การทำงานของโปรแกรม Go จะเริ่มต้นด้วยโค้ดที่อยู่ในแพ็คเกจชื่อ main

นำเข้า fmt – นำเข้าแพ็คเกจ fmt แพคเกจนี้ใช้ฟังก์ชัน I/O

func main() - นี่คือฟังก์ชันที่จุดเริ่มต้นของการทำงานของโปรแกรม ฟังก์ชั่นหลักควรอยู่ในแพ็คเกจหลักเสมอ ภายใต้ main() คุณสามารถเขียนโค้ดภายใน { } ได้

fmt.Println – จะพิมพ์ข้อความบนหน้าจอโดยฟังก์ชัน Println ของ fmt

หมายเหตุ: ในส่วนด้านล่างของบทช่วยสอน Go นี้ เมื่อคุณพูดถึงการรัน/รันโค้ด หมายความว่าให้บันทึกโค้ดในไฟล์ที่มีนามสกุล .go และรันโดยใช้ไวยากรณ์

    go run <filename>

ประเภทข้อมูล

ประเภท (ประเภทข้อมูล) แสดงถึงประเภทของค่าที่เก็บไว้ในตัวแปร ประเภทของค่าที่ฟังก์ชันส่งคืน เป็นต้น

Go Language มีสามประเภทพื้นฐาน

ประเภทตัวเลข – แสดงค่าตัวเลขซึ่งรวมถึงจำนวนเต็ม จุดทศนิยมลอยตัว และค่าเชิงซ้อน ประเภทตัวเลขต่างๆ มีดังนี้

int8 - จำนวนเต็มลงนาม 8 บิต

int16 - จำนวนเต็มลงนาม 16 บิต

int32 - จำนวนเต็มลงนาม 32 บิต

int64 - จำนวนเต็มลงนาม 64 บิต

uint8 - จำนวนเต็มที่ไม่ได้ลงนาม 8 บิต

uint16 - จำนวนเต็มที่ไม่ได้ลงนาม 16 บิต

uint32 - จำนวนเต็มที่ไม่ได้ลงนาม 32 บิต

uint64 - จำนวนเต็มที่ไม่ได้ลงนาม 64 บิต

float32 – ตัวเลขจุดลอยตัวขนาด 32 บิต

float64 – ตัวเลขจุดลอยตัวขนาด 64 บิต

complex64 – มีส่วนจริงและจินตภาพ float32

complex128 – มีส่วนจริงและจินตภาพ float32

ประเภทสตริง – แสดงถึงลำดับของไบต์ (อักขระ) คุณสามารถดำเนินการต่างๆ กับสตริงได้ เช่น การเชื่อมต่อสตริง การแยกสตริงย่อย เป็นต้น

ประเภทบูลีน – แทนค่า 2 ค่า เป็นจริงหรือเท็จ

อินเทอร์เฟซโกลัง

อินเทอร์เฟซโกลัง คือชุดของลายเซ็นวิธีการที่ใช้โดย Type เพื่อนำพฤติกรรมของออบเจ็กต์ไปใช้ เป้าหมายหลักของอินเทอร์เฟซ Golang คือการจัดหาลายเซ็นเมธอดพร้อมชื่อ อาร์กิวเมนต์ และประเภทการส่งคืน ขึ้นอยู่กับประเภทที่จะประกาศและดำเนินการวิธีการ อินเทอร์เฟซใน Golang สามารถประกาศได้โดยใช้คำหลัก “อินเทอร์เฟซ”

ตัวแปร

ตัวแปรชี้ไปที่ตำแหน่งหน่วยความจำซึ่งเก็บค่าบางประเภท พารามิเตอร์ประเภท (ในรูปแบบไวยากรณ์ด้านล่าง) แสดงถึงประเภทของค่าที่สามารถจัดเก็บไว้ในตำแหน่งหน่วยความจำ

ตัวแปรสามารถประกาศได้โดยใช้ไวยากรณ์

    var <variable_name> <type>

เมื่อคุณประกาศตัวแปรประเภทหนึ่งแล้ว คุณสามารถกำหนดตัวแปรให้กับค่าประเภทนั้น ๆ ก็ได้

คุณยังสามารถกำหนดค่าเริ่มต้นให้กับตัวแปรในระหว่างการประกาศได้โดยใช้

    var <variable_name> <type> = <value>

หากคุณประกาศตัวแปรด้วยค่าเริ่มต้น ให้อนุมานประเภทของตัวแปรจากประเภทของค่าที่กำหนด ดังนั้นคุณสามารถละเว้นประเภทในระหว่างการประกาศโดยใช้ไวยากรณ์

    var <variable_name> = <value>

นอกจากนี้ คุณสามารถประกาศตัวแปรหลายตัวโดยใช้ไวยากรณ์ได้

    var <variable_name1>, <variable_name2>  = <value1>, <value2>

โปรแกรมด้านล่างนี้ในบทช่วยสอน Go นี้มีตัวอย่าง Golang บางส่วนของการประกาศตัวแปร

 
package main
import "fmt"

func main() {
    //declaring a integer variable x
    var x int
    x=3 //assigning x the value 3 
    fmt.Println("x:", x) //prints 3
    
    //declaring a integer variable y with value 20 in a single statement and prints it
    var y int=20
    fmt.Println("y:", y)
    
    //declaring a variable z with value 50 and prints it
    //Here type int is not explicitly mentioned 
    var z=50
    fmt.Println("z:", z)
    
    //Multiple variables are assigned in single line- i with an integer and j with a string
    var i, j = 100,"hello"
    fmt.Println("i and j:", i,j)
}

ผลลัพธ์ที่ได้จะเป็น

x: 3
y: 20
z: 50
i and j: 100 hello

Go Language ยังมอบวิธีง่ายๆ ในการประกาศตัวแปรด้วยค่าโดยละเว้นการใช้คีย์เวิร์ด var

    <variable_name> := <value>

โปรดทราบว่าคุณใช้ := แทน =- คุณไม่สามารถใช้ := เพียงเพื่อกำหนดค่าให้กับตัวแปรที่ถูกประกาศไว้แล้ว := ใช้ในการประกาศและกำหนดค่า

สร้างไฟล์ชื่อ assign.go พร้อมด้วยโค้ดต่อไปนี้

package main
import ("fmt")

func main() {
	a := 20
	fmt.Println(a)

	//gives error since a is already declared
	a := 30
	fmt.Println(a)
}

ดำเนินการ go run assign.go เพื่อดูผลลัพธ์เป็น

./assign.go:7:4: no new variables on left side of :=		

ตัวแปรที่ประกาศโดยไม่มีค่าเริ่มต้นจะมี 0 สำหรับประเภทตัวเลข, false สำหรับบูลีน และสตริงว่างสำหรับสตริง

ค่าคงที่

ตัวแปรคงที่คือตัวแปรที่ไม่สามารถเปลี่ยนแปลงค่าได้เมื่อกำหนดแล้ว ค่าคงที่ในภาษาการเขียนโปรแกรม Go ถูกประกาศโดยใช้คำสำคัญ “const”

สร้างไฟล์ชื่อ constant.go และมีโค้ดดังต่อไปนี้

package main
import ("fmt")

func main() {
	const b =10
	fmt.Println(b)
	b = 30
	fmt.Println(b)
}

ดำเนินการ go run Constant.go เพื่อดูผลลัพธ์เป็น

.constant.go:7:4: cannot assign to b

สำหรับตัวอย่างวง

ลูปใช้เพื่อดำเนินการบล็อกคำสั่งซ้ำๆ ตามเงื่อนไข ภาษาโปรแกรมส่วนใหญ่มีลูป 3 ประเภท – for, while, do while แต่ภาษาการเขียนโปรแกรม Go รองรับเฉพาะการวนซ้ำเท่านั้น

ไวยากรณ์ของ Golang for loop คือ

for initialisation_expression; evaluation_expression; iteration_expression{
   // one or more statement
}

Initialisation_expression จะถูกดำเนินการก่อน (และเพียงครั้งเดียว) ใน Golang for loop

จากนั้นจะมีการประเมินการประเมินผล และหากเป็นจริง โค้ดภายในบล็อกก็จะถูกดำเนินการ

iteration_expression id ถูกดำเนินการ และ Eeeration_expression ได้รับการประเมินอีกครั้ง หากเป็นจริง บล็อกคำสั่งจะถูกดำเนินการอีกครั้ง สิ่งนี้จะดำเนินต่อไปจนกว่าการประเมินผล_การแสดงออกจะกลายเป็นเท็จ

คัดลอกโปรแกรมด้านล่างลงในไฟล์และรันเพื่อดูการพิมพ์ตัวเลขจาก 1 ถึง 5 ของ Golang

package main
import "fmt"

func main() {  
var i int
for i = 1; i <= 5; i++ {
fmt.Println(i)
    }
}

ผลลัพธ์คือ

1
2
3
4
5

ถ้าเป็นอย่างอื่น

ถ้า else เป็นคำสั่งแบบมีเงื่อนไข ไซแนกซ์คือ

if condition{
// statements_1
}else{
// statements_2
}

ที่นี่เงื่อนไขจะได้รับการประเมิน และหากเป็นจริงstatements_1 จะถูกดำเนินการ มิฉะนั้นstatement_2 จะถูกดำเนินการ

คุณสามารถใช้คำสั่ง if โดยไม่มีอย่างอื่นได้เช่นกัน คุณยังสามารถเชื่อมโยงคำสั่ง if else ได้ด้วย โปรแกรมด้านล่างนี้จะอธิบายเพิ่มเติมเกี่ยวกับกรณีอื่นๆ

ดำเนินการโปรแกรมด้านล่าง ตรวจสอบว่าตัวเลข x น้อยกว่า 10 หรือไม่ หากเป็นเช่นนั้น ระบบจะพิมพ์ “x น้อยกว่า 10”

package main
import "fmt"

func main() {  
    var x = 50
    if x < 10 {
        //Executes if x < 10
        fmt.Println("x is less than 10")
    } 
}

ในที่นี้เนื่องจากค่าของ x มากกว่า 10 คำสั่งภายในหากเงื่อนไขบล็อกจะไม่ถูกดำเนินการ

ตอนนี้ดูโปรแกรมด้านล่าง ในบทช่วยสอนภาษาการเขียนโปรแกรม Go นี้ เรามีบล็อกอื่นซึ่งจะถูกดำเนินการเมื่อล้มเหลวในการประเมินหาก

package main
import "fmt"

func main() {  
    var x = 50
    if x < 10 {
        //Executes if x is less than 10
        fmt.Println("x is less than 10")
    } else {
        //Executes if x >= 10
        fmt.Println("x is greater than or equals 10")
    }
}

โปรแกรมนี้จะให้ผลลัพธ์แก่คุณ

x is greater than or equals 10

ในบทช่วยสอน Go นี้ เราจะเห็นโปรแกรมที่มีบล็อก if else หลายบล็อก (chained if else) ดำเนินการตัวอย่าง Go ด้านล่าง ตรวจสอบว่าตัวเลขน้อยกว่า 10 หรืออยู่ระหว่าง 10-90 หรือมากกว่า 90

package main
import "fmt"

func main() {  
    var x = 100
    if x < 10 {
        //Executes if x is less than 10
        fmt.Println("x is less than 10")
    } else if x >= 10 && x <= 90 {
        //Executes if x >= 10 and x<=90
        fmt.Println("x is between 10 and 90")
    } else {
        //Executes if both above cases fail i.e x>90
        fmt.Println("x is greater than 90")
    }
}

ก่อนอื่นเงื่อนไข if จะตรวจสอบว่า x น้อยกว่า 10 หรือไม่ ดังนั้นจึงตรวจสอบเงื่อนไขถัดไป (ถ้าเป็นอย่างอื่น) ว่าอยู่ระหว่าง 10 ถึง 90 ซึ่งเป็นเท็จด้วย ดังนั้นมันจึงดำเนินการบล็อกภายใต้ส่วน else ซึ่งให้เอาต์พุต

x is greater than 90

สวิตซ์

Switch เป็นอีกหนึ่งคำสั่งที่มีเงื่อนไข คำสั่ง Switch จะประเมินนิพจน์และผลลัพธ์จะถูกเปรียบเทียบกับชุดของค่าที่มีอยู่ (กรณี) เมื่อพบการจับคู่แล้ว คำสั่งที่เกี่ยวข้องกับการจับคู่นั้น (กรณี) จะถูกดำเนินการ หากไม่พบการแข่งขัน จะไม่มีการดำเนินการใดๆ คุณยังสามารถเพิ่มกรณีเริ่มต้นเพื่อสลับได้ ซึ่งจะดำเนินการหากไม่พบรายการที่ตรงกันอื่นๆ ไวยากรณ์ของสวิตช์คือ

switch expression {
    case value_1:
        statements_1
    case value_2:
        statements_2
    case value_n:
        statements_n
    default:
        statements_default
    }

ที่นี่ค่าของนิพจน์จะถูกเปรียบเทียบกับค่าในแต่ละกรณี เมื่อพบการจับคู่ ข้อความที่เกี่ยวข้องกับกรณีนั้นจะถูกดำเนินการ หากไม่พบรายการที่ตรงกัน คำสั่งภายใต้ส่วนเริ่มต้นจะถูกดำเนินการ

ดำเนินการโปรแกรมด้านล่าง

package main
import "fmt"

func main() {  
    a,b := 2,1
    switch a+b {
    case 1:
        fmt.Println("Sum is 1")
    case 2:
        fmt.Println("Sum is 2")
    case 3:
        fmt.Println("Sum is 3")
    default:
        fmt.Println("Printing default")
    }
}

คุณจะได้ผลลัพธ์เป็น

Sum is 3		

เปลี่ยนค่าของ a และ b เป็น 3 และผลลัพธ์จะเป็น

Printing default

คุณยังสามารถมีหลายค่าในกรณีโดยคั่นด้วยเครื่องหมายจุลภาค

อาร์เรย์

อาร์เรย์แสดงถึงขนาดคงที่ โดยตั้งชื่อลำดับขององค์ประกอบประเภทเดียวกัน คุณไม่สามารถมีอาร์เรย์ที่มีทั้งจำนวนเต็มและอักขระอยู่ได้ คุณไม่สามารถเปลี่ยนขนาดของอาร์เรย์ได้เมื่อคุณกำหนดขนาดแล้ว

ไวยากรณ์สำหรับการประกาศอาร์เรย์คือ

var arrayname [size] type

แต่ละองค์ประกอบอาร์เรย์สามารถกำหนดค่าได้โดยใช้ไวยากรณ์

arrayname [index] = value

ดัชนีอาร์เรย์เริ่มต้นจาก 0 ถึงขนาด-1.

คุณสามารถกำหนดค่าให้กับองค์ประกอบอาร์เรย์ในระหว่างการประกาศโดยใช้ไวยากรณ์

arrayname := [size] type {value_0,value_1,…,value_size-1} 

คุณยังสามารถละเว้นพารามิเตอร์ขนาดในขณะที่ประกาศอาร์เรย์ด้วยค่าโดยการแทนที่ขนาดด้วย ... และคอมไพลเลอร์จะค้นหาความยาวจากจำนวนค่า ไวยากรณ์คือ

arrayname :=  […] type {value_0,value_1,…,value_size-1}

คุณสามารถค้นหาความยาวของอาร์เรย์ได้โดยใช้ไวยากรณ์

len(arrayname)

ดำเนินการตัวอย่าง Go ด้านล่างเพื่อทำความเข้าใจอาร์เรย์

package main
import "fmt"

func main() {  
    var numbers [3] string //Declaring a string array of size 3 and adding elements 
    numbers[0] = "One"
    numbers[1] = "Two"
    numbers[2] = "Three"
    fmt.Println(numbers[1]) //prints Two
    fmt.Println(len(numbers)) //prints 3
    fmt.Println(numbers) // prints [One Two Three]

    directions := [...] int {1,2,3,4,5} // creating an integer array and the size of the array is defined by the number of elements 
    fmt.Println(directions) //prints [1 2 3 4 5]
    fmt.Println(len(directions)) //prints 5

    //Executing the below commented statement prints invalid array index 5 (out of bounds for 5-element array)
    //fmt.Println(directions[5]) 
}

เอาท์พุต

Two
3
[One Two Three]
[1 2 3 4 5]
5

Golang Slice และผนวกฟังก์ชัน

สไลซ์คือส่วนหรือส่วนของอาร์เรย์ หรือเป็นมุมมองหรือมุมมองบางส่วนของอาร์เรย์ต้นแบบที่ชี้ไป คุณสามารถเข้าถึงองค์ประกอบของสไลซ์ได้โดยใช้ชื่อสไลซ์และหมายเลขดัชนีเช่นเดียวกับที่คุณทำในอาร์เรย์ คุณไม่สามารถเปลี่ยนความยาวของอาร์เรย์ได้ แต่คุณสามารถเปลี่ยนขนาดของชิ้นได้

จริงๆ แล้วเนื้อหาของสไลซ์เป็นตัวชี้ไปยังองค์ประกอบของอาร์เรย์ มันหมายถึง หากคุณเปลี่ยนองค์ประกอบใด ๆ ในชิ้นเนื้อหาอาร์เรย์ที่ซ่อนอยู่จะได้รับผลกระทบเช่นกัน

ไวยากรณ์สำหรับการสร้างชิ้นคือ

var slice_name [] type = array_name[start:end]

สิ่งนี้จะสร้างชิ้นชื่อ Slice_name จากอาร์เรย์ชื่อ array_name พร้อมด้วยองค์ประกอบที่ดัชนีเริ่มต้นถึงจุดสิ้นสุด 1

ในบทช่วยสอน Golang นี้ เราจะรันโปรแกรมด้านล่าง โปรแกรมจะสร้างสไลซ์จากอาเรย์แล้วพิมพ์ออกมา นอกจากนี้ คุณจะเห็นว่าการแก้ไขเนื้อหาในส่วนนั้นจะแก้ไขอาร์เรย์จริงด้วย

package main
import "fmt"

func main() {  
    // declaring array
    a := [5] string {"one", "two", "three", "four", "five"}
    fmt.Println("Array after creation:",a)

    var b [] string = a[1:4] //created a slice named b
    fmt.Println("Slice after creation:",b)

    b[0]="changed" // changed the slice data
    fmt.Println("Slice after modifying:",b)
    fmt.Println("Array after slice modification:",a)
}

ซึ่งจะพิมพ์ผลลัพธ์เป็น

Array after creation: [one two three four five]
Slice after creation: [two three four]
Slice after modifying: [changed three four]
Array after slice modification: [one changed three four five]

มีฟังก์ชันบางอย่างเช่น Golang len, Golang ต่อท้าย ซึ่งคุณสามารถใช้กับสไลซ์ได้

เลน (slice_name) – ส่งกลับความยาวของชิ้น

ต่อท้าย (slice_name, value_1, value_2) – Golang append ใช้เพื่อผนวก value_1 และ value_2 เข้ากับสไลซ์ที่มีอยู่

ผนวก (slice_nale1,slice_name2…) – ต่อท้าย Slice_name2 ถึง Slice_name1

ดำเนินการโปรแกรมต่อไปนี้

package main
import "fmt"

func main() {  
	a := [5] string {"1","2","3","4","5"}
	slice_a := a[1:3]
	b := [5] string {"one","two","three","four","five"}
	slice_b := b[1:3]

    fmt.Println("Slice_a:", slice_a)
    fmt.Println("Slice_b:", slice_b)
    fmt.Println("Length of slice_a:", len(slice_a))
    fmt.Println("Length of slice_b:", len(slice_b))

    slice_a = append(slice_a,slice_b...) // appending slice
    fmt.Println("New Slice_a after appending slice_b :", slice_a)
    
    slice_a = append(slice_a,"text1") // appending value
    fmt.Println("New Slice_a after appending text1 :", slice_a)
}

ผลลัพธ์ที่ได้จะเป็น

Slice_a: [2 3]
Slice_b: [two three]
Length of slice_a: 2
Length of slice_b: 2
New Slice_a after appending slice_b : [2 3 two three]
New Slice_a after appending text1 : [2 3 two three text1]

ขั้นแรกโปรแกรมจะสร้าง 2 ชิ้นแล้วพิมพ์ความยาว จากนั้นจึงต่อท้ายชิ้นหนึ่งเข้ากับอีกชิ้นหนึ่ง จากนั้นจึงต่อท้ายสตริงเข้ากับชิ้นผลลัพธ์

ฟังก์ชั่น

ฟังก์ชันแสดงถึงกลุ่มคำสั่งที่ทำงานเฉพาะ การประกาศฟังก์ชันจะบอกชื่อฟังก์ชัน ประเภทการส่งคืน และพารามิเตอร์อินพุตให้เราทราบ คำจำกัดความของฟังก์ชันแสดงถึงรหัสที่มีอยู่ในฟังก์ชัน ไวยากรณ์สำหรับการประกาศฟังก์ชันคือ

func function_name(parameter_1 type, parameter_n type) return_type {
//statements
}

พารามิเตอร์และประเภทการส่งคืนเป็นทางเลือก นอกจากนี้คุณยังสามารถคืนค่าหลายค่าจากฟังก์ชันได้อีกด้วย

ในบทช่วยสอน Golang นี้ เราจะลองเรียกใช้ตัวอย่าง Golang ต่อไปนี้ ฟังก์ชันชื่อ calc จะรับตัวเลข 2 ตัวและดำเนินการบวกและลบ และส่งคืนค่าทั้งสองค่า

package main
import "fmt"

//calc is the function name which accepts two integers num1 and num2
//(int, int) says that the function returns two values, both of integer type.
func calc(num1 int, num2 int)(int, int) {  
    sum := num1 + num2
    diff := num1 - num2
    return sum, diff
}

func main() {  
    x,y := 15,10

    //calls the function calc with x and y an d gets sum, diff as output
    sum, diff := calc(x,y) 
    fmt.Println("Sum",sum)
    fmt.Println("Diff",diff) 
}

ผลลัพธ์ที่ได้จะเป็น

Sum 25
Diff 5

แบบรวดเร็ว

แพ็คเกจใช้ในการจัดระเบียบโค้ด ในโครงการขนาดใหญ่ ไม่สามารถเขียนโค้ดเป็นไฟล์เดียวได้ Go Programming Language ช่วยให้เราสามารถจัดระเบียบโค้ดตามแพ็คเกจต่างๆ สิ่งนี้จะเพิ่มความสามารถในการอ่านโค้ดและการนำกลับมาใช้ใหม่ได้ โปรแกรม Go ที่ปฏิบัติการได้ควรมีแพ็คเกจชื่อ main และการทำงานของโปรแกรมเริ่มต้นจากฟังก์ชันชื่อ main คุณสามารถนำเข้าแพ็คเกจอื่นๆ ในโปรแกรมของเราได้โดยใช้ไวยากรณ์

import package_name

เราจะดูและหารือกันในบทช่วยสอน Golang นี้เกี่ยวกับวิธีการสร้างและใช้งานแพ็คเกจในตัวอย่าง Golang ต่อไปนี้

ขั้นตอน 1) สร้างไฟล์ชื่อ package_example.go และเพิ่มโค้ดด้านล่าง

package main
import "fmt"
//the package to be created
import "calculation"

func main() {  
	x,y := 15,10
	//the package will have function Do_add()
sum := calculation.Do_add(x,y)
fmt.Println("Sum",sum) 
}

ในโปรแกรม fmt ข้างต้นเป็นแพ็คเกจที่ภาษาการเขียนโปรแกรม Go มอบให้เราเพื่อวัตถุประสงค์ I/O เป็นหลัก นอกจากนี้คุณยังสามารถดูแพ็คเกจที่ชื่อการคำนวณได้ ภายใน main() คุณจะเห็นผลรวมขั้นตอน := การคำนวณDo_add(x,y) หมายความว่าคุณกำลังเรียกใช้ฟังก์ชัน Do_add จากการคำนวณแพ็คเกจ

ขั้นตอน 2) ขั้นแรก คุณควรสร้างการคำนวณแพ็คเกจภายในโฟลเดอร์ที่มีชื่อเดียวกันภายใต้โฟลเดอร์ src of the go เส้นทางที่ติดตั้งของ go สามารถพบได้จากตัวแปร PATH

สำหรับ Mac ให้ค้นหาเส้นทางโดยดำเนินการ echo $PATH

ดังนั้นเส้นทางคือ /usr/local/go

สำหรับ Windows ให้ค้นหาเส้นทางโดยดำเนินการ echo %GOROOT%

นี่คือเส้นทางคือ C:\Go\

ขั้นตอน 3) ไปที่โฟลเดอร์ src (/usr/local/go/src สำหรับ mac และ C:\Go\src สำหรับ windows) ตอนนี้จากโค้ด ชื่อแพ็กเกจคือการคำนวณ Go ต้องการให้แพ็กเกจอยู่ในไดเร็กทอรีที่มีชื่อเดียวกันภายใต้ไดเร็กทอรี src สร้างไดเร็กทอรีชื่อการคำนวณในโฟลเดอร์ src

ขั้นตอน 4) สร้างไฟล์ชื่อ calc.go (คุณสามารถตั้งชื่ออะไรก็ได้ แต่ชื่อแพ็คเกจในโค้ดมีความสำคัญ นี่ควรเป็นการคำนวณ) ภายในไดเร็กทอรีการคำนวณและเพิ่มโค้ดด้านล่าง

package calculation
  
func Do_add(num1 int, num2 int)(int) {
    sum := num1 + num2
    return sum
}

ขั้นตอน 5) รันคำสั่ง go install จากไดเร็กทอรีการคำนวณซึ่งจะคอมไพล์ calc.go

ขั้นตอน 6) ตอนนี้กลับไปที่ package_example.go แล้วรัน go run package_example.go ผลลัพธ์จะเป็นผลรวม 25

โปรดทราบว่าชื่อของฟังก์ชัน Do_add ขึ้นต้นด้วยตัวพิมพ์ใหญ่ เนื่องจากในภาษา Go หากชื่อฟังก์ชันขึ้นต้นด้วยตัวพิมพ์ใหญ่ หมายความว่าโปรแกรมอื่นสามารถดู(เข้าถึง) ได้ ส่วนโปรแกรมอื่นไม่สามารถเข้าถึงได้ หากชื่อฟังก์ชันเป็น do_add คุณก็จะได้รับข้อผิดพลาด

ไม่สามารถอ้างถึงการคำนวณชื่อที่ไม่ได้ส่งออก.calc..

เลื่อนและซ้อนเลื่อน

คำสั่ง Defer ใช้เพื่อเลื่อนการดำเนินการของการเรียกใช้ฟังก์ชันจนกว่าฟังก์ชันที่มีคำสั่ง defer จะดำเนินการเสร็จสิ้น

มาเรียนรู้สิ่งนี้ด้วยตัวอย่าง:

package main
import "fmt"

func sample() {  
    fmt.Println("Inside the sample()")
}
func main() {  
    //sample() will be invoked only after executing the statements of main()
    defer sample()
    fmt.Println("Inside the main()")
}

ผลลัพธ์ที่ได้จะเป็น

Inside the main()
Inside the sample()

ที่นี่การดำเนินการของตัวอย่าง() จะถูกเลื่อนออกไปจนกว่าการดำเนินการของฟังก์ชันการปิดล้อม (main()) จะเสร็จสมบูรณ์

การเลื่อนการซ้อนใช้คำสั่งเลื่อนหลายรายการ สมมติว่าคุณมีคำสั่งเลื่อนหลายคำสั่งภายในฟังก์ชัน Go วางการเรียกฟังก์ชันที่เลื่อนออกไปทั้งหมดไว้ในสแต็ก และเมื่อฟังก์ชันปิดล้อมกลับมา ฟังก์ชันที่ซ้อนกันจะถูกดำเนินการใน เข้าครั้งสุดท้ายออกก่อน (LIFO) คุณสามารถดูสิ่งนี้ได้ในตัวอย่างด้านล่าง

ดำเนินการโค้ดด้านล่าง

package main
import "fmt"

func display(a int) {  
    fmt.Println(a)
}
func main() {  
    defer display(1)
    defer display(2)
    defer display(3)
    fmt.Println(4)
}

ผลลัพธ์ที่ได้จะเป็น

4
3
2
1			

ที่นี่โค้ดภายใน main() จะดำเนินการก่อน จากนั้นการเรียกใช้ฟังก์ชันที่เลื่อนออกไปจะดำเนินการในลำดับย้อนกลับ เช่น 4, 3,2,1

ชี้

ก่อนจะอธิบายตัวชี้ เรามาพูดถึงตัวดำเนินการ '&' ก่อน ตัวดำเนินการ '&' ใช้เพื่อรับที่อยู่ของตัวแปร ซึ่งหมายความว่า '&a' จะพิมพ์ที่อยู่หน่วยความจำของตัวแปร a

ในบทช่วยสอน Golang นี้ เราจะรันโปรแกรมด้านล่างเพื่อแสดงค่าของตัวแปรและที่อยู่ของตัวแปรนั้น

package main
import "fmt"

func main() {
	a := 20
	fmt.Println("Address:",&a)
	fmt.Println("Value:",a)
}

ผลจะเป็นอย่างไร

Address: 0xc000078008
Value: 20

ตัวแปรพอยน์เตอร์เก็บที่อยู่หน่วยความจำของตัวแปรอื่น คุณสามารถกำหนดตัวชี้โดยใช้ไวยากรณ์

	var variable_name *type

เครื่องหมายดอกจัน(*) หมายถึงตัวแปรที่เป็นตัวชี้ คุณจะเข้าใจมากขึ้นโดยรันโปรแกรมด้านล่าง

package main
import "fmt"

func main() {
	//Create an integer variable a with value 20
	a := 20
	
	//Create a pointer variable b and assigned the address of a
	var b *int = &a

	//print address of a(&a) and value of a  
	fmt.Println("Address of a:",&a)
	fmt.Println("Value of a:",a)

	//print b which contains the memory address of a i.e. &a
	fmt.Println("Address of pointer b:",b)

	//*b prints the value in memory address which b contains i.e. the value of a
	fmt.Println("Value of pointer b",*b)

	//increment the value of variable a using the variable b
	*b = *b+1

	//prints the new value using a and *b
	fmt.Println("Value of pointer b",*b)
	fmt.Println("Value of a:",a)}

ผลลัพธ์ที่ได้จะเป็น

Address of a: 0x416020
Value of a: 20
Address of pointer b: 0x416020
Value of pointer b 20
Value of pointer b 21
Value of a: 21

โครงสร้าง

โครงสร้างเป็นประเภทข้อมูลที่ผู้ใช้กำหนดซึ่งมีองค์ประกอบมากกว่าหนึ่งองค์ประกอบที่เป็นประเภทเดียวกันหรือต่างกัน

การใช้โครงสร้างเป็นกระบวนการ 2 ขั้นตอน

ขั้นแรก สร้าง (ประกาศ) ประเภทโครงสร้าง

ประการที่สอง สร้างตัวแปรประเภทนั้นเพื่อเก็บค่า

โครงสร้างส่วนใหญ่จะใช้เมื่อคุณต้องการจัดเก็บข้อมูลที่เกี่ยวข้องไว้ด้วยกัน

พิจารณาข้อมูลพนักงานซึ่งมีชื่อ อายุ และที่อยู่ คุณสามารถจัดการเรื่องนี้ได้ 2 วิธี

สร้างอาร์เรย์ 3 ชุด โดยชุดหนึ่งเก็บชื่อของพนักงาน ชุดหนึ่งเก็บอายุ และชุดที่สามเก็บอายุ

ประกาศประเภทโครงสร้างด้วย 3 ฟิลด์ ได้แก่ ชื่อ ที่อยู่ และอายุ สร้างอาร์เรย์ของประเภทโครงสร้างนั้นโดยที่แต่ละองค์ประกอบเป็นวัตถุโครงสร้างที่มีชื่อ ที่อยู่ และอายุ

แนวทางแรกไม่มีประสิทธิภาพ ในสถานการณ์ประเภทนี้ โครงสร้างจะสะดวกกว่า

ไวยากรณ์สำหรับการประกาศโครงสร้างคือ

type structname struct {
   variable_1 variable_1_type
   variable_2 variable_2_type
   variable_n variable_n_type
}

ตัวอย่างของการประกาศโครงสร้างคือ

type emp struct {
    name string
    address string
    age int
}

นี่คือประเภทที่ผู้ใช้กำหนดใหม่ชื่อ emp ถูกสร้างขึ้น ตอนนี้คุณสามารถสร้างตัวแปรประเภท emp โดยใช้ไวยากรณ์ได้

	var variable_name struct_name

ตัวอย่างคือ

var empdata1 emp 

คุณสามารถตั้งค่าสำหรับ empdata1 เป็นได้

empdata1.name = "John"
	empdata1.address = "Street-1, Bangalore"
	empdata1.age = 30

คุณยังสามารถสร้างตัวแปรโครงสร้างและกำหนดค่าได้โดย

empdata2 := emp{"Raj", "Building-1, Delhi", 25}

ที่นี่คุณจะต้องรักษาลำดับขององค์ประกอบ Raj จะถูกแมปกับชื่อ องค์ประกอบถัดไปของที่อยู่ และองค์ประกอบสุดท้ายตามอายุ

ดำเนินการโค้ดด้านล่าง

package main
import "fmt"

//declared the structure named emp
type emp struct {
        name string
        address string
        age int
}       

//function which accepts variable of emp type and prints name property
func display(e emp) {
          fmt.Println(e.name)
}

func main() {
// declares a variable, empdata1, of the type emp
var empdata1 emp
//assign values to members of empdata1
empdata1.name = "John"
empdata1.address = "Street-1, London"
empdata1.age = 30

//declares and assign values to variable empdata2 of type emp
empdata2 := emp{"Raj", "Building-1, Paris", 25}

//prints the member name of empdata1 and empdata2 using display function
display(empdata1)
display(empdata2)
}

เอาท์พุต

John
Raj

วิธีการ (ไม่ใช่ฟังก์ชัน)

วิธีการคือฟังก์ชันที่มีอาร์กิวเมนต์ตัวรับ Archiโดยทางเทคนิคแล้ว มันอยู่ระหว่างคีย์เวิร์ด func และชื่อเมธอด ไวยากรณ์ของวิธีการคือ

func (variable variabletype) methodName(parameter1 paramether1type) {  
}

ลองแปลงโปรแกรมตัวอย่างข้างต้นให้ใช้วิธีการแทนฟังก์ชัน

package main
import "fmt"

//declared the structure named emp
type emp struct {
    name string
    address string
    age int
}

//Declaring a function with receiver of the type emp
func(e emp) display() {
    fmt.Println(e.name)
}

func main() {
    //declaring a variable of type emp
    var empdata1 emp
    
    //Assign values to members
    empdata1.name = "John"
    empdata1.address = "Street-1, Lodon"
    empdata1.age = 30

    //declaring a variable of type emp and assign values to members
    empdata2 := emp {
        "Raj", "Building-1, Paris", 25}

    //Invoking the method using the receiver of the type emp
   // syntax is variable.methodname()
    empdata1.display()
    empdata2.display()
}

Go ไม่ใช่ภาษาเชิงวัตถุและไม่มีแนวคิดเรื่องคลาส เมธอดให้ความรู้สึกถึงสิ่งที่คุณทำในโปรแกรมเชิงวัตถุ โดยที่ฟังก์ชันของคลาสถูกเรียกใช้โดยใช้ไวยากรณ์ objectname.functionname()

เห็นพ้องด้วย

Go รองรับการดำเนินการงานพร้อมกัน ซึ่งหมายความว่า Go สามารถดำเนินการงานหลายงานพร้อมกันได้ ซึ่งแตกต่างจากแนวคิดการทำงานแบบคู่ขนาน ในการทำงานแบบคู่ขนาน งานจะถูกแยกออกเป็นงานย่อยๆ และดำเนินการแบบคู่ขนาน แต่ในการทำงานพร้อมกัน งานหลายงานจะถูกดำเนินการพร้อมกัน การทำงานพร้อมกันทำได้โดยใช้ Goroutines และ Channels ใน Go

กรูทีน

goroutine คือฟังก์ชันที่สามารถทำงานพร้อมกันกับฟังก์ชันอื่นๆ ได้ โดยปกติแล้วเมื่อมีการเรียกใช้ฟังก์ชัน การควบคุมจะถูกถ่ายโอนไปยังฟังก์ชันที่ถูกเรียกใช้ และเมื่อการควบคุมการดำเนินการเสร็จสมบูรณ์แล้วจะกลับสู่ฟังก์ชันการเรียก ฟังก์ชันการโทรจะดำเนินการต่อไป ฟังก์ชันการเรียกจะรอให้ฟังก์ชันที่เรียกใช้ดำเนินการให้เสร็จสิ้นก่อนที่จะดำเนินการกับคำสั่งที่เหลือ

แต่ในกรณีของ goroutine ฟังก์ชันการเรียกใช้จะไม่รอให้การดำเนินการของฟังก์ชันที่เรียกใช้เสร็จสมบูรณ์ จะดำเนินการต่อไปในข้อความถัดไป คุณสามารถมีหลาย goroutine ในโปรแกรมได้

นอกจากนี้ โปรแกรมหลักจะออกเมื่อดำเนินการคำสั่งเสร็จสิ้น และจะไม่รอให้กอร์รูทีนที่เรียกใช้เสร็จสมบูรณ์

Goroutine ถูกเรียกใช้โดยใช้คีย์เวิร์ด go ตามด้วยการเรียกใช้ฟังก์ชัน

ตัวอย่าง

go add(x,y)

คุณจะเข้าใจ goroutines ด้วยตัวอย่าง Golang ด้านล่าง ดำเนินการโปรแกรมด้านล่าง

package main
import "fmt"
    
func display() {
	for i:=0; i<5; i++ {
		fmt.Println("In display")
	}
}

func main() {
	//invoking the goroutine display()
	go display()
	//The main() continues without waiting for display()
	for i:=0; i<5; i++ {
		fmt.Println("In main")
	}
}

ผลลัพธ์ที่ได้จะเป็น

In main
In main
In main
In main
In main

ที่นี่โปรแกรมหลักดำเนินการเสร็จสิ้นก่อนที่ goroutine จะเริ่มต้นเสียอีก display() เป็น goroutine ที่ถูกเรียกใช้โดยใช้ไวยากรณ์

go function_name(parameter list)

ในโค้ดด้านบน main() จะไม่รอให้ display() เสร็จสิ้น และ main() ดำเนินการเสร็จสิ้นก่อนที่ display() จะดำเนินการโค้ด ดังนั้นคำสั่งพิมพ์ภายใน display() จึงไม่ได้รับการพิมพ์

ตอนนี้เราแก้ไขโปรแกรมเพื่อพิมพ์คำสั่งจาก display() ด้วยเช่นกัน เราเพิ่มการหน่วงเวลา 2 วินาทีใน for loop ของ main() และการหน่วงเวลา 1 วินาทีใน for loop ของ display()

package main
import "fmt"
import "time"
    
func display() {
	for i:=0; i<5; i++ {
		time.Sleep(1 * time.Second)
		fmt.Println("In display")
	}
}

func main() {
	//invoking the goroutine display()
	go display()
	for i:=0; i<5; i++ {
		time.Sleep(2 * time.Second)
		fmt.Println("In main")
	}
}

ผลลัพธ์จะค่อนข้างคล้ายกันกับ

In display
In main
In display
In display
In main
In display
In display
In main
In main
In main

ที่นี่ คุณจะเห็นว่าทั้งสองลูปกำลังดำเนินการในลักษณะที่ทับซ้อนกันเนื่องจากมีการดำเนินการพร้อมกัน

ช่องทาง

ช่องสัญญาณเป็นวิธีหนึ่งสำหรับฟังก์ชันในการสื่อสารระหว่างกัน ถือได้ว่าเป็นสื่อกลางในการที่รูทีนหนึ่งวางข้อมูลและเข้าถึงได้โดยรูทีนอื่นในเซิร์ฟเวอร์ Golang

สามารถประกาศช่องสัญญาณได้ด้วยไวยากรณ์

channel_variable := make(chan datatype)

ตัวอย่าง:

	ch := make(chan int)

คุณสามารถส่งข้อมูลไปยังช่องทางโดยใช้ไวยากรณ์

channel_variable <- variable_name

ตัวอย่าง

    ch <- x

คุณสามารถรับข้อมูลจากช่องโดยใช้ไวยากรณ์

    variable_name := <- channel_variable

ตัวอย่าง

   y := <- ch

ในตัวอย่างภาษา Go ของ goroutine ข้างต้น คุณคงเห็นว่าโปรแกรมหลักไม่รอ goroutine แต่นั่นไม่ใช่กรณีที่ช่องเข้ามาเกี่ยวข้อง สมมติว่าถ้า goroutine ส่งข้อมูลไปยังช่องสัญญาณ main() จะรอคำสั่งรับข้อมูลช่องสัญญาณจนกว่าจะได้รับข้อมูล

คุณจะเห็นสิ่งนี้ในตัวอย่างภาษา Go ด้านล่าง ขั้นแรก เขียน goroutine ปกติและดูพฤติกรรม แล้วปรับเปลี่ยนโปรแกรมให้ใช้ช่องสัญญาณและดูพฤติกรรม

ดำเนินการโปรแกรมด้านล่าง

package main
import "fmt"
import "time"
    
func display() {
	time.Sleep(5 * time.Second)
	fmt.Println("Inside display()")
}

func main() {
	go display()
	fmt.Println("Inside main()")
}

ผลลัพธ์ที่ได้จะเป็น

Inside main()

main() ดำเนินการเสร็จสิ้นและออกก่อนที่ goroutine จะดำเนินการ ดังนั้นการพิมพ์ภายใน display() จึงไม่ได้รับการดำเนินการ

ตอนนี้แก้ไขโปรแกรมด้านบนเพื่อใช้ช่องสัญญาณและดูพฤติกรรม

package main
import "fmt"
import "time"
    
func display(ch chan int) {
	time.Sleep(5 * time.Second)
	fmt.Println("Inside display()")
	ch <- 1234
}

func main() {
	ch := make(chan int) 
	go display(ch)
	x := <-ch
	fmt.Println("Inside main()")
	fmt.Println("Printing x in main() after taking from channel:",x)
}

ผลลัพธ์ที่ได้จะเป็น

Inside display()
Inside main()
Printing x in main() after taking from channel: 1234

สิ่งที่เกิดขึ้นคือ main() เมื่อไปถึง x := <-ch จะรอข้อมูลบนช่อง ch จอแสดงผล () ต้องรอ 5 วินาทีแล้วจึงส่งข้อมูลไปที่ช่อง ch main() ในการรับข้อมูลจากช่องสัญญาณจะถูกปลดบล็อกและดำเนินการต่อไป

ผู้ส่งที่ส่งข้อมูลไปยังช่องสามารถแจ้งผู้รับว่าจะไม่มีการเพิ่มข้อมูลลงในช่องอีกต่อไปโดยการปิดช่อง ส่วนใหญ่จะใช้เมื่อคุณใช้ลูปเพื่อส่งข้อมูลไปยังช่องสัญญาณ สามารถปิดช่องได้โดยใช้

close(channel_name)

และด้านตัวรับสัญญาณสามารถตรวจสอบได้ว่าช่องถูกปิดโดยใช้ตัวแปรเพิ่มเติมในขณะดึงข้อมูลจากช่องโดยใช้

variable_name, status := <- channel_variable

หากสถานะเป็น True หมายความว่าคุณได้รับข้อมูลจากช่องสัญญาณ หากเป็นเท็จ แสดงว่าคุณกำลังพยายามอ่านจากช่องปิด

คุณยังสามารถใช้ช่องทางในการสื่อสารระหว่างโกรูทีนได้ จำเป็นต้องใช้ 2 goroutines - อันหนึ่งส่งข้อมูลไปที่ช่องสัญญาณและอีกอันรับข้อมูลจากช่อง ดูโปรแกรมด้านล่างนี้

package main
import "fmt"
import "time"

//This subroutine pushes numbers 0 to 9 to the channel and closes the channel
func add_to_channel(ch chan int) {	
	fmt.Println("Send data")
	for i:=0; i<10; i++ {
		ch <- i //pushing data to channel
	}
	close(ch) //closing the channel

}

//This subroutine fetches data from the channel and prints it.
func fetch_from_channel(ch chan int) {
	fmt.Println("Read data")
	for {
		//fetch data from channel
x, flag := <- ch

		//flag is true if data is received from the channel
//flag is false when the channel is closed
if flag == true {
			fmt.Println(x)
		}else{
			fmt.Println("Empty channel")
			break	
		}	
	}
}

func main() {
	//creating a channel variable to transport integer values
	ch := make(chan int)

	//invoking the subroutines to add and fetch from the channel
	//These routines execute simultaneously
	go add_to_channel(ch)
	go fetch_from_channel(ch)

	//delay is to prevent the exiting of main() before goroutines finish
	time.Sleep(5 * time.Second)
	fmt.Println("Inside main()")
}

ที่นี่มีซับรูทีน 2 ซับรูทีน ซับรูทีนหนึ่งส่งข้อมูลไปยังช่อง และซับรูทีนอื่นพิมพ์ข้อมูลไปยังช่อง ฟังก์ชัน add_to_channel จะเพิ่มตัวเลขตั้งแต่ 0 ถึง 9 และปิดช่อง พร้อมกันนั้น ฟังก์ชัน fetch_from_channel จะรอที่

x, flag := <- ch และเมื่อมีข้อมูลก็จะพิมพ์ข้อมูล มันจะออกเมื่อแฟล็กเป็นเท็จซึ่งหมายความว่าช่องถูกปิด

ให้รอใน main() เพื่อป้องกันไม่ให้ออกจาก main() จนกว่า goroutines จะเสร็จสิ้นการดำเนินการ

รันโค้ดและดูผลลัพธ์เป็น

Read data
Send data
0
1
2
3
4
5
6
7
8
9
Empty channel
Inside main()

เลือก

Select สามารถดูได้ว่าเป็นคำสั่งสวิตช์ที่ทำงานบนช่องสัญญาณ ในที่นี้ คำสั่ง case จะเป็นการดำเนินการของช่องสัญญาณ โดยปกติ คำสั่ง case แต่ละคำสั่งจะเป็นความพยายามอ่านจากช่องสัญญาณ เมื่อ case ใด ๆ พร้อมแล้ว (ช่องสัญญาณได้รับการอ่าน) คำสั่งที่เกี่ยวข้องกับ case นั้นจะถูกดำเนินการ หากมี case หลาย ๆ อันพร้อม ระบบจะเลือก case แบบสุ่ม คุณสามารถมี case เริ่มต้นซึ่งจะถูกดำเนินการหากไม่มี case ใด ๆ ที่พร้อม

มาดูโค้ดด้านล่างนี้กัน

package main
import "fmt"
import "time"

//push data to channel with a 4 second delay
func data1(ch chan string) {  
    time.Sleep(4 * time.Second)
    ch <- "from data1()"
}

//push data to channel with a 2 second delay
func data2(ch chan string) {  
    time.Sleep(2 * time.Second)
    ch <- "from data2()"
}

func main() {
    //creating channel variables for transporting string values
    chan1 := make(chan string)
    chan2 := make(chan string)
    
    //invoking the subroutines with channel variables
    go data1(chan1)
    go data2(chan2)
    
    //Both case statements wait for data in the chan1 or chan2.
    //chan2 gets data first since the delay is only 2 sec in data2().
    //So the second case will execute and exits the select block
    select {
    case x := <-chan1:
        fmt.Println(x)
    case y := <-chan2:
        fmt.Println(y)
    }
}

การรันโปรแกรมข้างต้นจะให้ผลลัพธ์:

from data2()

ที่นี่คำสั่ง select จะรอให้ข้อมูลพร้อมใช้งานในช่องใดๆ data2() จะเพิ่มข้อมูลลงในช่องสัญญาณหลังจากเข้าสู่โหมดสลีป 2 วินาที ซึ่งจะทำให้กรณีที่สองดำเนินการ

เพิ่มกรณีเริ่มต้นให้กับตัวเลือกในโปรแกรมเดียวกันและดูผลลัพธ์ ที่นี่ เมื่อถึงบล็อกที่เลือก หากไม่มีกรณีใดที่มีข้อมูลพร้อมบนช่องสัญญาณ ก็จะดำเนินการบล็อกเริ่มต้นโดยไม่ต้องรอให้ข้อมูลปรากฏบนช่องสัญญาณใดๆ

package main
import "fmt"
import "time"

//push data to channel with a 4 second delay
func data1(ch chan string) {  
    time.Sleep(4 * time.Second)
    ch <- "from data1()"
}

//push data to channel with a 2 second delay
func data2(ch chan string) {  
    time.Sleep(2 * time.Second)
    ch <- "from data2()"
}

func main() {
    //creating channel variables for transporting string values  
    chan1 := make(chan string)
    chan2 := make(chan string)
    
    //invoking the subroutines with channel variables
    go data1(chan1)
    go data2(chan2)

    //Both case statements check for data in chan1 or chan2.
    //But data is not available (both routines have a delay of 2 and 4 sec)
    //So the default block will be executed without waiting for data in channels.
    select {
    case x := <-chan1:
        fmt.Println(x)
    case y := <-chan2:
        fmt.Println(y)
    default:
    	fmt.Println("Default case executed")
    }
}

โปรแกรมนี้จะให้ผลลัพธ์:

Default case executed			

เนื่องจากเมื่อถึงบล็อกที่เลือก ไม่มีช่องใดที่มีข้อมูลสำหรับการอ่าน ดังนั้นกรณีเริ่มต้นจึงถูกดำเนินการ

มิวเท็กซ์

มิวเท็กซ์เป็นรูปแบบย่อของการแยกกันใช้ซึ่งกันและกัน มิวเท็กซ์ใช้เมื่อคุณไม่ต้องการให้ซับรูทีนหลายตัวเข้าถึงทรัพยากรได้ในเวลาเดียวกัน มิวเท็กซ์มี 2 วิธี คือ ล็อกและปลดล็อก มิวเท็กซ์มีอยู่ในแพ็คเกจ sync ดังนั้นคุณต้องนำเข้าแพ็คเกจ sync คำสั่งที่ต้องดำเนินการแยกกันใช้สามารถวางไว้ใน mutex.Lock() และ mutex.Unlock() ได้

มาเรียนรู้ mutex ด้วยตัวอย่างที่นับจำนวนครั้งของการวนซ้ำ ในโปรแกรมนี้เราคาดว่ารูทีนจะรันวนซ้ำ 10 ครั้งและการนับจะถูกจัดเก็บเป็นผลรวม คุณเรียกรูทีนนี้ 3 ครั้ง ดังนั้นจำนวนทั้งหมดควรเป็น 30 การนับจะถูกเก็บไว้ในจำนวนตัวแปรร่วม

ขั้นแรก คุณรันโปรแกรมโดยไม่มี mutex

package main
import "fmt"
import "time"
import "strconv"
import "math/rand"
//declare count variable, which is accessed by all the routine instances
var count = 0

//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
	//loop incrementing the count by 10
	for i := 0; i < 10; i++ {
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		temp := count
		temp++
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		count = temp
	}
	fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}

func main() {
	//loop calling the process() 3 times
	for i := 1; i < 4; i++ {
		go process(i)
	}

	//delay to wait for the routines to complete
	time.Sleep(25 * time.Second)
	fmt.Println("Final Count:", count)
}

เห็นผล

 Count after i=1 Count: 11
Count after i=3 Count: 12
Count after i=2 Count: 13
Final Count: 13

ผลลัพธ์อาจแตกต่างกันเมื่อคุณดำเนินการ แต่ผลลัพธ์สุดท้ายจะไม่เป็น 30

สิ่งที่เกิดขึ้นคือ 3 goroutines กำลังพยายามเพิ่มจำนวนการวนซ้ำที่เก็บไว้ในจำนวนตัวแปร สมมติว่าขณะนี้นับเป็น 5 และ goroutine1 จะเพิ่มจำนวนเป็น 6 ขั้นตอนหลักได้แก่

คัดลอกนับเป็นอุณหภูมิ

เพิ่มอุณหภูมิ

เก็บอุณหภูมิกลับไปนับ

สมมติว่าไม่นานหลังจากดำเนินการขั้นตอนที่ 3 โดย goroutine1; goroutine อื่นอาจมีค่าเก่าบอกว่า 3 ทำตามขั้นตอนข้างต้นและเก็บ 4 กลับซึ่งเป็นสิ่งที่ผิด ซึ่งสามารถป้องกันได้โดยใช้ mutex ซึ่งทำให้รูทีนอื่นต้องรอเมื่อรูทีนหนึ่งใช้ตัวแปรอยู่แล้ว

ตอนนี้คุณจะรันโปรแกรมด้วย mutex ที่นี่ 3 ขั้นตอนที่กล่าวถึงข้างต้นจะดำเนินการใน mutex

package main
import "fmt"
import "time"
import "sync"
import "strconv"
import "math/rand"

//declare a mutex instance
var mu sync.Mutex

//declare count variable, which is accessed by all the routine instances
var count = 0

//copies count to temp, do some processing(increment) and store back to count
//random delay is added between reading and writing of count variable
func process(n int) {
	//loop incrementing the count by 10
	for i := 0; i < 10; i++ {
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		//lock starts here
		mu.Lock()
		temp := count
		temp++
		time.Sleep(time.Duration(rand.Int31n(2)) * time.Second)
		count = temp
		//lock ends here
		mu.Unlock()
	}
	fmt.Println("Count after i="+strconv.Itoa(n)+" Count:", strconv.Itoa(count))
}

func main() {
	//loop calling the process() 3 times
	for i := 1; i < 4; i++ {
		go process(i)
	}

	//delay to wait for the routines to complete
	time.Sleep(25 * time.Second)
	fmt.Println("Final Count:", count)
}

ตอนนี้ผลลัพธ์จะเป็น

 Count after i=3 Count: 21
Count after i=2 Count: 28
Count after i=1 Count: 30
Final Count: 30

ที่นี่เราได้รับผลลัพธ์ที่คาดหวังเป็นผลลัพธ์สุดท้าย เนื่องจากคำสั่งการอ่าน การเพิ่ม และการเขียนกลับของการนับจะดำเนินการใน mutex

การจัดการข้อผิดพลาด

ข้อผิดพลาดคือเงื่อนไขที่ผิดปกติ เช่น การปิดไฟล์ที่ไม่ได้เปิด เปิดไฟล์ที่ไม่มีอยู่ เป็นต้น ฟังก์ชันมักจะส่งคืนข้อผิดพลาดเป็นค่าส่งคืนสุดท้าย

ตัวอย่างด้านล่างจะอธิบายเพิ่มเติมเกี่ยวกับข้อผิดพลาด

package main
import "fmt"
import "os"

//function accepts a filename and tries to open it.
func fileopen(name string) {
    f, er := os.Open(name)

    //er will be nil if the file exists else it returns an error object  
    if er != nil {
        fmt.Println(er)
        return
    }else{
    	fmt.Println("file opened", f.Name())
    }
}

func main() {  
    fileopen("invalid.txt")
}

การส่งออกจะได้รับ:

open /invalid.txt: no such file or directory

ที่นี่เราพยายามเปิดไฟล์ที่ไม่มีอยู่ และมันส่งคืนข้อผิดพลาดให้กับตัวแปร er หากไฟล์ถูกต้อง ข้อผิดพลาดจะเป็นโมฆะ

ข้อผิดพลาดที่กำหนดเอง

การใช้คุณลักษณะนี้ คุณสามารถสร้างข้อผิดพลาดแบบกำหนดเองได้ ทำได้โดยใช้แพ็คเกจข้อผิดพลาด New() เราจะเขียนโปรแกรมข้างต้นใหม่เพื่อใช้ข้อผิดพลาดแบบกำหนดเอง

รันโปรแกรมด้านล่างนี้

package main
import "fmt"
import "os"
import "errors"

//function accepts a filename and tries to open it.
func fileopen(name string) (string, error) {
    f, er := os.Open(name)

    //er will be nil if the file exists else it returns an error object  
    if er != nil {
        //created a new error object and returns it  
        return "", errors.New("Custom error message: File name is wrong")
    }else{
    	return f.Name(),nil
    }
}

func main() {  
    //receives custom error or nil after trying to open the file
    filename, error := fileopen("invalid.txt")
    if error != nil {
        fmt.Println(error)
    }else{
    	fmt.Println("file opened", filename)
    }  
}

การส่งออกจะได้รับ:

Custom error message:File name is wrong

ที่นี่ area() ส่งคืนพื้นที่ของสี่เหลี่ยมจัตุรัส หากอินพุตน้อยกว่า 1 ดังนั้น area() จะส่งกลับข้อความแสดงข้อผิดพลาด

การอ่านไฟล์

ไฟล์ถูกใช้เพื่อจัดเก็บข้อมูล Go ช่วยให้เราสามารถอ่านข้อมูลจากไฟล์ได้

ขั้นแรกให้สร้างไฟล์ data.txt ในไดเร็กทอรีปัจจุบันของคุณโดยมีเนื้อหาด้านล่างนี้

Line one
Line two
Line three

ตอนนี้รันโปรแกรมด้านล่างเพื่อดูว่าจะพิมพ์เนื้อหาของไฟล์ทั้งหมดเป็นเอาต์พุต

package main
import "fmt"
import "io/ioutil"

func main() {  
    data, err := ioutil.ReadFile("data.txt")
    if err != nil {
        fmt.Println("File reading error", err)
        return
    }
    fmt.Println("Contents of file:", string(data))
}

นี่คือข้อมูล err := ioutil.ReadFile(“data.txt”) อ่านข้อมูลและส่งกลับลำดับไบต์ ขณะพิมพ์จะถูกแปลงเป็นรูปแบบสตริง

การเขียนไฟล์

คุณจะเห็นสิ่งนี้พร้อมกับโปรแกรม

package main
import "fmt"
import "os"

func main() {  
    f, err := os.Create("file1.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    l, err := f.WriteString("Write Line one")
    if err != nil {
        fmt.Println(err)
        f.Close()
        return
    }
    fmt.Println(l, "bytes written")
    err = f.Close()
    if err != nil {
        fmt.Println(err)
        return
    }
}

ที่นี่ไฟล์จะถูกสร้างขึ้น test.txt หากไฟล์มีอยู่แล้ว เนื้อหาของไฟล์จะถูกตัดทอน Writeline() ใช้สำหรับเขียนเนื้อหาลงในไฟล์ หลังจากนั้น คุณปิดไฟล์โดยใช้ Close()

แผ่นโกง

ในบทช่วยสอน Go นี้ เราได้กล่าวถึง

กระทู้ Descriptไอออน วากยสัมพันธ์
ประเภทพื้นฐาน ตัวเลข สตริง บูล
ตัวแปร ประกาศและกำหนดค่าให้กับตัวแปร ประเภท var_ชื่อตัวแปร
var ชนิดตัวแปร_ชื่อ = ค่า
var ตัวแปร_ชื่อ1, ตัวแปร_ชื่อ2 = ค่า1, ค่า2
ตัวแปร_ชื่อ := ค่า
ค่าคงที่ ตัวแปรที่ไม่สามารถเปลี่ยนแปลงค่าได้เมื่อกำหนดแล้ว ตัวแปร const = ค่า
สำหรับ Loop ดำเนินการคำสั่งในวง สำหรับการเริ่มต้น_การแสดงออก; การประเมินผล_การแสดงออก; การวนซ้ำ_การแสดงออก{
// หนึ่งหรือหลายคำสั่ง
}
ถ้าเป็นอย่างอื่น มันเป็นคำสั่งแบบมีเงื่อนไข ถ้าเงื่อนไข{
// คำสั่ง_1
} else {
// คำสั่ง_2
}
สลับ คำสั่งแบบมีเงื่อนไขที่มีหลายกรณี สลับการแสดงออก {
กรณีค่า_1:
แถลงการณ์_1
กรณีค่า_2:
แถลงการณ์_2
กรณีมูลค่า_n:
แถลงการณ์_n
ค่าเริ่มต้น:
งบ_default
}
แถว ขนาดคงที่ตั้งชื่อลำดับขององค์ประกอบประเภทเดียวกัน arrayname := [ขนาด] ประเภท {value_0,value_1,…,value_size-1}
ชิ้น ส่วนหรือส่วนของอาร์เรย์ var Slice_name [] ประเภท = array_name [เริ่มต้น: สิ้นสุด]
ฟังก์ชั่น กลุ่มคำสั่งที่ทำงานเฉพาะ func function_name (ประเภทพารามิเตอร์_1, ประเภท parameter_n) return_type {
//คำสั่ง
}
แบบรวดเร็ว ใช้ในการจัดระเบียบโค้ด เพิ่มความสามารถในการอ่านโค้ดและการนำกลับมาใช้ใหม่ได้ นำเข้า package_nam
Defer เลื่อนการดำเนินการของฟังก์ชันจนกว่าฟังก์ชันที่มีอยู่จะเสร็จสิ้นการดำเนินการ เลื่อน function_name (parameter_list)
ชี้ เก็บที่อยู่หน่วยความจำของตัวแปรอื่น var ชื่อตัวแปร *type
โครงสร้าง ประเภทข้อมูลที่ผู้ใช้กำหนดซึ่งมีองค์ประกอบมากกว่าหนึ่งองค์ประกอบที่เป็นประเภทเดียวกันหรือต่างกัน พิมพ์ชื่อโครงสร้าง struct {
ตัวแปร_1 ตัวแปร_1_ประเภท
ตัวแปร_2 ตัวแปร_2_ประเภท
ตัวแปร_n ตัวแปร_n_type
}
วิธีการ วิธีการคือฟังก์ชันที่มีอาร์กิวเมนต์ตัวรับ func (ประเภทตัวแปร) methodName (parameter_list) {
}
โกรูทีน ฟังก์ชันที่สามารถทำงานควบคู่กับฟังก์ชันอื่นๆ ได้ ไป function_name (parameter_list)
ช่อง วิธีที่ฟังก์ชันต่างๆ จะสื่อสารระหว่างกัน สื่อกลางที่รูทีนหนึ่งวางข้อมูลไว้และเข้าถึงได้โดยรูทีนอื่น ประกาศ:
ch := make(จัง int)
ส่งข้อมูลไปที่ช่อง:
channel_variable <- ตัวแปร_ชื่อ
รับจากช่อง:
ตัวแปร_ชื่อ := <- channel_variable
เลือก คำสั่งสลับที่ทำงานบนช่องสัญญาณ คำสั่งเคสจะเป็นการดำเนินการของช่องสัญญาณ เมื่อช่องสัญญาณใดช่องหนึ่งพร้อมด้วยข้อมูล คำสั่งที่เกี่ยวข้องกับเคสนั้นก็จะถูกดำเนินการ เลือก {
กรณี x := <-chan1:
fmt.Println(x)
กรณี y := <-chan2:
fmt.Println(y)
}
มิวเท็กซ์ Mutex ถูกใช้เมื่อคุณไม่ต้องการอนุญาตให้เข้าถึงทรัพยากรโดยรูทีนย่อยหลายรายการในเวลาเดียวกัน Mutex มี 2 วิธี - ล็อคและปลดล็อค mutex.ล็อค()
//คำสั่ง
mutex.ปลดล็อค()
อ่านไฟล์ อ่านข้อมูลและส่งกลับลำดับไบต์ ข้อมูลผิดพลาด := ioutil.ReadFile (ชื่อไฟล์)
เขียนไฟล์ เขียนข้อมูลลงในไฟล์ l ผิดพลาด := f.WriteString(text_to_write)