Skip to content
Home » [NEW] เรียนรู้การใช้ภาษา Go ใน 15 นาที | การใช้ semicolon – NATAVIGUIDES

[NEW] เรียนรู้การใช้ภาษา Go ใน 15 นาที | การใช้ semicolon – NATAVIGUIDES

การใช้ semicolon: คุณกำลังดูกระทู้

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

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

เพราะภาษาส่วนใหญ่มักมีหลายสิ่งเหมือนกัน จะให้โปรแกรมเมอร์ไปเริ่มภาษาใหม่ด้วยการนั่งอ่านวิธีประกาศตัวแปร ความหมายของ character หรือรู้จักว่า if statement และ for loop ต่างกันยังไง โลกนี้คงตายด้านน่าดู คิดแล้วขอบินไปเปิดหูเปิดตาที่ดูไบบ้างจะดีกว่า

เราเข้าใจคุณ! ชุดบทความนี้ผมจะแนะนำการโปรแกรมด้วยภาษา Go ในมุมมองของโปรแกรมเมอร์ที่มั่วได้ซักภาษา เราจะไม่มา Intro to Programming 101 แต่เราจะท่องดูไบไปดูซิว่า Go มีอะไรเด่นและเด็ดบ้างจนคุณต้องเผ่นออกนอกประเทศ~

สำหรับบทความนี้เราจะทัศนากันว่า Go มีอะไรเด่น รวมถึงการใช้งานเบื้องต้น เพื่อให้มองเห็นภาพรวมกว้างๆของการเขียนโปรแกรมด้วยภาษา Go

โอเค เตรียมถุงกาวในมือให้พร้อม แล้วไปดึงดาวกันกับบทความนี้

อะไร อะไรก็ Go

อะไรคือการที่ Go คลอดตอนปี 2007?

Go เป็นภาษาที่อุบัติในยุคที่อะไรๆก็ไฮโซ CPU ก็เร็ว และเป็นยุคที่ใครยังใช้ CPU คอร์เดียวถือว่าอยู่หลังเขาหิมาลัยละ

ความที่ Go เกิดทีหลัง จึงซึมซับสิ่งดีๆจากภาษาอื่น และอัปเปหิข้อผิดพลาดทั้งหลายของการโปรแกรมแบบเก่าๆ นั่นรวมถึงไวยากรณ์ยุ่งยากคร่ำครึสมัยพ่อขุนราม การเขมือบหน่วยความจำชนิดอดอยากมาสิบปี (Java ไง, เพื่อนเราเอง) หรือการทำงานแบบ Concurrency ที่ยากราวๆข้อสอบ A-NET

ความเร็วในการพัฒนา

จะเลือกอะไรดี ความเร็วในการพัฒนาหรือความเร็วในการประมวลผล?

แน่นอนว่าสองสิ่งนี้ยากที่จะมาด้วยกัน ภาษา C มีประสิทธิภาพดีกว่าภาษา Ruby แต่กลับแย่กว่าในเชิงของการพัฒนาโปรแกรม มุมกลับคือ Ruby เขียนและบำรุงรักษาโปรแกรมได้ง่ายกว่า C แต่ประสิทธิภาพคือลาก่อย

จะดีกว่าไหม ถ้าจะมีซักภาษาที่ทำให้เราพัฒนาโปรแกรมได้ง่าย ความเร็วก็ดี แม้จะไม่ได้เขียนง่ายเท่า Ruby หรือเร็วเท่า Assembly แต่ก็ดีจนเป็นคนที่ใช่ และพร้อมฝากใจให้เป็นแม่ของลูกเรา

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

Google คือยักษ์

ภาษาที่ดีไม่เพียงเกิดจากไวยากรณ์ที่ดี แต่ต้องมีคอมมูนิตี้ที่ดีด้วย

Go เป็นภาษาที่เกิดจากยักษ์ Google และมีการใช้อย่างแพร่หลายโดย Google Adobe IBM Intel และอื่นๆอีกมาก โปรเจคดังๆหลายตัว เช่น Docker และ Kubernate ก็ใช้ Go แล้วตอนนี้หละ คุณพร้อมจะเปิดใจเลือกใช้ Go หรือยัง?

รู้จักภาษา Go ฉบับคนแปลกหน้า

Go เป็น static language นั่นแปลว่าตัวแปรใดต้องมีการประการชนิดข้อมูล และตัวแปรภาษาจะสามารถตรวจสอบชนิดข้อมูลได้ว่าเรากำหนดถูกต้องหรือไม่ตั้งแต่ช่วงของการคอมไพล์

Go ยังเป็นภาษาประเภท compiled language นั่นคือเราสามารถสั่ง compiler ให้อ่านซอร์สโค้ดแล้วสร้างผลลัพธ์ในรูปของโปรแกรม (Executable File) ที่ทำงานบนแพลตฟอร์มนั้นๆได้เลยโดยไม่ทำงานอยู่บน VM เฉกเช่น JVM ในภาษา Java

ภาษา Go นั้นเหมาะกับงานจำพวก System Programs ไม่ว่าจะสร้าง API Server ก็ได้ ทำ Network Applications ก็ดี หรือจะคูลๆชิคๆไปกับการสร้าง Game Engines ก็ไม่ว่ากัน

