Go언어의 기본적인 사용법을 정리해본다.

다른 프로그래밍 언어에 대해서 알고 있다고 가정하고 새로운 언어를 입문하는 내용으로 정리한다.


환경

  • Go(1.15.8)
  • 다른 프로그래밍 언어 사용 경험


설치

Mac

$ go version
go version go1.15.8 darwin/amd64

Ubuntu

  • 파일 다운로드
curl https://golang.org/dl/go1.15.8.linux-amd64.tar.gz -o go1.15.8.linux-amd64.tar.gz
  • 파일 설치
sudo tar -C /usr/local -xzf go1.15.8.linux-amd64.tar.gz
  • /usr/local/go/binPATH 환경변수에 추가
  • 하단처럼 해도 좋으나 ~/.bash_profile이나 ~/.bashrc에 넣어두는 방향이 좋다.
export PATH=$PATH:/usr/local/go/bin


빌드 및 테스트

  • 여기서는 기본 언어의 사용법만 보기에 폴더 구조 생성은 생략한다.

기본 코드 작성

test.go

package main

import "fmt"

func main() {
	fmt.Println("Hello, world!")
}

빌드하고 실행

  • 빌드 후 실행
$ go build test.go
$ ./test
Hello, world!
  • 아래처럼도 가능
$ go run test.go
Hello, world!


사용법

기본 코드 구조

test.go

// package name
// every Go program is made up of packages
// programs start running in package main
package main

// import other packages
import "fmt"

// main function
// it is like "void main()" in C language
func main() {
	fmt.Println("Hello, world!")
}
Hello, world!


변수 선언

특징

  • 기존과 달리 var 변수 타입으로 선언
  • 포인터도 사용할 수 있다.
  • 여러가지 방식으로 아래처럼 선언이 가능하다.
package main

import "fmt"

func main() {
	/*
	variable examples
	*/
	var i int
	i = 1

	var j = 2

	var a,b int
	a = 3
	b = 4

	x := 5

	fmt.Println(i, j, a, b, x)


	/*
	constant examples
	*/

	const c = 10
	const str = "string"

	// multiple constants
	const (
    	visa = "Visa"
    	master = "MasterCard"
    	amex = "American Express"
	)

	fmt.Println(c, str, visa, master, amex)


	/*
	pointer example
	*/

	var ptrVal int = 100
	var ptr *int = &ptrVal

	*ptr = 150

	fmt.Println(ptrVal, *ptr)
}
1 2 3 4 5
10 string Visa MasterCard American Express
150 150


조건문 - if

특징

  • if 다음에 숫자가 아닌 true 또는 false여야 한다.
package main

import "fmt"

func main() {
    testVal := 10

    if testVal % 3 == 1 {
        fmt.Println("Remainder is 1")
    } else if testVal % 3 == 2 {
        fmt.Println("Remainder is 2")
    } else {
        fmt.Println("Remainder is 0")
    }
}
Remainder is 1


조건문 - switch

특징

  • case문 하나를 완료하면 break가 없어도 switch문을 빠져나간다.
  • fallthrough를 이용하면 case문 하나를 완료해도 switch문을 빠져나가지 않고 다음 case를 진행한다.
package main

import "fmt"

func main() {
	var name string
    var category = 2

    switch category {
    case 1:
        name = "Good"
    case 2:
        name = "Very Good"
    case 3, 4: // multiple value can be used in case
        name = "Super Nice"
    default:
        name = "Power"
    }
    fmt.Println(name)
}
Very Good


반복문 - for문

특징

  • for문만 있고 while문이 없으며 for문을 다양하게 지원하기 때문에 while문처럼 쓸 수 있다.
  • continuebreak는 C언어처럼 동일하게 사용 가능하다.
package main

import "fmt"

