บทช่วยสอน 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) |