สวัสดี Golang

เพื่อให้ Go รู้ว่าจะเริ่มทำงานจากจุดไหน จึงจำเป็นที่เราต้องสร้าง package ชื่อ main พร้อมทั้งประกาศฟังก์ชันชื่อ main เช่นกัน

Go

1

2

package

main

3

4

5

func

main

(

)

{

6

7

}

การโปรแกรมด้วย Go สามารถแยกส่วนโค้ดด้วย package ได้ import จึงเป็นคำสำคัญเพื่อใช้ในการนำเข้า package อื่นมาใช้ในโค้ดเรา

Go

1

package

main

2

3

4

import

"fmt"

5

6

func

main

(

)

{

7

8

fmt

.

Println

(

"Hello, Go"

)

9

}

การ import นั้นไม่จำกัดอยู่เฉพาะไลบรารี่ของ Go เอง เรายังสามารถ import ซอร์สโค้ดจากที่อื่น เช่น Github มาใช้ได้ด้วย เช่น

Go

1

import

"github.com/jinzhu/gorm"

คอมไพเลอร์ของ Go นั้นเป็นคนแก่ขี้บ่น เห็นอะไรไม่ต้องตาเป็นต้องเอะอะโวยวาย หากเรา import บางสิ่งเข้ามาแต่ไม่ได้เรียกใช้ เจ๊ Go ก็จะบ่นดังๆตอนเราคอมไพล์ เช่น

Go

1

package

main

2

3

import

"fmt"

4

5

func

main

{

6

7

}

จากโค้ดข้างต้นพบว่าเรา import แพคเกจ fmt เข้ามา แต่ไม่ได้เรียกใช้งาน เมื่อทำการคอมไพล์ Go ก็จะกริ้วนิดหน่อย ด้วยการบ่นๆดังนี้

Code

1

main.go:3:1: imported and not used: "fmt"

Build Run และ Format

สมมติโครงสร้างโปรเจคภายใต้ชื่อ godubai ของเราเป็นดังนี้

Code

1

-- src

2

-- godubai << โปรเจคเรา

3

-- main.go

เมื่อเราต้องการสร้าง executable file เราสามารถออกคำสั่ง go build ได้ ซึ่งจะได้ผลลัพธ์ตามชื่อโปรเจคคือ godubai นั่นหมายความว่าเราสามารถสั่งโปรแกรมนี้ของเราให้ทำงานได้ด้วยการเรียก ./godubai นั่นเอง

ระหว่างช่วงการพัฒนาโปรแกรม เราคงต้องการสั่งโปรแกรมให้ทำงานเพียงเพื่อต้องการเห็นผลลัพธ์ แต่ไม่ต้องการ executable file ออกมา เราสามารถสั่ง go run main.go แทนได้ ด้วยคำสั่งนี้ซอร์สโค้ดของเราจะถูกอ่าน build และ run โดยไม่มีการสร้างไฟล์ผลลัพธ์ให้เป็นขยะในโฟลเดอร์ของเรา

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

Go

1

2

package

main

3

import

"fmt"

4

func

main

(

)

{

5

fmt

.

Println

(

"Hello, Go"

)

6

}

Go ได้เตรียมเครื่องมือ gofmt เพื่อจัดการฟอร์แมตซอร์สโค้ดของคุณให้เป็นตามมาตรฐาน GO ภายหลังออกคำสั่ง gofmt -w main.go หน้าตาโค้ดเราก็จะเข้าสู่สภาวะเป็นผู้เป็นคน

Go

1

package

main

2

3

import

"fmt"

4

5

func

main

(

)

{

6

fmt

.

Println

(

"Hello, Go"

)

7

}

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

การใช้ gofmt แม้จะดูง่าย แต่ชีวิตเราจะง่ายกว่านี้หากไม่ต้องมาคอยรัน gofmt ดังนั้นเราจึงควรตั้งค่าให้ gofmt ทำงานทุกครั้งหลังจากเรากดเซฟไฟล์ และทำงานอีกครั้งตอนเราส่งโค้ดขึ้น version control

ไหนๆ gofmt ก็ทำให้ชีวิตเป็นเรื่องง่ายแล้ว จะให้ง่ายกว่านี้อีกหน่อยได้ไหม ด้วยการช่วย import package ให้เราอัตโนมัติซะเลย จากในตัวอย่างพบว่าเรามีการใช้งาน fmt แต่ยังไม่ import package ดังกล่าว

Go

1

package

main

2

3

func

main

(

)

{

4

fmt

.

Println

(

"Hello, Go"

)

5

}

Go ได้เตรียมเครื่องมือชื่อ goimports ให้กับเรา หลังออกคำสั่ง goimports -w main.go แพคเกจไหนที่ยังไม่ได้นำเข้า Go ก็จะ import เข้ามาให้ โปรดสังเกตเราใส่ -w เพื่อบอกให้เครื่องมือดังกล่าวเขียนผลลัพธ์ทับไฟล์เดิม

Go

1

package

main

2

3

import

"fmt"

4

5

func

main

(

)

{

6

fmt

.

Println

(

"Hello, Go"

)

7

}

