Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

함수

함수는 독립적으로 실행할 수 있는 코드로 입력을 받아서 처리하고 출력하는 일을한다. 프로시져(procedures)이나 서브루틴(subroutin)이라고 부르기도 한다. 함수는 종종 블랙박스로 표현한다.

Go에서 가장 유명한 함수는 아마도 main 함수일 것이다. 독립적으로 실행되는 모든 go 프로그램은 반드시 하나의 main 함수를 포함해야 한다.
func main() {}

함수 만들기

나는 간단한 장부 프로그램을 만들려고 한다. 장부 프로그램에는 평균을 내는 코드가 필요해서, 테스트를 했다.
package main
    
import "fmt"
    
func main() {
    xs := []float64{98, 93, 77, 82, 83}
    total := 0.0
    
    for _, v := range xs {
        total += v
    }
    fmt.Println(total / float64(len(xs)))
}   
잘 작동한다. 장부 프로그램은 여러 영역에서 평균값을 계산하는 코드를 사용할 거다. Copy & Paste 신공으로 위의 코드를 가져다 붙이는 방법이 있겠으나, 추천하지 않는다. 평균 코드에 문제가 생겼다거나 기능 개션이 있어서 수정을 해야 한다면, 전체 코드를 뒤져서 수정을 해야 할 것이다. 제대로 될리가 없다. 하여 나는 재 사용가능한 함수를 만들기로 했다.
func average(xs []float64) float64 {
    total := 0.0
    for _, v := range xs {
        total += v
    }
    return (total / float64(len(xs)))
}
이제 다른 코드에서는 average만 호출하면 된다. 물론 제대로 재 사용하기 위해서는 package를 만들어야 겠는데, go 시작에 간단히 설명한게 있으니 참고하자.

그럼 함수를 분석해 보자. 함수는 func키워드로 시작하고 그 뒤에 함수의 이름을 붙인다. 다음 함수에 입력할 매개변수를 (name type, name type, ...)이런 식으로 적어주면 된다. average 함수에서 나는 float64 배열데이터를 xs이름으로 넘겼다. 다음 반환값의 타입을 적어주면 된다. 평균함수의 반환 값 데이터 타입은 float64다.

이제 main 함수를 수정해보자.
func main() {
    xs := []float64{98, 93, 77, 82, 83}
    fmt.Println("Average Is : ", average(xs))
}

Returning Multiple Values

Go는 한번에 두 개 이상의 값을 반환할 수 있다.
func f() (int, int) {
    return 5, 6
}
func main() {
    x, y := f()
    fmt.Printf("%d %d\n", x, y)
}

Multiple value는 종종 에러를 반환하기 위해서 사용한다. x, err := f() 이런식이다.
import (
    "errors"
    "fmt"
    "math"
)

func f(a float64, b float64) (rtv float64, err error) {
    if b == 0 {
        rtv = float64(math.NaN())
        err = errors.New("분모가 0이면 안됨.")
    } else {
        rtv = a / b
    }
    return
}
func main() {
    fmt.Println(f(10.0, 2.0))
    fmt.Println(f(10.0, 0.0))
}

실행결과
5 <nil>
NaN 분모가 0이면 안됨.

가변 매개변수

First class function

Go를 처음 접하는 프로그래머들은 종종 go가 function type, function as value, 클로저(closures)를 지원하는 데 놀라움을 표하곤 한다.(사용하기 쉬운 C 언어 혹은 동시성 제어를 특별히 잘하는 C 언어 정도로 생각하는 경향이 있다.)

First class function은 함수의 매개변수로 다른 함수를 사용할 수 있으며, 다른 함수의 리턴 값을 할당 받아서 사용할 수 있는 수단을 제공한다.

다음은 간단한 예제 프로그램이다.
import "fmt"
import "strings"

func main() {
    rot13 := func(r rune) rune {
        switch {
        case r >= 'A' && r <= 'Z':
            return 'A' + (r-'A'+13)%26
        case r >= 'a' && r <= 'z':
            return 'a' + (r-'a'+13)%26
        }
        return r
    }

    fmt.Println(strings.Map(rot13, "Hello World! My name is Hello!"))
}
Map함수는 두번째 매개변수로 넘어온 문자열을 첫번째 매개변수로 넘어온 함수를 이용해서 맵핑 변환하는 일을 한다. 위 코드는 Map함수를 이용해서 문자열을 인코딩하는 일을 한다.
# go run main.go
Uryyb Jbeyq! Zl anzr vf Uryyb!

First class function을 지원하는 언어는 동적으로 재사용가능한 코드를 쉽게 만들 수 있다. 위의 코드를 대소문자 변환하는 일을 하도록 만들어 보자.
func convert(r rune) rune {
    switch {
    case r >= 'A' && r <= 'Z':
        return r + 32
    case r >= 'a' && r <= 'z':
        return r - 32
    }
    return r
}

func main() {
    fmt.Println(strings.Map(convert, "Hello World! My name is Hello!"))
}

Anonymous Function과 클로저

Go는 익명함수(Anonymous)와 클로저(closures)를 제공한다. Javascript 개발자라면, 이 두개의 개념이 익숙할 것이다. 루비나 파이선 같은 언어는 람다(lambda)함수라는 이름으로 같은 기능을 지원한다. 익명함수와 람다는 이름만 다를 뿐(A.k.A) 같은 개념이다. Go는 아주 단순한 방법으로 익명함수를 지원한다.

func() {
    fmt.Println("hello")
}
그냥 이름없이 사용하면 된다. 보통은 변수에 저장해서, structure에서 함께 사용하거나 다른 함수의 매개변수로 사용한다. 가장 일반적인 사용방식은 다음과 같다.
fn := func() {
    fmt.Println("hello")
}

func()을 변수 fn에 저장했다. fn은 func() 타입의 변수라고 생각하면 되겠다. 이제 fn()처럼 사용하거나 혹은 다른 func()에 재할당 하면 된다.
func main() {
    x := 5
    fn := func() {
        fmt.Println("x is ", x)
    }
    fn()
    x++
    fn()
}
실행 결과는 다음과 같다. 변수 x의 scope가 func() scope를 포함하기 때문에, x를 함께 사용할 수 있다.
x is 5
x is 6

Function Collections

Multiple return value

참고