Second Day with Go-Lang

5 minute read

문자열 : Back Quote(``) / 이중인용부호(“”) 를 사용해 표현

  1. Back Quote ; Raw String Literal

이 안에 있는 문자열은 별도의 해석 없이 Raw String 그대로의 값을 가짐 복수 라인의 문자열을 표현할 때 자주 사용됨

ex) 문자열 안의 \n 은 New Line 으로 해석되지 않음

  1. 이중인용부호(“”) ; Interpreted String Literal

인용부호 안의 Escape 문자열은 특별한 의미로 해석됨 이중인용부호를 이용해 문자열을 여러 라인에 걸쳐 쓰기 위해 + 연산자 이용

package main

import "fmt"

func main() {
  // Raw String Literal. 복수 라인.
  rawLiteral := `아리랑\n
아리랑
  아라리요`

  // Interpreted String Literal
  interLiteral := "아리랑아리랑\n아라리요"
  // 아래와 같이 +를 사용해 두 라인에 걸쳐 사용할 수 있다.
  // interLiteral := "아리랑아리랑\n" + "아라리요"

  fmt.Println(rawLiteral)
  fmt.Println()
  fmt.Println(interLiteral)
}

데이터 타입 변환 (Type Conversion)

아래 예제는 여러 Type Conversion 의 예를 보인 것이다. 한 가지 주의점은 Go 언어에서 타입간 변환은 명시적으로 지정해 주어야 한다. 암묵적 변환이 일어나지 않으므로, 명시적 지정이 없이 변환이 일어나면 런타임 에러가 발생한다.

func main(){
  var i int = 100
  var u uint = uint(i)
  var f float32 = float32(i)
  println(f, u)

  str := "ABC"
  bytes := []bytes(str)
  str2  := string(bytes)
  println(bytes, str2)
}

Short Variable 선언

함수 블록 내부에서, Short assignment statement(:=) 가 var 없이 변수 선언을 할 수 있게 한다. 함수 외부에서는 모든 statement 가 keyword(var, func, …) 로 시작해야 하기 때문에 := 를 사용할 수 없다.

Go 연산자

Go 연산자에서 사용하는 연산자들은 기존의 C++ 연산자들과 유사

  1. 산술연산자 : 사칙연산(+, -, *, /) + Modular 연산(%), 증감연산자(++, --)
  2. 관계연산자 : ==, !=, >=, ...
  3. 논리연산자 : &&, ||, !
  4. Bitwise 연산자 : &, |, ^, <<
  5. 대입연산자 : =, *=, >>=, |=
  6. 포인터연산자 : &, *

Go 는 포인터 연산자를 제공하지만, 포인터 자체에 더하고 빼는 기능은 제공하지 않음

조건문

  • if 문

    Go 의 if 조건문은 조건식을 괄호로 둘러싸지 않아도 된다. 그리고 중괄호를 반드시 if 문과 같은 라인에 두어야 한다. else 문 (else if 문) 은 해당하는 if 문의 중괄호가 끝나는 라인에 같이 존재해야 한다.

    if 문에서 조건식을 사용하기 이전에 간단한 문장(Optional Statement) 을 함께 실행할 수 있다. 아래와 같이 val := i * 2 라는 문장을 조건식 이전에 실행할 수 있는데, 이 때 주의할 점은 정의된 변수 val 는 블럭 내에서만 사용이 가능하다는 점이다. 이러한 Optional Statement 는 switch, for 문 등 여러 문법에서 사용이 가능하다.

if k == 1 {
  println("One")
} else if k == 2 { // 같은 라인
  println("Two")
} else {           // 같은 라인
  println("Other")
}
if val := i * 2; val < max {
  println(val)
}

// 아래와 같은 사용은 Scope 를 벗어나서 에러를 발생시킴
val++
  • switch 문

    기본적인 사용은 C 언어의 switch 문과 같고, 복수개의 case 값이 있을 경우는 콤마를 써서 해당 값을 나열할 수 있다.

    1. switch 뒤에 expression 이 없을 수 있다.
    2. case 문에 Literal 값 뿐이 아니라 expression 을 사용할 수 있다.
    3. Default fall through 발생하지 않음 ; break 문 없이 다음 case 로 넘어가지 않는다.
    4. Literal 값 뿐 아니라 Type 에 따라 case 로 분기할 수 있다.