goimports นั้น นอกจากจะทำการ import package ต่างๆที่เราใช้งานในซอร์สโค้ดเข้ามาให้ มันยังทำการฟอร์แมตโค้ดให้กับเราอัตโนมัติโดยไม่ต้องเรียก gofmt เลยด้วยครับ

ประโยคควบคุมในภาษา Go

ประโยคควบคุมในภาษา Go มีไม่เยอะมาก โดยแต่ละตัวก็จะมีลักษณะงานที่เหมาะสมจำเพาะกับมันไปเลย ไม่เหมือนภาษาอื่นๆที่มีทั้ง while และ for-loop ที่ชวนปวดหัวว่าจะใช้ตัวไหนดี

if statement

Go

1

if

i

%

2

==

0

{

2

3

}

else

{

4

5

}

โปรดสังเกต นอกจาก Go จะไม่ต้องปิดท้าย statement ด้วย semicolon แล้ว ประโยคควบคุมยังไม่ต้องครอบด้วยวงเล็บอีกด้วย

for-loop

Go

1

for

i

:=

1

;

i

<=

10

;

i

++

{

2

fmt

.

Println

(

i

)

3

}

for-loop ไม่แตกต่างจากภาษาอื่นมากนัก นั่นคือสามารถแบ่งย่อยออกได้เป็นสามส่วนโดยใช้ semicolon เป็นตัวแบ่ง ส่วนแรกคือการตั้งค่าเริ่มต้น ส่วนถัดมาเป็นเงื่อนไข และส่วนสุดท้ายคือการทำงานหลังจบรอบ

switch

Go

1

switch

i

{

2

case

0

:

fmt

.

Println

(

"Zero"

)

3

case

1

:

fmt

.

Println

(

"One"

)

4

case

2

:

fmt

.

Println

(

"Two"

)

5

case

3

:

fmt

.

Println

(

"Three"

)

6

case

4

:

fmt

.

Println

(

"Four"

)

7

case

5

:

fmt

.

Println

(

"Five"

)

8

default

:

fmt

.

Println

(

"Unknown Number"

)

9

}

ตัวแปรและชนิดข้อมูลในภาษา Go

โดยหลักแล้วเราสามารถประกาศตัวแปรในภาษา Go ได้สองวิธีด้วยกัน

Manual Type Declaration

วิธีการนี้คือการประกาศตัวแปรพร้อมระบุชนิดข้อมูล

Go

1

var

<

ชื่อตัวแปร

>

<

ชนิดข้อมูล

>

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

Go

1

var

message

string

2

message

=

"Hello, Go"

คำถามครับ สมมติเราประกาศตัวแปรขึ้นมา แต่ยังไม่ได้กำหนดค่าให้มันหละ เช่นนี้ค่าของตัวแปรดังกล่าวจะเป็นอะไร?

Zero Values เป็นคำเรียกเก๋ๆสำหรับค่า “default value” ในกรณีที่เราประกาศตัวแปรขึ้นมาแต่ไม่ได้กำหนดค่าให้กับมัน ตัวอย่างเช่น

TypeZero Valueboolfalseint0float0.0string””functionnil

เพราะฉะนั้น หากเราประกาศ var message string ขึ้นมาลอยๆ ตอนนี้คงตอบได้แล้วซิครับว่า message นั้นมีค่าเป็น “” นั่นเอง

Type Inference

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

Go

1

<

ชื่อตัวแปร

>

:=

<

ค่า

>

2

3

4

message

:=

"Hello, Go"

การประกาศตัวแปรพร้อมระบุค่าด้วยการใช้ := Go จะดูว่าฝั่งขวานั้นมีชนิดข้อมูลเป็นอะไร เพื่อนำไปอนุมานว่าตัวแปรดังกล่าวควรเป็นชนิดข้อมูลอะไร เราจึงเรียกวิธีกำหนดค่าแบบนี้ว่า Type Inference

Arrays และ Slices ในภาษา Go

อาร์เรย์ในภาษา Go ก็มีวิธีประกาศและใช้ไม่ต่างอะไรจากภาษาอื่นมากนัก

Go

1

2

var

names

[

3

]

string

3

4

names

[

0

]

=

"Somchai"

5

names

[

1

]

=

"Somsree"

6

names

[

2

]

=

"Somset

หรือถ้าคุณเป็นสายย่อ โค้ดต่อไปนี้จะดูกรุบกริบ

Go

1

names

:=

[

3

]

string

{

"Somchai"

,

"Somsree"

,

"Somset"

}

อาร์เรย์อาจไม่ยืดหยุ่นเพียงพอสำหรับเรา การประกาศอาร์เรย์เราต้องระบุขนาดของมัน แต่การใช้งานจริงของเราเราอาจต้องการยืดและหดขนาดได้อย่างอิสระ Slices จึงเข้ามาตอบโจทย์ตรงนี้แทน Arrays

Go

1

2

3

var

names

[

]

string

4

5

6

names

=

append

(

names

,

"Somchai"

)

7

names

=

append

(

names

,

"Somsree"

)

8

names

=

append

(

names

,

"Somset"

)

สำหรับสายย่อ slices ก็มีวิธีประกาศเช่นเดียวกับ arrays เพียงแต่ไม่ต้องระบุจำนวน

