[Golang] Golang 마스터하기(1)

Golang 마스터 1편

변수 선언

  1. var + 변수명 + 데이터 타입
    1. 변수 선언 뒤에 등호와 원하는 값을 넣어 변수 초기화 가능
    2. 초기값이 있다면 데이터 타입 선언 생략 가능
    3. 변수의 초기값이 없다면 해당 타입의 제로 값으로 초기화
  2. var 대신 := 이용하여 변수 선언 가능. 뒤에 오는 데이터 타입 추론해 새 변수 선언
    1. := 공식 이름 짧은 대입문
    2. var 키워드 사용하는 경우는 거의 없음. var 사용하는 경우는 주로 전역 변수를 선언하거나 초깃값 없이 변수를 선언할 경우.
    3. 전역 변수를 선언할 때 var 사용하는 경우는 함수 밖에 있는 문장은 반드시 func 나 var 과 같은 키워드로 시작하기 때문
  3. 특정 데이터 타입 명시적으로 선언할 경우 var 사용 ex) int 대신 int8, int32

변수 출력

  1. fmt.Println()
  2. 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)
}

0


프로그램 흐름 제어

이걸 보기 전에 먼저 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)
}

1

위에서 보면 알 수 있듯이 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는 현재 원소의 인덱스와 값을 반환한다. 인덱스와 값 중에 사용하고 싶지 않은 값이 있다면 _를 적어 무시할 수 있다.

2


사용자 입력받기

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)
}

3

위에서 다 실습한 내용의 복습같은 느낌이다. 최대 최솟값 구하는 로직은 커맨드라인 인수 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)
}

4

5

고루틴은 무작위 순서로 초기화되고 무작위 순서로 실행되기 때문에 위와 같이 실행할 때마다 다른 결과가 나타난다. 운영체제의 스케줄러가 스레드를 실행하는 역할을 하는 것처럼 GO 스케줄러는 고루틴을 실행하는 역할을 맡는다.

Tags:

Categories:

Updated:

Leave a comment