package main

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

  switch category { // 괄호 사용 X
  case 1:
    name = "Paper Book"
  case 2:
    name = "eBook"
  case 3, 4:
    name = "Blog"
  default:
    name = "Other"
  }

  println(name)
}
func grade(score int) {
  // switch 뒤에 expression 을 적지 않고 case 문에 expression 을 사용해 조건 비교
  // 복잡한 if ~ else if ~ else 문을 단순화하는 데 유용하게 사용될 수 있다.
  switch {
  case score >= 90:
    println("A")
  case score >= 80:
    println("B")
  case score >= 70:
    println("C")
  case score >= 60:
    println("D")
  default:
    println("No Hope")
  }
}
switch v.(type) {
case int:
  println("int")
case bool:
  println("bool")
case string:
  println("string")
default:
  println("Unknown")
}
package main

func main(){
  check(2)
}

func check(val int) {
  switch val {
  case 1:
    println("1 이하")
    fallthrough
  case 2:
    println("2 이하")
    fallthrough
  case 3:
    println("3 이하")
    fallthrough
  default:
    println("default 도달")
  }
}

반복문

Go 언어에서 반복문은 for 문 하나만 존재한다. 형식은 for 초기값; 조건식; 증감식 { ... } 의 형식을 따른다.

  • for 문 예제
package main

func main(){
  sum := 0
  for i := 1; i <= 100; i++ { // for 문에서 변수 선언할 때 short assignment (:=) 만 사용 가능
    sum += i
  }
  println(sum)
}
  • 조건식만 사용한 for 문

    Go 언어에서 for 문은 초기값 / 증감식을 생략할 수 있다. 이를 통해, 다른 언어의 while 문과 유사하게 사용할 수 있도록 한다.

    (조건식을 생략할 경우, 무한루프를 생성할 수 있다)

package main

func main(){
  n := 1
  for n < 100 {
    n *= 2
  }
  println(n)
}
  • for range 문

    for range 문은 컬렉션으로부터 한 요소씩 가져와 블록 내의 문장을 실행한다. 다른 언어의 foreach 와 유사한 사용법이다.

    for range 문은 for 인덱스, 요소값 := range 컬렉션 과 같이 구성하는데 range 키워드 다음의 컬렉션으로부터 하나씩 요소를 리턴해 그 요소의 인덱스와 값을 for 키워드 다음의 두 변수에 각각 할당한다.

names := []string{"홍길동", "이순신", "강감찬"}

for index, name := range names {
  println(index, name)
}
  • break, continue, goto 문

    각각 다른 언어에서의 사용과 동일하나 break 를 break Label 과 같이 사용할 수 있다.

package main

func main(){
  var a = 1
  for a < 15 {
    if a == 5 {
      a += a
      continue
    }
    a++
    if a > 10 {
      break
    }
  }
  if a == 11 {
    goto END
  }
  println(a)

END:
  println("End")
}

함수

함수는 여러 문장을 묶어 실행하는 코드 블럭의 단위, Go 에서 함수는 func 키워드를 사용해 정의한다. Parameter 는 0개 이상 사용할 수 있고, 각 Parameter 는 이름 뒤에 Parameter Type 을 적어서 정의한다. 함수 Return Type 은 괄호 뒤에 적게 되는데, 이는 다른 언어에서 Return Type 이 함수명 앞에 적힌 것과 대조적이다.

Callee 함수가 반드시 Caller 함수의 앞에 위치해야 할 필요는 없다

package main
func main(){
  msg := "Hello"
  say(msg)
  println(msg)
}

func say(msg string) {
  println(msg)
  msg = "Changed"
}
  • Parameter 전달 방식

    Go 에서 Parameter 를 전달하는 방식은 크게 Pass by Value 와 Pass by Reference 로 나뉜다.

package main
func main() {
  msg := "Hello"
  say(&msg)
  println(msg) //  변경된 메시지 출력
}

func say(msg *string) {
  println(*msg)
  *msg = "Changed" // 메시지 변경
}
  • Variadic Function (가변 인자 함수)

    함수에 고정된 숫자의 Parameter 를 전달하지 않고, 정해지지 않은 개수의 Parameter 를 전달하고자 할 때 가변 Parameter 를 나타내는 ... (3개의 마침표) 를 사용한다. 이 때 ... 는 함수의 마지막 Parameter 에만 사용될 수 있다. 이렇게 가변 Parameter 를 사용하는 함수를 가변인자 함수라 부른다.