func main() {

	// basic form
	var sum int = 0

	for i := 1; i < 10; i++ {
		sum += i
	}
	fmt.Println(sum)


	// range usage with array
	names := []string {"김씨", "이씨", "박씨"}

	// index variable is index of array or slice
	for index, name := range names{
		fmt.Println(index, name)
	}


	// for statement with only condition
	sum = 0
	i := 0

	for i < 10 {
		sum += i
		i++
	}
	fmt.Println(sum)
}
45
0 김씨
1 이씨
2 박씨
45


함수

특징

  • 파이썬처럼 반환값이 하나가 여려개가 가능하다.
  • C++처럼 ‘Pass By Reference’가 가능하다.
package main

import "fmt"

func exampleFunc (msg string) {
	fmt.Println(msg)
}

func exampleFuncReturn (msg string) string{
	return msg
}

func exampleFuncReturnMultiple (first int, second int) (int, int){
	return first, second
}

func exampleFuncPassByReference (val *int){
	*val += 10
}

func main() {
	exampleFunc("exampleFunc test")

	fmt.Println(exampleFuncReturn("exampleFuncReturn test"))

	a,b := exampleFuncReturnMultiple(3, 4)
	fmt.Println(a, b)

	var testVal int = 100
	exampleFuncPassByReference(&testVal)
	fmt.Println(testVal)
}
exampleFunc test
exampleFuncReturn test
3 4
110


익명함수

특징

  • 변수에 함수를 할당할 수 있으며 함수명이 없는 익명함수가 사용 가능하다.
package main

import "fmt"

func main() {

    sum := func (values []int) int {
        res := 0
        for _, val := range values {
            res += val
        }
        return res
    }

    fmt.Println(sum([]int {1, 2, 3}))


    sum2 := func (values ... int) int {
        res := 0
        for _, val := range values {
            res += val
        }
        return res
    }

    fmt.Println(sum2(1, 2, 3))
}
6
6


배열

package main

import "fmt"

func printResult(values [3]int) {

    for _, val := range values{
        fmt.Println(val)
    }
    fmt.Println()
}

func main() {

    var arr1 [3]int
    arr1[0] = 1
    arr1[1] = 2
    // arr1[2] will be 0 it is default value

    var arr2 = [3] int {1, 2, 3}
    var arr3 = [...] int {1, 2, 3}

    printResult(arr1)
    printResult(arr2)
    printResult(arr3)
}
1
2
0

1
2
3

1
2
3

슬라이스(Slice)

특징

  • 동적으로 사용할 수 있는 배열로 추가, 병합, 복사 그리고 부분 슬라이싱이 가능하다.
package main

import "fmt"

func main() {

    var sliceExample []int   
    sliceExample = []int{1, 1, 1}
    // also same as below
    // sliceExample := make([]int, 3, 1)


    // append
    sliceExample = append(sliceExample, 2)
    sliceExample = append(sliceExample, 3, 4, 5)
    fmt.Println(sliceExample)
    fmt.Println()


    // append slices
    sliceExampleA := []int{1, 2, 3}
    sliceExampleB := []int{4, 5, 6}
    fmt.Println(append(sliceExampleA, sliceExampleB...))
    fmt.Println()


    // sub-slice
    sliceExample  = []int{1, 2, 3, 4, 5}
    fmt.Println(sliceExample[:]) // 1 2 3 4 5
    fmt.Println(sliceExample[2:]) // 3 4 5
    fmt.Println(sliceExample[:4]) // 1 2 3 4
    fmt.Println(sliceExample[2:4]) // 3 4
}
[1 1 1 2 3 4 5]

[1 2 3 4 5 6]

[1 2 3 4 5]
[3 4 5]
[1 2 3 4]
[3 4]


맵(Map)

특징

  • 맵(Map)을 순회할 때 순서가 없다.
package main

import (
    "fmt"
    "strconv"
)