Go

1

names

:=

[

]

string

{

"Somchai"

,

"Somsree"

,

"Somset"

}

arrays และ slices ยังสามารถใช้ for เพื่อวนลูปได้ผ่าน range

Go

1

names

:=

[

3

]

string

{

"Somchai"

,

"Somsree"

,

"Somset"

}

2

3

for

index

,

name

:=

range

names

{

4

fmt

.

Println

(

index

,

name

)

5

}

6

7

8

9

10

ฟังก์ชันในภาษา Go

ฟังก์ชันในภาษา Go ก็เป็นปกติทั่วไปตามแบบฉบับภาษาตระกูลมี Type ต่างกันนิดหน่อยตรงที่ชื่อตัวแปรจะมาก่อนชนิดข้อมูล เช่น

Go

1

package

main

2

3

import

(

4

"fmt"

5

)

6

7

func

main

(

)

{

8

printFullName

(

"Babel"

,

"Coder"

)

9

}

10

11

12

func

printFullName

(

firstName

string

,

lastName

string

)

{

13

fmt

.

Println

(

firstName

+

" "

+

lastName

)

14

}

ในกรณีที่มีการคืนค่ากลับจากฟังก์ชัน เราต้องระบุชนิดข้อมูลของค่าที่จะคืนออกไปด้วย

Go

1

2

func

getMessage

(

)

string

{

3

4

}

ฟังก์ชันในภาษา Go สามารถคืนค่าได้มากกว่าหนึ่งค่า และเป็นธรรมเนียมปฏิบัติที่เรามักจะคืนค่า error เพื่อบ่งบอกว่าการทำงานของฟังก์ชันมีปัญหาหรือไม่ออกมาด้วย

Go

1

package

main

2

3

import

(

4

"errors"

5

"fmt"

6

)

7

8

func

main

(

)

{

9

10

result

,

err

:=

divide

(

5

,

3

)

11

12

13

if

err

!=

nil

{

14

os

.

Exit

(

1

)

15

}

16

17

fmt

.

Println

(

result

)

18

}

19

20

21

func

divide

(

dividend

float32

,

divisor

float32

)

(

float32

,

error

)

{

22

if

divisor

==

0.0

{

23

err

:=

errors

.

New

(

"Division by zero!"

)

24

return

0.0

,

err

25

}

26

27

return

dividend

/

divisor

,

nil

28

}

นิยามโครงสร้างข้อมูลด้วย Structs

แม้ภาษา Go จะไม่มีคลาส แต่เรามี structs ที่สามารถนิยามโครงสร้างของข้อมูลขึ้นมาเองได้

Go

1

type

human

struct

{

2

name

string

3

age

int

4

}

หลังจากที่เรานิยามโครงสร้างข้อมูลภายใต้ชื่อ Human เราก็สามารถสร้างข้อมูลเหล่านี้พร้อมตั้งค่าให้กับมันได้ ดังนี้

Go

1

somchai

:=

human

{

name

:

"Somchai"

,

age

:

23

}

2

somsree

:=

human

{

name

:

"Somsree"

,

age

:

32

}

เมื่อ structs เป็นตัวแทนของโครงสร้างข้อมูลที่เรานิยามขึ้นมา เราจึงสามารถนิยามพฤติกรรมที่สัมพันธ์กับ structs นี้ได้

Go

1

type

human

struct

{

2

name

string

3

age

int

4

}

5

6

7

8

func

(

h human

)

printInfo

(

)

{

9

fmt

.

Println

(

h

.

name

,

h

.

age

)

10

}

11

12

func

main

(

)

{

13

somchai

:=

human

{

name

:

"Somchai"

,

age

:

23

}

14

somchai

.

printInfo

(

)

15

}

พอยเตอร์ในภาษา Go

สมมติเราต้องการสร้างฟังก์ชันใหม่ชื่อ setIsAdult ทำหน้าที่ในการตรวจสอบอายุของคนนั้นๆ หากอายุเกิน 18 ปีจะถือว่าเป็นผู้ใหญ่แล้ว

Go

1

package

main

2

3

import

"fmt"

4

5

type

human

struct

{

6

name

string

7

age

int

8

isAdult

bool

9

}

10

11

12

13

14

func

setAdult

(

h human

)

{

15

h

.

isAdult

=

h

.

age

>=

18

16

}

17

18

func

main

(

)

{

19

somchai

:=

human

{

name

:

"Somchai"

,

age

:

23

}

20

setAdult

(

somchai

)

21

fmt

.

Println

(

somchai

)

22

}

เมื่อผ่าน somchai เข้าไปยังฟังก์ชัน setAdult ปรากฎว่าค่า isAdult ของ somchai ไม่ถูกเซ็ตเป็น true ทำไมจึงเป็นเช่นนั้น?

ภาษา Go ส่งค่าเข้าฟังก์ชันแบบ Pass by Value ความหมายคือมันจะทำการก็อบปี้ข้อมูลต้นทางมาไว้กับฟังก์ชันอีกชุด ดังนั้น somchai และตัวแปร h สำหรับฟังก์ชันจึงเป็นคนละตัว การแก้ไขค่า h.isAdult จึงไม่ใช่การแก้ไข somchai.isAdult นั่นเอง

