Go/Golang - Function
Function 심화
vartadic function(가변인수 함수) 만들기
1
2
3
4
5
6
7
8
| // ex)
func sum(nums ...int) int {
sum := 0
fmt.Printf("nums 타입: %T\n", nums)
for _, v := range nums {sum += v}
return sum
}하지 않는다.
}
|
vartadic function(가변인수 함수)와 interface{}
그렇다면 모든 타입을 받을 수 있는 가변인수는 어떻게 만들 수 있을까? 바로 빈 인터페이스(interface {})
를 사용한다. 모든 타입은 interface{}를 포함하고 있으므로 interface{}를 가변인수로 받으면 모든 타입을 받을 수 있다.
1
2
3
4
5
6
7
8
| // ex)
func alltypes(args ...interface{})) string {
sum := 0
fmt.Printf("nums 타입: %T\n", nums)
for _, v := range nums {sum += v}
return sum
}하지 않는다.
}
|
Defer 란 무엇인가?
함수 종료 전에 실행되는 Defer
defer
는 함수의 종료 전 반드시 실행되어야 하는 구문
을 표현할 때 사용된다. 예를들어 운영체제에서 사용한 자원을 반환해야 할 때 사용하면 유용하다. 특이한 점은, 작성한 defer의 역순으로 실행
된다는 점이다.
1
2
3
| defer fmt.Println("반드시 호출됩니다.") // 1
defer f.Close() // 2
defer fmt.Println("파일을 닫았습니다.") // 3
|
함수 타입 변수
함수 타입 변수
상황에 따라 다른 함수를 호출해야 하는 경우, 함수 타입 변수를 사용한다. 리턴타입이 함수
인 것. 함수 또한 프로그램 카운터(PC)에 의해 표시되는 주소
이다. 사용예시는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| func add(a, b int) int {
return a + b
}
func mul(a, b int) int {
return a * b
}
// calculation의 리턴타입은 `func (int, int) int` 인 것!
func calculation(op string) func (int, int) int {
if op == "+"{
return add
}
.
.
.
}
// 매개변수로 요약한다면
// type calReturnType func (int, int) int
// func calculation(op string) calReturnType {
// ...
// }
|
Function Literal이란 무엇인가?
익명함수, Lamda 라는 용어로 더 익숙하다. 함수명이 없는 형태로, 함수명으로 호출하지 않고 함수 타입 변수로만 호출된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| func calculation(op string) func (int, int) int {
if op == "+"{
return func (a, b int) int {
return a + b
}
}
.
.
.
}
// 사용 형태 1)
function := func (a, b int) int {
return a + b
}
res := function(1,2)
// 사용 형태 2)
function := func (a, b int) int {
return a + b
}(1,2)
|
Function Literal 내부 상태
함수 리터럴은 필요한 외부 변수를 내부 변수로 가질 수 있다. 함수 리터럴 내부에서 사용되는 외부변수는 자동으로 함수 내부 상태로 저장된다. 이렇게 함수 리터럴 외부 변수를 함수 내부로 가져오는 것을 캡쳐(capture)
라고 한다. 캡쳐는 값 복사가 아닌 참조 형태
로 가져오게 된다. 따라서 아래 코드에서 주석이 아닌, 본 코드 그대로를 실행하면 3,3,3이 리턴되게 된다. i에 대한 참조복사가 일어나기 때문이다. 이를 방지하기 위해, 주석처럼 값을 임의변수에 대입해 해당 변수를 함수 리터럴 내부에서 사용해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| func CaptureOne() {
f := make([]func(), 3)
fmt.Println("this is refer capt")
for i := 0; i < 3; i++ {
// 캡쳐에서 값 복사를 원하는 경우
// v := i -> 이 경우, for문이 반복될 때마다 새로운 v가 할당된다.
f[i] = func() {
fmt.Println(i)
// 캡쳐에서 값 복사를 원하는 경우
// fmt.Println(v)
}
}
for i:= 0; i < 3; i++ {
f[i]()
}
}
|
Function Literal 예시2 파일 핸들
아래 코드에서는 writeHello안에 들어가는 파라미터를 함수 리터럴(Function Literal) 형태로 구현했다. 이때, writeHello 입장에서는 넘어오는 writer가 어떤 방식으로 동작할 지 모르게 된다. 이런 방식으로 외부에서 로직을 주입하는 형태
를 의존성 주입(dependency injection)
이라고 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| type Writer func(string)
func writeHello(writer Writer) {
writer("Hello World")
}
// if change in interface form?
// type Writer interface{
// writeHello( func(string) )
// }
// func writeHello(writer func(string) ) {
// writer("Hello World")
// }
func main(){
f, err := os.Create("test.txt")
if err != nil {
fmt.Println("Failed to create a file")
return
}
defer f.Close()
writeHello(func(msg string){
fmt.Fprintln(f, msg)
})
}
|
Questions
Q1. 함수의 파라미터로 가변인수를 가져오려면 어떤 Keyword(혹은 기호) 를 사용하는가?
sub1) 해당 기호로 int형 가변인수들을 가져오면 리턴 타입은 무엇인가?
sub2) 그 리턴타입으로 함수 파라미터를 변경 후 디버깅 하면 어떻게 되는가?
sub3) 모든 타입을 인수로 받기 위한 방법을 설명하라
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| // 1. ...
// sub1. []int (슬라이스)
// sub2. 에러가 발생한다. 함수 입력을 슬라이스로 입력하면 정상 디버깅된다.
// sub3. interface{} 를 사용한다.
func sum(nums ...int) int {
sum := 0
fmt.Printf("nums 타입: %T\n", nums)
for _, v := range nums {sum += v}
return sum
}
func sum2(nums []int) int {
sum := 0
fmt.Printf("nums 타입: %T\n", nums)
for _, v := range nums {sum += v}
return sum
}
func main() {
fmt.Println(sum(1, 2, 3, 4, 5))
fmt.Println(sum(10, 20))
fmt.Println(sum())
fmt.Println(sum2([]int{1, 2, 3, 4, 5}))
}
|
Q2. 함수 리터럴(Function Literal)은 무엇인가? 또 함수 리터럴에서 외부변수를 내부로 가져오는 것을 무엇이라 부르는가?
1
2
| 1) 함수명이 없는 형태로, 함수명으로 호출하지 않고 함수 타입 변수로만 호출하는 형태 : lamda
2) capture
|
Q3. 아래 코드를 보고 의존성 주입을 설명하라. 또한 아래 의존성 주입을 함수 리터럴이 아닌 인터페이스로 구현하라.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| type Writer func(string)
func writeHello(writer Writer) {
writer("Hello World")
}
// if change in interface form?
// type Writer interface{
// writeHello( func(string) )
// }
// func writeHello(f *os.File, msg string) {
// fmt.Fprintln(f, msg)
// }
func main(){
f, err := os.Create("test.txt")
if err != nil {
fmt.Println("Failed to create a file")
return
}
defer f.Close()
writeHello(func(msg string){
fmt.Fprintln(f, msg)
})
// writeHello(f, "hi hello")
}
|