func main() {

    var exampleMap map[string]int
    exampleMap = make(map[string]int)

    // add
    exampleMap["Mike"] = 100
    exampleMap["John"] = 110
    exampleMap["Jennie"] = 120

    // delete
    delete(exampleMap, "John")

    fmt.Println(exampleMap["Mike"])
    fmt.Println()

    // map iteration
    for key, value := range exampleMap {
        fmt.Println(key, value)
    }
    fmt.Println()

    // check key exist
    val, exist := exampleMap["Jennie"]
    if exist {
        fmt.Println("exist! its value is " + strconv.Itoa(val))
    }

}
100

Mike 100
Jennie 120

exist! its value is 120


참고자료

Summarize about basice usage of Go.

This post assumes that reader already know about other computer languages. This is not for computer language beginner.


Environment and Prerequisite

  • Go(1.15.8)
  • Experience with other programming languages


Installation

Mac

$ go version
go version go1.15.8 darwin/amd64

Ubuntu

  • Download file
curl https://golang.org/dl/go1.15.8.linux-amd64.tar.gz -o go1.15.8.linux-amd64.tar.gz
  • Install file
sudo tar -C /usr/local -xzf go1.15.8.linux-amd64.tar.gz
  • Add /usr/local/go/bin to PATH environment variable
  • It it fine to use like below but I recommend add to ~/.bash_profile or ~/.bashrc.
export PATH=$PATH:/usr/local/go/bin


Build and Test

  • Skip project directory structure because this only for basic language usage.

Write Basic Code

test.go

package main

import "fmt"

func main() {
	fmt.Println("Hello, world!")
}

Build and Run

  • Run after build
$ go build test.go
$ ./test
Hello, world!
  • Also possible like below
$ go run test.go
Hello, world!


Usage

Basic Code Form

test.go

// package name
// every Go program is made up of packages
// programs start running in package main
package main

// import other packages
import "fmt"

// main function
// it is like "void main()" in C language
func main() {
	fmt.Println("Hello, world!")
}
Hello, world!


Variable Declaration

Characteristics

  • Declare var variable type(its form is different from others)
  • There is pointer.
  • It can be declared in various ways like below.
package main

import "fmt"

func main() {
	/*
	variable examples
	*/
	var i int
	i = 1

	var j = 2

	var a,b int
	a = 3
	b = 4

	x := 5

	fmt.Println(i, j, a, b, x)


	/*
	constant examples
	*/

	const c = 10
	const str = "string"

	// multiple constants
	const (
    	visa = "Visa"
    	master = "MasterCard"
    	amex = "American Express"
	)

	fmt.Println(c, str, visa, master, amex)


	/*
	pointer example
	*/

	var ptrVal int = 100
	var ptr *int = &ptrVal

	*ptr = 150

	fmt.Println(ptrVal, *ptr)
}
1 2 3 4 5
10 string Visa MasterCard American Express
150 150


Condition Statement - if

Characteristics

  • After if must be true or false not a number.
package main

import "fmt"

func main() {
    testVal := 10

    if testVal % 3 == 1 {
        fmt.Println("Remainder is 1")
    } else if testVal % 3 == 2 {
        fmt.Println("Remainder is 2")
    } else {
        fmt.Println("Remainder is 0")
    }
}
Remainder is 1


Condition Statement - switch

Characteristics

  • It breaks switch statement if one case statement is settled.
  • If use fallthrough, then it continues next case statement.
package main

import "fmt"

func main() {
	var name string
    var category = 2

    switch category {
    case 1:
        name = "Good"
    case 2:
        name = "Very Good"
    case 3, 4: // multiple value can be used in case
        name = "Super Nice"
    default:
        name = "Power"
    }
    fmt.Println(name)
}
Very Good


Iteration - for

Characteristics

  • There is no while statement and only for statement is exist. We can use for statement like while statement.
  • Also we can use continue and break like C language.
package main

import "fmt"