วิธีแก้คือเราต้องส่ง Reference ของ somchai ไปให้กับฟังก์ชัน โดยที่ฟังก์ชันเมื่อได้รับ reference มาแล้วต้องทำการ dereference หรือถอดเอาค่าที่แท้จริงออกมา

Go

1

package

main

2

3

import

"fmt"

4

5

type

human

struct

{

6

name

string

7

age

int

8

isAdult

bool

9

}

10

11

12

func

setAdult

(

h

*

human

)

{

13

h

.

isAdult

=

h

.

age

>=

18

14

}

15

16

func

main

(

)

{

17

somchai

:=

human

{

name

:

"Somchai"

,

age

:

23

}

18

19

setAdult

(

&

somchai

)

20

fmt

.

Println

(

somchai

)

21

}

ภาษา Go ไม่มีการสืบทอดนะจ๊ะ

การสืบทอด (inheritance) เป็นหนึ่งในหลักการที่โปรแกรมเมอร์มักใช้กันผิดๆ ลองดูการสืบทอดที่เราทำกันผิดๆในภาษาอื่นๆกัน

สมมติว่าในสำนักงานเรามีอุปกรณ์มากมาย ส่วนใหญ่เข้าถึงได้จาก IP เราจึงสร้างคลาส Devise แทนอุปกรณ์ของเรา

TypeScript

1

class

Devise

{

2

ip

:

string

3

location

:

string

4

}

เนื่องจากออฟฟิตเรามีเครื่องพิมพ์ จึงเพิ่ม printer ให้สืบทอดจาก Devise เพราะเป็นอุปกรณ์เหมือนกัน

TypeScript

1

class

Device

{

2

ip

:

string

3

location

:

string

4

}

5

6

class

Printer

extends

Device

{

7

print

(

)

{

8

9

}

10

}

ฝ่ายขายเห็นฮาร์ดดิสก์ลดราคา เลยจัดมาซักสองสามตัว

TypeScript

1

class

Device

{

2

ip

:

string

3

location

:

string

4

}

5

6

class

Harddisk

extends

Device

{

7

store

(

)

{

8

9

}

10

}

ทว่า Harddisk ไม่ได้ติดต่อผ่าน IP แต่เราดันเก็บ IP ไว้กับ Device เป็นผลให้ Harddisk มีฟิลด์ IP ลอยหน้าลอยตาเล่นๆ โดยไม่ได้ใช้งาน

Go นั้นชอบให้เราประกอบร่างจากชิ้นส่วนเล็กๆ ให้เป็นชิ้นส่วนใหญ่ ตามหลักการ Composition over Inheritance มากกว่า เราจึงไม่เห็นไวยากรณ์ของการสืบทอดในภาษา Go

สำหรับเพื่อนๆที่สนใจสามารถอ่านเพิ่มเติมได้จาก รู้จัก Composition over Inheritance หลักการออกแบบคลาสให้ยืดหยุ่น

Interfaces และ Duck Typing

Go ก็มี Interface เหมือนภาษา Java และ C# เพียงแต่ Go ไม่ต้อง implements Interface แบบเดียวกับภาษาเหล่านั้น

Go

1

package

main

2

3

import

"fmt"

4

5

type

human

struct

{

6

name

string

7

age

int

8

}

9

10

type

parrot

struct

{

11

name

string

12

age

int

13

}

14

15

type

talker

interface

{

16

talk

(

)

17

}

18

19

func

(

h human

)

talk

(

)

{

20

fmt

.

Println

(

"Human - I'm talking."

)

21

}

22

23

func

(

p parrot

)

talk

(

)

{

24

fmt

.

Println

(

"Parrot - I'm talking."

)

25

}

26

27

func

main

(

)

{

28

talkers

:=

[

2

]

talker

{

29

human

{

name

:

"Somchai"

,

age

:

23

}

,

30

parrot

{

name

:

"Dum"

,

age

:

2

}

,

31

}

32

33

for

_

,

talker

:=

range

talkers

{

34

talker

.

talk

(

)

35

}

36

37

38

39

40

}

จากตัวอย่างพบว่าทั้งนกแก้วและคนต่างเป็นนักพูด เราจึงประกาศ interface ชื่อ talker ขึ้นมา การที่จะทำให้ Go รู้ว่าคนและนกแก้วเป็นนักพูดนั้น เราไม่ต้อง implements interface แบบภาษาอื่น Go ถือหลักการของ Duck Typing นั่นคือ ถ้าคุณร้องก๊าบๆและเดินเหมือนเป็ด คุณก็คือเป็ด หาก struct คุณมีเมธอด talk คุณก็คือ taker นั่นเอง!

Concurrency และ Parallelism

หนึ่งในจุดเด่นของภาษา Go คือการโปรแกรมเพื่อทำ Concurrency ที่ง่ายกว่าและมีประสิทธิภาพมากกว่าด้วย Goroutines แต่ก่อนที่เราจะไปทำความรู้จักกับฟีเจอร์นี้ เราต้องแยกแยะสองคำที่แตกต่างคือ Concurrency และ Parallelism ออกจากกันเสียก่อน

