[Golang] Golang 마스터하기(1)
Golang 마스터 1편
변수 선언
- var + 변수명 + 데이터 타입
- 변수 선언 뒤에 등호와 원하는 값을 넣어 변수 초기화 가능
- 초기값이 있다면 데이터 타입 선언 생략 가능
- 변수의 초기값이 없다면 해당 타입의 제로 값으로 초기화
- var 대신 := 이용하여 변수 선언 가능. 뒤에 오는 데이터 타입 추론해 새 변수 선언
- := 공식 이름 짧은 대입문
- var 키워드 사용하는 경우는 거의 없음. var 사용하는 경우는 주로 전역 변수를 선언하거나 초깃값 없이 변수를 선언할 경우.
- 전역 변수를 선언할 때 var 사용하는 경우는 함수 밖에 있는 문장은 반드시 func 나 var 과 같은 키워드로 시작하기 때문
- 특정 데이터 타입 명시적으로 선언할 경우 var 사용 ex) int 대신 int8, int32
변수 출력
- fmt.Println()
- fmt.Printf() 출력 포멧팅 지정. 특히 부동소수점 값들을 출력 편함 ex) %.2f
package main
import (
"fmt"
"math"
)
var Global int = 1234
var AnotherGlobal = -5678
func main() {
var j int
i := Global + AnotherGlobal
fmt.Println("Initial j value", j)
j = Global
k := math.Abs(float64(AnotherGlobal))
fmt.Printf("Global=%d, i=%d, j=%d k=%.2f.\n", Global, i, j, k)
}
프로그램 흐름 제어
이걸 보기 전에 먼저 Golang 처음 하시는 분들에게 커맨드라인 인자(Command-line arguments)를 설명해야 함.
커맨드라인 인자로 프로그램의 실행을 매개변수화 한다고 하는데 이걸 말로 설명하면 어렵고 바로 보자면
package main
import (
"fmt"
"os"
)
func main() {
argsPath := os.Args
argsArguments := os.Args[1:]
arg := os.Args[3]
arg2 := os.Args[2]
fmt.Println("os.Args[0]: ", argsPath)
fmt.Println("os.Args[1:]: ", argsArguments)
fmt.Println("os.Args[2]: ", arg2)
fmt.Println("os.Args[3]: ", arg)
}
위에서 보면 알 수 있듯이 os.Args[1:] 은 인자값을 가지고 os.Args[0] 은 경로라는 것을 알 수 있다.
이제 흐름 제어를 살펴보자.
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
if len(os.Args) != 2 {
fmt.Println("Please provide a command line argument")
return
}
argument := os.Args[1]
// switch 다음에 표현식이 오는 경우
switch argument {
case "0":
fmt.Println("Zero!")
case "1":
fmt.Println("One!")
case "2", "3", "4":
fmt.Println("2 or 3 or 4")
fallthrough
default:
fmt.Println("Value:", argument)
}
value, err := strconv.Atoi(argument)
if err != nil {
fmt.Println("Cannot convert to int:", argument)
return
}
switch {
case value == 0:
fmt.Println("Zero!")
case value > 0:
fmt.Println("Positive integer")
case value < 0:
fmt.Println("Negative integer")
default:
fmt.Println("This should not happen", value)
}
}
if len(os.Args) != 2 {
fmt.Println("Please provide a command line argument")
return
}
이거부터 설명하자면 커맨드라인 인자를 아까 봤으니 알겠지만 0번지는 주소이고 1번지는 인자값이니까 뭐라도 입력하면 길이가 2가 되는 것이다.
그래서 입력을 하지 않았을 경우에 입력하라고 나타내주는 조건문이라고 생각하면 된다.
argument := os.Args[1]
// switch 다음에 표현식이 오는 경우
switch argument {
case "0":
fmt.Println("Zero!")
case "1":
fmt.Println("One!")
case "2", "3", "4":
fmt.Println("2 or 3 or 4")
fallthrough
default:
fmt.Println("Value:", argument)
}
이제 1번지에 들어와 있는 인자 값을 argument에 넣고 케이스 문을 돌린다.
여기서 설명해야 할 것은 fallthrough 인 것 같은데 저것이 들어있는 분기를 실행하고 다음 분기로 진행하라고 알려주는 키워드다. 이 예제에서는 default 분기로 진행한다.
value, err := strconv.Atoi(argument)
if err != nil {
fmt.Println("Cannot convert to int:", argument)
return
}
일단 커맨드라인 인수는 무조건 문자열 값을 가진다. 그걸 정수 값으로 변환하기 위해 strconv.Atoi() 호출하여 변환한 것이다.
err 가 nil 값이면 변환이 성공하므로 다음 코드로 진행한다.
마지막은 흔한 case문이므로 설명하지 않겠다.
for 루프와 range로 반복
이건 조금 적응하기 빡세네… 코드가 좀 이상해
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
fmt.Print(i*i, " ")
}
fmt.Println()
i := 0
for ok := true; ok; ok = (i != 10) {
fmt.Print(i*i, " ")
i++
}
fmt.Println()
// 아래 값은 슬라이스지만 range는 배열에서도 동작
aSlice := []int{-1, 2, 1, -1, 2, -2}
for i, v := range aSlice {
fmt.Println("index:", i, "value:", v)
}
}
1번째 for문은 이해하기 쉬운데 2번째 for문은 좀 고급과정…
while문을 for문으로 만든 느낌이라고 생각하면 될 듯
aSlice라는 슬라이스가 있을 때 range를 이용해 aSlice의 모든 원소를 반복한다. range는 현재 원소의 인덱스와 값을 반환한다. 인덱스와 값 중에 사용하고 싶지 않은 값이 있다면 _를 적어 무시할 수 있다.
사용자 입력받기
package main
import "fmt"
func main() {
fmt.Printf("Please give me your name: ")
var name string
fmt.Scanln(&name)
fmt.Println("Your name is", name)
}
간단하게 함 지켜보고 가면 된다. java 같은 언어와 크게 다르지 않음
커맨드라인 인수 사용 최소, 최댓값 구하기
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
argument := os.Args
if len(argument) == 1 {
fmt.Println("Need one or more arguments!")
}
var min, max float64
for i := 1; i < len(argument); i++ {
n, err := strconv.ParseFloat(argument[i], 64)
if err != nil {
continue
}
if i == 1 {
min = n
max = n
continue
}
if n < min {
min = n
}
if n > max {
max = n
}
}
fmt.Println("MIN: ", min)
fmt.Println("MAX: ", max)
}
위에서 다 실습한 내용의 복습같은 느낌이다. 최대 최솟값 구하는 로직은 커맨드라인 인수 os.Args[1]로 min, max를 초기화 한 후 n에 os.Args[i] 를 바꾸면서 min, max 값을 비교한다.
여기서 마찬가지로 커맨드라인 인수에 strconv.ParseFloat 를 적용해 실수값으로 바꾸고 에러라면 continue 하는 과정도 추가했기에 결과 값을 보면 a, b가 들어가도 무시하고 결과값을 출력하는 것을 볼 수 있다.
Go의 동시성 모델 이해
Go의 동시성 모델은 고루틴과 채널을 이용해 구현한다. 고루틴은 그 중 제일 작은 단위다.
채널은 Go에서 고루틴끼리 통신하고 데이터를 주고받을 수 있게 하는 메커니즘이다.
고루틴을 만들기는 쉽지만 동시성 프로그래밍에는 고루틴을 동기화하고 데이터를 공유하는 등 다른 어려움이 있다. main() 도 고루틴의 하나로 실행되기에 고루틴들이 끝나기 전에 main()의 실행이 끝나지 않도록 주의해라.
time.Sleep() 호출을 이용해 고루틴들을 동기화하는 다음 Go 프로그램을 좋아하는 에디터에서 작성하고 goRoutines.go로 저장하자.
package main
import (
"fmt"
"time"
)
func myPrint(start, finish int) {
for i := start; i <= finish; i++ {
fmt.Print(i, " ")
}
fmt.Println()
time.Sleep(100 * time.Microsecond)
}
func main() {
for i := 0; i < 5; i++ {
go myPrint(i, 5)
}
time.Sleep(time.Second)
}
고루틴은 무작위 순서로 초기화되고 무작위 순서로 실행되기 때문에 위와 같이 실행할 때마다 다른 결과가 나타난다. 운영체제의 스케줄러가 스레드를 실행하는 역할을 하는 것처럼 GO 스케줄러는 고루틴을 실행하는 역할을 맡는다.
Leave a comment