func main() {

	// basic form
	var sum int = 0

	for i := 1; i < 10; i++ {
		sum += i
	}
	fmt.Println(sum)


	// range usage with array
	names := []string {"김씨", "이씨", "박씨"}

	// index variable is index of array or slice
	for index, name := range names{
		fmt.Println(index, name)
	}


	// for statement with only condition
	sum = 0
	i := 0

	for i < 10 {
		sum += i
		i++
	}
	fmt.Println(sum)
}
45
0 김씨
1 이씨
2 박씨
45


Function

Characteristics

  • It can return multi values like python.
  • ‘Pass By Reference’ exists like C++.
package main

import "fmt"

func exampleFunc (msg string) {
	fmt.Println(msg)
}

func exampleFuncReturn (msg string) string{
	return msg
}

func exampleFuncReturnMultiple (first int, second int) (int, int){
	return first, second
}

func exampleFuncPassByReference (val *int){
	*val += 10
}

func main() {
	exampleFunc("exampleFunc test")

	fmt.Println(exampleFuncReturn("exampleFuncReturn test"))

	a,b := exampleFuncReturnMultiple(3, 4)
	fmt.Println(a, b)

	var testVal int = 100
	exampleFuncPassByReference(&testVal)
	fmt.Println(testVal)
}
exampleFunc test
exampleFuncReturn test
3 4
110


Anonymous Function

Characteristics

  • We can assign function to variable and also there anonymous function which does not have function name.
package main

import "fmt"

func main() {

    sum := func (values []int) int {
        res := 0
        for _, val := range values {
            res += val
        }
        return res
    }

    fmt.Println(sum([]int {1, 2, 3}))


    sum2 := func (values ... int) int {
        res := 0
        for _, val := range values {
            res += val
        }
        return res
    }

    fmt.Println(sum2(1, 2, 3))
}
6
6


Array

package main

import "fmt"

func printResult(values [3]int) {

    for _, val := range values{
        fmt.Println(val)
    }
    fmt.Println()
}

func main() {

    var arr1 [3]int
    arr1[0] = 1
    arr1[1] = 2
    // arr1[2] will be 0 it is default value

    var arr2 = [3] int {1, 2, 3}
    var arr3 = [...] int {1, 2, 3}

    printResult(arr1)
    printResult(arr2)
    printResult(arr3)
}
1
2
0

1
2
3

1
2
3

Slice

Characteristics

  • It is dynamic size array. It supports append, merge, copy and sub-slice.
package main

import "fmt"

func main() {

    var sliceExample []int   
    sliceExample = []int{1, 1, 1}
    // also same as below
    // sliceExample := make([]int, 3, 1)


    // append
    sliceExample = append(sliceExample, 2)
    sliceExample = append(sliceExample, 3, 4, 5)
    fmt.Println(sliceExample)
    fmt.Println()


    // append slices
    sliceExampleA := []int{1, 2, 3}
    sliceExampleB := []int{4, 5, 6}
    fmt.Println(append(sliceExampleA, sliceExampleB...))
    fmt.Println()


    // sub-slice
    sliceExample  = []int{1, 2, 3, 4, 5}
    fmt.Println(sliceExample[:]) // 1 2 3 4 5
    fmt.Println(sliceExample[2:]) // 3 4 5
    fmt.Println(sliceExample[:4]) // 1 2 3 4
    fmt.Println(sliceExample[2:4]) // 3 4
}
[1 1 1 2 3 4 5]

[1 2 3 4 5 6]

[1 2 3 4 5]
[3 4 5]
[1 2 3 4]
[3 4]


Map

Characteristics

  • There is no order in map iteration.
package main

import (
    "fmt"
    "strconv"
)

func main() {

    var exampleMap map[string]int
    exampleMap = make(map[string]int)

    // add
    exampleMap["Mike"] = 100
    exampleMap["John"] = 110
    exampleMap["Jennie"] = 120

    // delete
    delete(exampleMap, "John")

    fmt.Println(exampleMap["Mike"])
    fmt.Println()

    // map iteration
    for key, value := range exampleMap {
        fmt.Println(key, value)
    }
    fmt.Println()

    // check key exist
    val, exist := exampleMap["Jennie"]
    if exist {
        fmt.Println("exist! its value is " + strconv.Itoa(val))
    }

}
100