เราต้องการสร้างระบบค้นหารูปภาพด้วยชื่อจากโฟลเดอร์ต่างๆในคอมพิวเตอร์ สมมติว่ามีสามโฟลเดอร์คือ Document Image และ Library

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

Go

1

func

search

(

keyword

string

)

{

2

folders

:=

[

3

]

string

{

"Document"

,

"Image"

,

"Library"

}

3

4

5

for

_

,

folder

:=

range

folders

{

6

7

}

8

}

9

10

func

main

(

)

{

11

search

(

"dog"

)

12

}

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

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

เมื่อทุกส่วนของโปรแกรมเป็นอิสระต่อกัน เราจะเริ่มทำส่วนไหนก่อนก็ไม่ใช่ปัญหา จะเริ่มค้นหาที่ Document ก่อน หรือจะเริ่มค้นหาที่ Image ก่อน แบบไหนก็ผลลัพธ์ไม่แตกต่าง

นี่คือยุค 2017 ที่ใครๆก็ใช้ CPU หลายคอร์ละนะ เมื่องานแต่ละชิ้นเป็นอิสระต่อกัน ก็แจกจ่ายงานแต่ละส่วนให้ CPU แต่ละคอร์ทำงานไปแบบอิสระต่อกันซะ การทำงานแบบขนานเช่นนี้เราเรียกว่า Parallelism

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

Goroutines

เราสามารถสร้างการทำงานแบบ Concurrency ได้ด้วยการใช้ Goroutines เพียงแค่เติม go เข้าไปข้างหน้าฟังก์ชัน ทุกอย่างก็จะสดชื่น

Go

1

func

searchFromFolder

(

keyword

string

,

folder

string

)

{

2

3

}

4

5

func

search

(

keyword

string

)

{

6

folders

:=

[

3

]

string

{

"Document"

,

"Image"

,

"Library"

}

7

8

for

_

,

folder

:=

range

folders

{

9

10

go

searchFromFolder

(

keyword

,

folder

)

11

}

12

}

13

14

func

main

(

)

{

15

search

(

"dog"

)

16

}

แน่นอนว่าการทำงานแบบ Concurrency ด้วย Goroutines นี้หากเราทำงานบน CPU หลายคอร์ก็จะเปลี่ยนเป็นการทำงานแบบขนานได้

หากใครได้ลองรันโปรแกรมดังกล่าวจะพบว่าโปรแกรมหยุดการทำงานทันที สำหรับภาษา Go เมื่อ main สิ้นสุด การทำงานก็จะสิ้นสุดตาม เราจึงต้องเพิ่มอะไรบางอย่างเพื่อบอกให้ Go คอยการทำงานของ Goroutines ให้เสร็จสิ้นเสียก่อน สิ่งนั้นก็คือ WaitGroup ภายใต้แพคเกจ sync

Go

1

import

"sync"

2

3

func

search

(

keyword

string

)

{

4

var

wg sync

.

WaitGroup

5

6

}

7

8

func

main

(

)

{

9

search

(

"dog"

)

10

}

เพื่อให้ Go ทราบว่าจะต้องรอการทำงานของ Goroutines กี่ตัว เราจึงต้องระบุจำนวนลงไป

Go

1

import

"sync"

2

3

func

search

(

keyword

string

)

{

4

folders

:=

[

3

]

string

{

"Document"

,

"Image"

,

"Library"

}

5

var

wg sync

.

WaitGroup

6

7

wg

.

Add

(

len

(

folders

)

)

8

9

}

10

11

func

main

(

)

{

12

search

(

"dog"

)

13

}

และเพื่อป้องกันไม่ให้ Go หยุดการทำงานไปในทันที เราจึงต้อง Wait จนกว่า Goroutines จะทำงานเสร็จหมด

Go

1

import

"sync"

2

3

func

search

(

keyword

string

)

{

4

folders

:=

[

3

]

string

{

"Document"

,

"Image"

,

"Library"

}

5

var

wg sync

.

WaitGroup

6

wg

.

Add

(

len

(

folders

)

)

7

8

9

wg

.

Wait

(

)

10

}

11

12

func

main

(

)

{

13

search

(

"dog"

)

14

}

คำถามถัดมา Go จะรู้ได้อย่างไรว่า Goroutine ตัวไหนทำงานเสร็จแล้วบ้าง เราจึงต้องสั่ง Done ในแต่ละ routine เพื่อบอกว่าการทำงานของมันเสร็จสิ้นแล้ว

Go

1

import

"sync"

2

3

4

func

searchFromFolder

(

keyword

string

,

folder

string

,

wg

*

sync

.

WaitGroup

)

{

5

6

7

8

wg

.

Done

(

)

9

}

10

11

func

search

(

keyword

string

)

{

12

folders

:=

[

3

]

string

{

"Document"

,

"Image"

,

"Library"

}

13

var

wg sync

.

WaitGroup

14

wg

.

Add

(

len

(

folders

)

)

15

for

_

,

folder

:=

range

folders

{

16

17

go

searchFromFolder

(

keyword

,

folder

,

&

wg

)

18

}

19

wg

.

Wait

(

)

20

}

21

22

func

main

(

)

