프로그래밍 언어/Go

Go: 함수 (func)

Go 가 다른 언어가 다른 점은 클래스가 없다는 점이다. 메서드라고 표현하는 것마저도 표현 방식이 다를 뿐 일반 함수와 아주 유사하게 표시된다. 메서드에 대해서는 추후 알아보기로 하고, 지금부터는 Go 의 일반적인 함수에 대해 알아본다.

 

함수로직이나 알고리즘을 묶은 코드의 집합이다. 함수형 프로그래밍에서는 기본 단위로 사용되기도 한다. 함수를 사용하는 가장 큰 의미는 중복을 제거하는 일이다. 이것이 가장 먼저이며 그 다음으로는 함수 내부는 블랙박스로 하여 구현에 상관없이 추상화를 하여 함수 단위로 프로그램을 구성할 수 있도록 만드는 것이다. 함수의 입력과 출력만 알고 있으면 구현과 관계 없이 프로그램을 구성할 수 있다. 여기서 함수는 프로그램을 구성하는 기본 단위로써 동작한다.

Hello, Go

다시 처음으로 돌아가서, Hello, Go 의 코드를 봐보자. 여기서 우리가 주목해야 할 것은 func main() {} 부분이다.

package main

import "fmt"

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

이 코드를 보면, main() 함수가 정의되어 있다. 코드에서 보이듯 함수를 선언하는 키워드로는 func 을 사용한다는 것을 알 수 있고, 그 다음으로 함수의 이름을 준다는 것을 알 수 있다. main() 함수는 우리가 go run 을 통해 Go 프로그램을 실행하면 가장 처음 실행되는 함수이며 Go 프로그램의 본체라고도 볼 수 있다.

 

이렇듯 Go 는 함수가 무대로써 사용할 수 있도록 만들어진 언어라는 것을 볼 수 있다. 이제, 함수에 대해 조금 더 깊게 들어가보자.

구성요소

아래의 코드는 Go 함수의 많은 부분을 이야기한다. 하나 하나씩 파헤쳐보자. Go 함수의 구성요소는 func 키워드, 함수의 이름, 입력, 출력정도가 있다.

package main

import (
	"fmt"
	"log"
)

func sayHello(message string) (string, error) {
	return message, nil
}

func main() {
	helloGo, err := sayHello("Hello, Go!")
	if err != nil {
		log.Fatal(err)
	}
	// -> Hello, Go!
	fmt.Println(helloGo)
}

func 키워드를 통해 sayHello 라는 함수를 정의했다. 그리고 맨 처음에 봤던 코드와는 다른 것을 볼 수 있는데, 바로 괄호 사이에 파라매터가 있다는 것과 함수를 호출하면 값을 돌려주는 것이다. 함수는 기본적으로 어떠한 값을 입력하면 출력을 해주게 되어있다. 출력은 있는 경우도 있고 없는 경우도 있으나, 있는 경우가 더 많다.

 

sayHello 를 보자면, string 타입의 message 라는 파라매터를 입력으로 받으며, 출력으로는 string, error 타입에 해당하는 값을 돌려주게 되는 것이다. 여기서 Go 함수가 가지는 특이한 점이 드러나게 되는데, Go 는 리턴 값으로 여러 개의 값을 돌려줄 수 있다. 다른 언어처럼 배열로 해야한다거나 객체를 써서 해야한다거나 하는 것이 아니다.

 

또한 이것은 자료구조로 알아서 변하는 것이 아니라, 그냥 여러 개의 값을 반환하는 것 뿐이다. 다중 값 반환을 지원하는 타 언어에서는 암묵적으로 다중 값 반환에 대해 튜플로 변환하는 경우가 있는데, Go 에서는 그런식으로 처리하지 않는다.

nil 은 주로 에러가 없을 때 반환한다. nil 은 맵, 슬라이스에 대해 제로 값으로써 동작하기도 한다.

return

return 키워드는 함수에서 값을 반환할 때 사용하는데, return 을 사용하면 그 이후의 코드는 실행되지 않는다. 즉, return 을 사용하면 함수를 그 자리에서 멈추고 밖으로 내보낸다. 일반적으로 함수의 맨 마지막에서 사용한다. 조건문과 함께 사용하는 경우는 상당히 일반적이다.