Mike 100
Jennie 120

exist! its value is 120


Reference

파이썬(Python) 리스트(list)에서 중복된 원소를 제거하는 방법을 정리


환경

  • Python


중복된 원소들 제거

  • 딕셔너리(dictionary)에 키 값으로 넣었다가 다시 리스트로 변환한다.
  • 아래 방식으로 하면 리스트(list)에 집어 넣은 순서가 유지된다.
  • 버전마다 방식이 달라서 맞게 사용해야 한다. 관련된 내용들은 하단 참고자료에 첨부했다.


Python 2.7 이상

OrderedDict.fromkeys(iterable[, value])

  • 주어진 값들을 키 값으로 사용하는 딕셔너리(dictionary)를 반환한다. value가 있다면 모든 키 값들에 대해 value로 값이 정해지며 없다면 None이 된다.
  • OrderedDict: 파이썬 2.7부터 사용 가능
  • iterable에 추가된 원소들의 순서가 유지

예시

ex_list = [3, 4, 5, 6, 10, 1, 4, 5, 7, 8, 1, 5, 9]
from collections import OrderedDict
result = list(OrderedDict.fromkeys(ex_list))
print(result)
[3, 4, 5, 6, 10, 1, 7, 8, 9]

예시

ex_list = ['e', 'e', 'b', 'c', 'a', 'e', 'd', 'd', 'b', 'a']
from collections import OrderedDict
result = list(OrderedDict.fromkeys(ex_list))
print(result)
['e', 'b', 'c', 'a', 'd']


Python 3.7 이상

dict.fromkeys(iterable[, value])

  • 주어진 값들을 키 값으로 사용하는 딕셔너리(dictionary)를 반환한다. value가 있다면 모든 키 값들에 대해 value로 값이 정해지며 없다면 None이 된다.
  • 파이썬 3.7부터 iterable에 추가된 원소들의 순서가 유지
  • 3.6에서는 일부 구현은 지원하고 일부는 지원하지 않음

예시

ex_list = [3, 4, 5, 6, 10, 1, 4, 5, 7, 8, 1, 5, 9]
result = list(dict.fromkeys(ex_list))
print(result)
[3, 4, 5, 6, 10, 1, 7, 8, 9]

예시

ex_list = ['e', 'e', 'b', 'c', 'a', 'e', 'd', 'd', 'b', 'a']
result = list(dict.fromkeys(ex_list))
print(result)
['e', 'b', 'c', 'a', 'd']


참고자료

Post about removing duplicated elements in Python list


Environment and Prerequisite

  • Python


Remove Duplicated Elements

  • Insert to dictionary as key values and change those to list again.
  • By following below methods inserted order in list is preserved.
  • Different versions have different methods, so they should be used accordingly. Related things are in Reference.


Python 2.7 or higher

OrderedDict.fromkeys(iterable[, value])

  • Returns a dictionary using the given values ​​as key values. If there is a value, the value is set as value for all key values, otherwise it is None.
  • OrderedDict: Available starting with Python 2.7
  • Inserted elements order in iterable is preserved

Example

ex_list = [3, 4, 5, 6, 10, 1, 4, 5, 7, 8, 1, 5, 9]
from collections import OrderedDict
result = list(OrderedDict.fromkeys(ex_list))
print(result)
[3, 4, 5, 6, 10, 1, 7, 8, 9]

Example

ex_list = ['e', 'e', 'b', 'c', 'a', 'e', 'd', 'd', 'b', 'a']
from collections import OrderedDict
result = list(OrderedDict.fromkeys(ex_list))
print(result)
['e', 'b', 'c', 'a', 'd']


Python 3.7 or higher