{

23

search

(

"dog"

)

24

}

เป็นอันจบพิธี~

รู้จัก Package ในภาษา Go

เมื่อโปรแกรมมีขนาดใหญ่ขึ้น เราอาจต้องมีการแยกโค้ดของเราเป็นหลายแพคเกจ

Code

1

-- src

2

-- godubai

3

-- main.go

4

-- search

5

-- main.go

เราอาจสร้างแพคเกจ search ขึ้นมาเพื่อแยกการค้นหาออกจากแพคเกจหลักคือ main

Go

1

func

searchFromFolder

(

keyword

string

,

folder

string

,

wg

*

sync

.

WaitGroup

)

{

2

3

wg

.

Done

(

)

4

}

5

6

func

Run

(

keyword

string

)

{

7

folders

:=

[

3

]

string

{

"Document"

,

"Image"

,

"Library"

}

8

var

wg sync

.

WaitGroup

9

wg

.

Add

(

len

(

folders

)

)

10

for

_

,

folder

:=

range

folders

{

11

go

searchFromFolder

(

keyword

,

folder

,

&

wg

)

12

}

13

wg

.

Wait

(

)

14

}

ส่วนของ main ต้องมีการ import แพคเกจดังกล่าวเข้ามาใช้งาน

Go

1

import

"godubai/search"

2

3

func

main

(

)

{

4

search

.

Run

(

"dog"

)

5

}

หลังจากผ่านโค้ดกันมาเยอะแล้ว เพื่อนๆคงอาจสงสัย เอ๊ะทำไมฟังก์ชันบางตัวก็ขึ้นต้นด้วยตัวเล็ก บางตัวก็ขึ้นต้นด้วยตัวใหญ่?

สำหรับภาษา Go การขึ้นต้นด้วยตัวเล็กใหญ่มีความสำคัญครับ ถ้าตัวแปร ฟังก์ชัน structs หรืออื่นใด ที่ขึ้นต้นด้วยตัวเล็ก สิ่งนั้นจะอ้างอิงถึงได้เฉพาะในแพคเกจตัวเอง หากต้องการให้เข้าถึงได้จากแพคเกจอื่น เราต้องขึ้นต้นสิ่งเหล่านั้นด้วยอักษรตัวใหญ่แทน นี่จึงเป็นเหตุผลที่ว่าทำไม Run ในแพคเกจ search ถึงขึ้นต้นด้วยตัวใหญ่ เพราะมีการเรียกใช้จากแพคเกจอื่นคือ main นั่นเอง

สรุป

บทความนี้เป็นแค่การชิมลางให้มองเห็นภาพรวมกว้างๆของการใช้งาน Go ครับ สำหรับ Go ของเล่นอื่นๆยังมีอีกเยอะ ใครสนใจอ่านเพิ่มเติมก็โปรดติดตามชุดบทความนี้แบบติดๆเลยนะฮะ

[Update] ภาษาอังกฤษดอทคอม “อยู่เมืองไทยก็เก่งภาษาอังกฤษได้” | การใช้ semicolon – NATAVIGUIDES

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

  • Alan stayed up late last night.

    Therefore

    , he went to work late this morning.

  • Alan stayed up late last night;

    therefore

    , he went to work late this morning.

  • Aland stayed up late last night. He,

    therefore

    , went to work late this morning.

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

แบบที่สองนี่ก็เจอบ่อยเหมือนกันคือไม่ต้องมีการแยกประโยคอะไรให้วุ่นวาย จับมันรวมกันซะเลย  แต่ต้องมีตัวช่วยนิดนึงคือ ใส่ semicolon (;) ไว้หน้า therefore แล้วใส่ comma ปิดท้าย แค่นี้ก็เป็นอันเสร็จกระบวนการ

แบบที่สาม นี่แบบเก๋ๆ คือจบประโยคแรกไปก่อนเหมือนแบบที่หนึ่ง  แต่เราทำเก๋ด้วยการเอาประธานขึ้นก่อน แล้วค่อยใส่ therefore ตามหลังมา โดยมี comma คั่นหน้าคั่นหลัง แค่นี้ประโยคก็สุดแสนจะเก๋แล้ว

Conjunctive adverb แต่ละประเภทมีดังนี้ค่ะ
1.  ข้อความที่เสริมกันหรือเป็นการให้ข้อมูลเพิ่มเติม โดย แปลคล้ายๆกันว่า  นอกจากนี้  นอกเหนือจากนี้  เช่น in addition, moreover, furthermore, also, on top of that, above all, besides

The company will be downsizing.  Moreover, it will be moved to a new location.
Frank spent most of his life traveling around the world.  He, furthermore, has written many books about his experiences.

2.  ข้อความที่ขัดแย้งกัน เช่น however, nevertheless, still, yet, nonetheless, in contrast, on the contrary, on the other hand

I studied hard. Still, I didn’t pass the exam.
Your idea is good; however, it’s difficult to implement.

3.  ข้อความที่แสดงทางเลือกอย่างใดอย่างหนึ่ง  เช่น  or else, otherwise, if not

We must hurry up. Otherwise, we will miss the train.

It’s good that you attend the competition. Or else, you’ll never have a chance to do it anymore.

