갈루아의 반서재

함수라는 것은 인풋 파라메터를 아웃풋 파라메터로 매핑시키는 독립된 코드를 말한다. 함수 (또는 프로시져나 서브루틴으로 알려져있기도 하다)는 종종 블랙박스에 비유되기도 한다. 지금까지 살펴봤던 프로그램에는 오직 하나의 함수만 포함되어 있었다. 

1
func main() {}
cs

이제 다수의 함수를 포함한 프로그램을 만들어보자. 


Your Second Function

앞서 살펴봤던 프로그램을 다시 살펴보자.

1
2
3
4
5
6
7
8
9
10
11
12
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)))
}
cs

이미 살펴본 바와 같이 이 프로그램은 평균값을 계산한다. 평균값 계산 같은 경우는 아주 일반적인 문제로, 함수를 정의하는데 아주 적합하다.

평균 함수는 다수의 float64 값을 받아서 하나의 float64 값으로 반환해준다. main 함수 앞에 다음의 내용을 넣어보자. 

1
2
3
func average(xs []float64float64 {
  panic("Not Implemented")
}
cs

함수는 func 라는 키워드로 시작하고, 뒤이어 해당 함수의 이름이 나온다. 입력 파라메터의 경우에는 다음과 같은 식으로 정의된다. name type, name type, …. 
위의 함수는 xs 라고 명명된 하나의 파라메터 (점수 리스트) 를 가지고 있다. 파라메터 다음에 반환 타입을 지정한다. 파라메터와 반환 타입을 함께 함수의 시그니쳐라고 부른다. 

다음으로 나오는 것이 함수의 body 부분이다. 여기에는 {} 안에 일련의 문장을 포함한다. 위의 예에서 body 안에는 런타임 에러를 발생시키는 panic 이라고 부르는 내장 함수를 호출했다. 

그러면 main 함수의 코드를 가져와 average 함수에 넣어보자. 

1
2
3
4
5
6
7
8
9
10
11
package main
 
import "fmt"
 
func average(xs []float64float64 {
  total := 0.0
  for _, v := range xs {
    total += v
  }
  return total / float64(len(xs))
}
cs

fmt.Println 를 return 으로 변경했음에 주목하자. return 문은 함수로 하여금 즉각 중지하고 값을 반환하도록 한다. 다음과 같이 main 을 수정해보자. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
 
import "fmt"
 
func average(xs []float64float64 {
  total := 0.0
  for _, v := range xs {
    total += v
  }
  return total / float64(len(xs))
}
 
func main() {
  xs := []float64{98,93,77,82,83}
  fmt.Println(average(xs))
}
cs

위의 프로그램을 실행해보면 마찬가지의 결과를 얻을 수 있을 것이다. 

그리고 파라메터 이름은 호출함수의 것과 일치할 필요는 없다. 다음과 같이 작성해도 여전히 작동한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
 
import "fmt"
 
func average(xs []float64float64 {
  total := 0.0
  for _, v := range xs {
    total += v
  }
  return total / float64(len(xs))
}
 
func main() {
  alphago := []float64{98,93,77,82,83}
  fmt.Println(average(alphago))
}
cs

함수는 호출 함수에 대한 접근 권한이 없다. 아래는 작동하지 않는다.

1
2
3
4
5
6
7
8
9
10
11
12
package main
 
import "fmt"
 
func f() {
  fmt.Println(x)
}
 
func main() {
  x := 5
  f()
}
cs
1
2
3
4
:~/go/src/golang-book/chapter7# go run main.go
# command-line-arguments
./main.go:6:15: undefined: x
 
cs

다음과 같이 수정해야 한다. 

1
2
3
4
5
6
7
8
9
10
11
12
package main
 
import "fmt"
 
func f(x int) {
  fmt.Println(x)
}
 
func main() {
  x := 5
  f(x)
}
cs

또는 

1
2
3
4
5
6
7
8
9
10
11
12
13
package main
 
import "fmt"
 
var x int = 5
 
func f() {
  fmt.Println(x)
}
 
func main() {
  f()
}
cs


1
2
3
:~/go/src/golang-book/chapter7# go run main.go
5
 
cs


함수는 차곡차곡 쌓아올릴 수 있다. 다음과 같은 프로그램을 생각해보자. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
 
import "fmt"
 
func main() {
    fmt.Println(f1())
}
 
func f1() int {
    return f2()
}
 
func f2() int {
    return 1
}
cs

다음 그림과 같이 생각하면 된다. 


함수를 호출할 때마다, 그것을 call stack 에 올려두는 것이고, 빠져나올 때마다 마지막 함수를 끄집어 내리는 셈이다.

반환형을 다음과 같이 명명할 수도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
 
import "fmt"
 
func main() {
    fmt.Println(f1())
}
 
func f1() int {
    return f2()
}
 
func f2() (r int) {
  r = 1
  return
}
cs