dict.fromkeys(iterable[, value])

  • Returns a dictionary using the given values ​​as key values. If there is a value, the value is set as value for all key values, otherwise it is None.
  • Inserted elements order in iterable is preserved starting with Python 3.7
  • Partially supported in 3.6 implementation

Example

ex_list = [3, 4, 5, 6, 10, 1, 4, 5, 7, 8, 1, 5, 9]
result = list(dict.fromkeys(ex_list))
print(result)
[3, 4, 5, 6, 10, 1, 7, 8, 9]

Example

ex_list = ['e', 'e', 'b', 'c', 'a', 'e', 'd', 'd', 'b', 'a']
result = list(dict.fromkeys(ex_list))
print(result)
['e', 'b', 'c', 'a', 'd']


Reference

Dockerfile로 도커 이미지를 빌드할 때 다운로드할 파일이 변경되었어도 캐싱된 레이어를 사용해 변경된 파일이 적용되지 않는 경우에 대해서 정리


환경

  • Linux
  • Docker


문제 및 해결 방법

문제 상황

  • Dockerfile를 통해서 도커 이미지를 빌드할 때 RUN에서 curl을 이용해 파일을 다운로드 받는 부분이 있다. 이때, 새로운 이미지 빌드시 파일이 변경되었음에도 처음에 캐싱된 레이어의 파일이 들어가서 이전 파일이 이미지에 포함되는 문제가 발생한다.

문제 상황 예시

  • 파일 확인
$ curl https://twpower.me/test.txt
test1
  • Dockerfile 예시
$ cat Dockerfile
FROM ubuntu:18.04
RUN apt-get update -y
RUN apt-get install curl -y
RUN curl https://twpower.me/test.txt -o test.txt
CMD cat test.txt
  • 도커 이미지 빌드
$ sudo docker build --tag test:1.0 .
  • 빌드된 이미지 실행
$ sudo docker run test:1.0
test1
  • 변경된 파일 확인
$ curl https://twpower.me/test.txt
test2
  • 이미지 다른 버전으로 빌드
  • 하단에 캐시 이용하는거 확인
$ sudo docker build --tag test:2.0 .
Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM ubuntu:18.04
 ---> 56def654ec22
Step 2/5 : RUN apt-get update -y
 ---> Using cache
 ---> 47696d4f4d5f
Step 3/5 : RUN apt-get install curl -y
 ---> Using cache
 ---> 15080e39cb8b
Step 4/5 : RUN curl https://twpower.me/test.txt -o test.txt
 ---> Using cache
 ---> 75bbac2b003f
Step 5/5 : CMD cat test.txt
 ---> Using cache
 ---> 3ce1a5c10a5b
Successfully built 3ce1a5c10a5b
Successfully tagged test:2.0
  • 빌드된 이미지 실행
  • 파일은 변경되었으나 변경된 파일이 적용되지 않은 문제 발생!
$ sudo docker run test:2.0
test1
$ curl https://twpower.me/test.txt
test2


원인

  • Dockerfile에서는 명령어 한줄 한줄을 레이어(Layer)로 판단하며 해당 레이어(Layer)별로 캐싱을 진행하기 때문에 만약 특정줄에 해당하는 명령어와 이전 명령어들이 동일하다면 동일한 레이어(Layer)로 판단하기 때문에 캐싱되어 있는 부분을 가져오기 때문이다.
  • 위의 예제에서는 파일이 변경되었어도 명령어 RUN curl https://twpower.me/test.txt -o test.txt는 동일하다.
  • 공식 홈페이지 문서 https://docs.docker.com/develop/develop-images/dockerfile_best-practices/에 따르면 “A Docker image consists of read-only layers each of which represents a Dockerfile instruction. The layers are stacked and each one is a delta of the changes from the previous layer.”라고 나와있다.


해결 방법

  • --no-cache를 이용해 도커 이미지 빌드시 캐시를 사용하지 않도록 설정
$ sudo docker build --no-cache --tag test:3.0 .


참고자료