4. ข้อความที่แสดงความเป็นเหตุเป็นผลกัน  เช่น therefore, thus, accordingly, consequently, as a result, for this reason, then, hence  เป็นต้น

Tom is talking on the phone.  Thus, he isn’t paying attention to his son.
We had no food left at home; therefore, we went shopping.

5. ข้อความที่แสดงการยกตัวอย่าง เช่น for example, for instance, namely, in particular

You should eat healthy food, namely, brown rice, whole wheat bread, milk, fruits, and vegetables.

6. ข้อความที่พูดซ้ำความเดิมเพื่อให้เกิดความเข้าใจ เช่น in other words, that is to say, that is
The mobile library services have been reorganized to cut costs.  That is, they visit fewer places.


การใช้ Semicolon ในการเชื่อมประโยค | ภาษาอังกฤษวันละนิด By Yes iStyle


วันนี้ครูโรสจะมาสอนการใช้ Semicolon ในการเชื่อมประโยค
ติดตามได้ที่ :
Facebook: Yes iStyle
Youtube: Yes iStyle
www.yesistyle.co.th
Yes iStyle เรียนได้ทุกที่ ทุกเวลา
ติดต่อสปอนเซอร์
▸ Email : [email protected]
ครูโรส ภาษาอังกฤษวันละนิด YesiStyle

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

การใช้ Semicolon ในการเชื่อมประโยค | ภาษาอังกฤษวันละนิด By Yes iStyle

How misused modifiers can hurt your writing – Emma Bryce


View full lesson: http://ed.ted.com/lessons/howmisusedmodifierscanhurtyourwritingemmabryce
Modifiers are words, phrases, and clauses that add information about other parts of a sentence—which is usually helpful. But when modifiers aren’t linked clearly enough to the words they’re actually referring to, they can create unintentional ambiguity. Emma Bryce navigates the sticky world of misplaced, dangling and squinting modifiers.
Lesson by Emma Bryce, animation by Karrot Animation.

How misused modifiers can hurt your writing - Emma Bryce

เรามองเห็นสีได้อย่างไร – Colm Kelleher


รับชมบทเรียนเต็มๆ: http://ed.ted.com/lessons/howweseecolorcolmkelleher
มีตัวรับสีเพียง 3 ชนิดในตาของคุณ คือ สีแดง สีเขียว และสีน้ำเงิน แต่ทำไมเราจึงสามารถมองเห็นแสงสีต่างๆ มากมายในโลกรอบตัวเราได้ คอล์ม เคลเลเฮอร์ อธิบายว่ามนุษย์เราสามารถมองเห็นสีต่างๆ ตั้งแต่ สีน้ำตาลแดงไปจนถึงสีเขียวน้ำเงินได้อย่างไร
สอนโดย Colm Kelleher, แอนนิเมชั่นโดย TEDEd

เรามองเห็นสีได้อย่างไร - Colm Kelleher

English Punctuation Guide – English Writing Lesson


In this lesson, you can learn about English punctuation.You’ll see the most common punctuation marks in English, what they’re called, and how to use them.
Using punctuation correctly is critical for your English writing. Punctuation problems can make a bad impression or lead to misunderstandings.

Want to learn more about other punctuation marks? Get help from a certified English teacher in an online English lesson: http://bit.ly/ooeteachers.

 See the full version of this lesson on our website: https://www.oxfordonlineenglish.com/englishpunctuationguide.
Contents:
1. Period 0:58
2. Comma 3:38
3. Colon 5:34
4. Semicolon 6:37
5. Apostrophe 8:09
6. Hyphen 9:57
7. Dash 12:13
8. Quotation Mark 14:19
9. Parentheses 17:06
10. Question and Exclamation Marks 18:53
This lesson will help you:
Get a better understanding for all punctuation marks in English so you can improve your English writing and comprehension.
Use our English punctuation guide to get real examples of using each punctuation mark correctly.
See common mistakes students make when using English punctuation.
Go through ten different parts of the English punctuation guide to better understand each punctuation mark.
SUBSCRIBE to continue improving your English! https://goo.gl/UUQW8j
Become an OOE member to see our newest lessons before they’re available to the public, and more! https://www.youtube.com/channel/UCNbeSPp8RYKmHUliYBUDizg/join
See more free English lessons like this on our website: https://www.oxfordonlineenglish.com/.

English Punctuation Guide - English Writing Lesson

semicolon กับ colon ใช้อย่างไร


เว็ปอดัม: http://www.ajarnadam.com
FBของอดัม: http://www.facebook.com/AjarnAdamBradshaw
FBของซู่ชิง: http://www.facebook.com/jitsupachin
Twitter: http://twitter.com/AjarnAdam
Twitter ซูชิง: http://twitter.com/Sue_Ching

semicolon กับ colon ใช้อย่างไร

นอกจากการดูบทความนี้แล้ว คุณยังสามารถดูข้อมูลที่เป็นประโยชน์อื่นๆ อีกมากมายที่เราให้ไว้ที่นี่: ดูวิธีอื่นๆMAKE MONEY ONLINE

ขอบคุณที่รับชมกระทู้ครับ การใช้ semicolon

Leave a Reply

Your email address will not be published. Required fields are marked *