package main

import (
	"errors"
	"fmt"
	"log"
)

func sayHello() (string, error) {
	if err := errors.New("Err"); err != nil {
		return "", err
	}
	return "Hello, Go!", nil
}

func main() {
	helloGo, err := sayHello()
	if err != nil {
		// -> Err
		log.Fatal(err)
	}
	fmt.Println(helloGo)
}

이름이 있는 리턴 값

리턴을 명시할 때 이름도 같이 명시해주는 것으로 함수 내부에서 변수를 별도로 선언하지 않고도 값을 반환해준다.

package main

import "fmt"

func sayHello() (helloGo string) {
	helloGo = "Hello, Go!"
	return
}

func main() {
	helloGo := sayHello()

	// -> Hello, Go!
	fmt.Println(helloGo)
}

리턴 값에 이름을 준다는 것은, 해당 리턴 값이 의미하는 것을 다른 개발자에게 알려줄 수 있는 도구가 되기도 한다.

package main

import "fmt"

func sayHello() (helloGo string) {
	return "Hello, Go!"
}

func main() { /* ... */ }

함수 스코프

함수는 또한 하나의 스코프로 동작할 수 있으며 함수 자신보다 상위의 스코프에서 선언한 변수에 대해서는 접근할 수 있지만, 함수내부에서 선언한 변수에 대해서 상위 스코프에서 접근할 수 없다. 따라서 아래의 코드에서 패키지 스코프에서 선언한 message 변수에는 접근이 가능하지만, 함수 스코프에서 선언한 helloGo 에 대해서는 접근할 수 없다.

package main

var (
	message string = "Hello, Go!"
)

func sayHello() {
	helloGo := message
}

func main() {
	sayHello()
	// fmt.Println(helloGo) // -> Error
}

익명 함수

Go 는 함수가 중요하게 여겨지고 있으므로, 익명 함수또한 존재한다. 함수를 파라매터로 넘길 수 있으며 값으로 취급할 수 있다. 그 말은 함수형 프로그래밍에서 일급 함수고차 함수로써의 역할도 할 수 있다는 이야기다. 함수를 값으로써 여길 수 있다면 일급 함수이며 함수를 리턴하거나 함수를 파라매터로 받거나 리턴한다면 그 함수는 고차 함수라고 이야기할 수 있다.

package main

import "fmt"

func each(arr []string, iterator func(string)) {
	for _, v := range arr {
		iterator(v)
	}
}

func main() {
	printValue := func(v string) {
		fmt.Println(v)
	}

	// each([]string{"Hello, Go!", "Who are you?"}, func(v string) {
	// 	fmt.Println(v)
	// })
	each([]string{"Hello, Go!", "Who are you?"}, printValue)
}

Go 함수가 지원하지 않는 것들

Go 언어의 함수가 다른 언어에 비해 지원하지 않는 것들을 알아둘 필요가 있다. Go 언어만 하는 것이 아니라면 Go 를 학습하면서 의아한 점이 몇몇 생기기 때문이다. Go 에서 함수 파트에 대해 지원하지 않는 것은 다음과 같다.

 

  • 선택적 파라매터
  • 파라매터에 대한 기본 값(default value)
  • 메서드 오버로딩

선택적 파라매터는 파라매터가 있을 수도 있고 없을 수도 있는 것이며, 기본 값은 함수 호출시 해당 파라매터에 대한 값을 넘기지 않았을 경우 적용할 값이다. 구글에서는 이러한 것을 언어 차원에서 배제시켜버렸다. 타언어에서 함수에 대해 저러한 것들은 함수의 가능성과 다양성을 발휘할 수 있도록 해주었지만, Go 언어는 단순함과 일관성을 추구하므로 함수의 복잡성이 증가하는 것을 일찌감치 배제하기 위해 저런 선택을 한 것으로 생각된다.

'프로그래밍 언어 > Go' 카테고리의 다른 글

Go: 반복문 (for, range)  (0) 2020.11.25
Go: 조건문 (if, switch)  (0) 2020.11.25
Go: 패키지(package, import)  (0) 2020.11.23
Go: 함수 (func)  (0) 2020.11.21
Go: 변수와 상수 (var, const)  (0) 2020.11.19
Go: Hello, Go  (0) 2020.11.17