package main
func main() {
  say("This", "is", "a", "book")
  say("Hi")
}

func say(msg ...string) {
  for _, s := range msg {
    println(s)
  }
}
  • 함수 Return Value

    Go 언어에서 함수는 Return 이 없을 수도, 하나일 수도, 여러 개일 수도 있다. 또한 Named Return Parameter 라는 기능을 제공하는데, 이는 리턴되는 값들을 Return Parameter 들에 할당할 수 있는 기능이다.

package main

func main() {
  total := sum(1, 7, 3, 5, 9)
  println(total)
}

func sum(nums ...int) int {
  s := 0
  for _, n := range nums {
    s += n
  }
  return s
}

Go 에서 여러 Return Value 들을 Return 하기 위해서는 해당 Return Type 들을 괄호 안에 적어 준다.

package main

func main() {
  count, total := sum(1, 7, 3, 5, 9)
  println(count, total)
}

func sum(nums ...int) (int, int) {
  s := 0
  count := 0 // s, count := 0, 0 과 같이 표현할 수 있다.
  for _, n := range nums {
    s += n
    count++
  }

  return count, s  
}

Go 에서는 Named Return Parameter 를 사용해서 Return Value 들을 할당해 Return 할 수 있다. 위의 sum() 함수를 Named Return Parameter 를 이용해 고쳐 쓰면 다음과 같다. Return Parameter 의 이름과 그 Type 을 함께 정의해 사용한다. 그리고 함수 내에서는 이 count, total 에 결과값을 직접 할당하고 있음을 볼 수 있다. 함수의 마지막에는 Return 문이 있는데, 실제 return 문에서 아무 값도 Return 하지 않지만 그래도 Return Value 가 있을 경우 빈 return 문을 반드시 써 주어야 한다.

named return parameter 는 기본적으로 0 으로 초기화

func sum(nums ...int) (count int, total int) {
  for _, n := range nums {
    total += n
  }
  count = len(nums)
  return
}

Go 익명함수

익명함수 (Anonymous Function) ; 함수명을 갖지 않는 함수

일반적으로 익명함수는 그 함수 전체를 변수에 할당하거나 다른 함수의 파라미터에 직접 정의되어 사용된다. 일단 익명함수가 변수에 할당된 이후에는 변수명이 함수명과 같이 취급되며 변수명(Parameters) 형식으로 함수를 호출할 수 있다.

package main

func main() {
  sum := func(n ...int) int {
    s := 0
    for _, i := range n {
      s += i
    }
    return s
  }

  result := sum(1, 2, 3, 4, 5)
  println(result)
}
  • 일급함수

Go 언어에서 함수는 일급함수로서 Go 의 기본 타입과 동일하게 취급되며, 다른 함수의 파라미터로 전달하거나 다른 함수의 리턴값으로도 사용될 수 있다. 함수를 다른 함수의 파라미터로 전달하기 위해서는 익명함수를 변수에 할당한 후 이 변수를 전달하는 방법과 직접 다른 함수 호출 파라미터에 함수를 적는 방법이 있다.

package main

func main() {
  // 변수 add 에 익명함수 할당
  add := func(i int, j int) int {
    return i + j
  }

  // add 함수 전달
  r1 := calc(add, 10, 20)
  println(r1)

  // 직접 첫번째 ㅏ라미터에 익명함수를 정의함
  r2 := calc(func(x int, y int) int { return x  - y}, 10, 20)
  println(r2)
}

func calc(f func(int, int) int, a int, b int) int {
  result += f(a, b)
  return result
}
  • Type 문을 사용한 함수 원형 정의

type 문은 구조체, 인터페이스 등 Custom Type(혹은 User Defined Type) 을 정의하기 위해 사용된다. 위 예제에서 func(x int, y int) int 함수 원형이 코드 상에 계속 반복되는데, 이 경우 type 문을 정의함으로써 해당 함수의 원형을 간단히 표현할 수 있다.

// 원형 정의
type calculator func(int, int) int

// calculator 원형 사용
func calc(f calculator, a int, b int) int {
  result := f(a, b)
  return result
}

이렇게 함수의 원형을 정의하고 함수를 다른 Method 에 전달하고 리턴받는 기능을 흔히 Delegate 라 부른다. Go 는 이러한 Delegate 기능을 제공하고 있다.

Reference : 예제로 배우는 Go 프로그래밍