본문 바로가기
Language/Golang

Golang (Go언어) 함수(Function)

by HeeWorld 2024. 10. 30.

Tucker의 Go 언어 프로그래밍 책과 유튜브를 통해 학습 중입니다.

 

Golang 마스코트 Gopher(고퍼)

 

함수

* 반복이 싫어서 만들어진 것, 반복적으로 호출해야 하는 것을 함수로 만들어서 묶어 놓고 호출(call)해서 쓰게 만듦.

- 함수 정의 키워드

- 함수명

- 매개변수

- 반환 타입

- 함수 코드 블록

package main

import "fmt"

func Add(a int, b int) int {
	return a + b
}

func main() {
	c := Add(3, 6)
	fmt.Println(c)
}

// 결과

9

 

func Add(a int, b int) int {

→ func Add를 만들어서 입력을 2개를 받고 출력은 int타입으로 함

 

return a + b

→ a+b의 결과 값을 반환한다

 

c := Add(3, 6)

→ c는 add의 3,6을 한 결과를 사용함.

→ Add(3,6)이 실행되면, 위에 func Add의 함수를 실행하게 되고 한 줄씩 수행하다가 return을 만나면 다시 C :=의 Add(3,6) 자리로 돌아옴.

    함수 출력 결과가 반환 값으로 치환됨.

 

* 함수를 호출할 때

- 함수를 호출할 때 입력 받는 값을 armument라고 하며, 아큐먼트 혹은 인수라고 함.

- 함수가 외부로부터 입력 받는 변수를 parameter라고 하며, 매개변수 혹은 파라미터라고 함.

 

* 함수 호출 및 매개변수 전달 과정

→ 함수를 호출하여 입력한 값은 실제 함수에서는 그대로 사용하는 것이 아니라, 값을 복사해 사용하게 됨.

함수 호출 시, 매개변수에 복사되어 전달되는 과정

 

(1) Add 함수를 호출하여 (2) 매개변수를 선언하고 입력한 인숫값을 복사함(a에는 3이, b에는 6이 복사) ⇢ 이는 함수 내에서 선언한 a,b 두 변수에 초깃값으로 3과 6을 대입하는 것과 같음

(3) return을 사용하여 함수 결과가 반환 되고 반환은 값을 전달함

(4) 반환된 값은 함수가 호출된 곳을 대체하는 것과 같으며 (5) 호출한 함수가 종료되면 함수에서 사용한 지역 변수에 접근할 수 없게 됨.

return으로 함수 결과가 반환되면서 함수가 즉시 종료되어 함수를 호출했던 위치로 명령 포인터(Instruction Pointer)가 되돌아 가서 수행

마지막으로 c에 반환 값이 대입(복사)됨.

 

★ '인수는 매개변수로 복사되어 매개변수와 함수 내에서 선언된 변수는 함수가 종료되면 변수 범위를 벗어나 접근하지 못함' ★

✓ 다른 언어는 값 전달과 레퍼런스 전달 두 가지를 지원하기도 하지만, Go에서는 값 전달만 지원

 

* 함수를 사용하는 이유

- 함수를 사용해서 반복되는 코드를 묶을 수 있음

- 함수를 이용해 중복 코드를 제거하여 간결하게 코드를 만들 수 있음

package main

import "fmt"

func main() {
	math := 80
	eng := 74
	history := 95
	fmt.Println("김길동 님의 평균 점수는", (math+eng+history)/3, "입니다.")

	math = 88
	eng = 92
	history = 53

	fmt.Println("송이등 님의 평균 점수는", (math+eng+history)/3, "입니다.")

	math = 78
	eng = 73
	history = 78

	fmt.Println("박삼등 님의 평균 점수는", (math+eng+history)/3, "입니다.")
}

// 결과

김길동 님의 평균 점수는 83 입니다.
송이등 님의 평균 점수는 77 입니다.
박삼등 님의 평균 점수는 76 입니다.

 

→ 해당 코드는 각 변수에 점수를 입력하는 코드와 점수를 출력하는 코드가 3번 반복 됨. 만약, 학생이 300명이나 3000명이라면 해당 방식은 효율적이지 못함.

 

✓ 아래 코드를 통해 중복 코드를 없애는 예제 코드 확인

package main

import "fmt"

func PrintAvgScore(name string, math int, eng int, history int) {
	total := math + eng + history
	avg := total / 3
	fmt.Println(name, "님 평균 점수는", avg, "입니다.")
}

func main() {
	PrintAvgScore("김길동", 80, 74, 95)
	PrintAvgScore("송이등", 88, 92, 53)
	PrintAvgScore("박삼등", 78, 73, 78)
}

// 결과

김길동 님 평균 점수는 83 입니다.
송이등 님 평균 점수는 77 입니다.
박삼등 님 평균 점수는 76 입니다.

 

먼저, PrintAvgScore() 함수를 정의하고, 이름과 각 성정을 입력받아 이름과 평균 점수를 출력하는 함수 만듦.

PrintAvgScore() 함수를 반복 호출하여 처리하면, 효율적으로 코딩을 할 수 있고 추후 프로그램 변경 요구에도 간단하게 대처할 수 있음.

또한, 관련된 코드를 묶어서 이름을 부여하기에 코드를 읽기에도 편함.

 

 

멀티 반환 함수

- 함수는 값을 여러 개 반환할 수 있고, 반환 값이 여럿일 때는 반환 타입들을 소괄호로 묶어서 표현함.

package main

import "fmt"

func Divide(a, b int) (int, bool) {
	if b == 0 {
		return 0, false
	}
	return a / b, true
}

func main() {
	c, success := Divide(9, 3)
	fmt.Println(c, success)
	d, success := Divide(9, 0)
	fmt.Println(d, success)
}

// 결과

3 true
0 false

 

func Divide(a, b int) (int, bool) {

→ Divide함수 를 정의하고, 입력 값 a,b을 int 형식으로 받고(Go에서는 동일한 형식은 한 번만 적어도 됨) int(첫 번째), bool(두 번째) 타입으로 나옴.


if b == 0 {

→ 조건문 만약에 b가 0과 같다면


return 0, false

→ b가 0이 맞다면, 첫 번째 값으로 0을 반환하고(0으로 나누는 숫자는 0), 두 번째 값으로 false를  반환(나누기를 성공 했느냐/못했느냐)

=> 근데  return이 되면 더 이상 다음 줄을 실행하지 않고 끝남.

return a / b, true

 위에서 b가 0이 아니라면, 실행하는 줄로 a/b의 결과 값과 true를 반환

c, success := Divide(9, 3)

→ Divide 함수를 호출하여 a(첫 번째)에는 9 값을, b(두 번째)에는 3 값을 넣음(push)


fmt.Println(c, success)

→ 들어온 값을 출력함(pop), c는 3을, success는 true를 출력


d, success := Divide(9, 0)

→ Divide 함수를 호출하여, 첫 번째 반환값은 c에, 두 번째 반환값은 success에 대입

→ 선언 대입문 :=을 사용 시, 앞에서 선언한 success를 또 선언한 것으로 여러 개를 한번에 선언 대입문을 쓸 때

    하나라도 선언하지 않은 것이 있다면 선언이 가능

 

 

변수명 지정하여 반환하기

- 함수 선언부에 반환 타입을 적을 때, 변수명까지 지정하면 return문으로 해당 변수를 명시적으로 반환하지 않아도 값을 반환할 수 있음

package main

import "fmt"

func Divide(a, b int) (result int, success bool) {
	if b == 0 {
		result = 0
		success = false
		return
	}
	result = a / b
	success = true
	return
}

func main() {
	c, success := Divide(9, 3)
	fmt.Println(c, success)
	d, success := Divide(9, 0)
	fmt.Println(d, success)
}

// 결과

3 true
0 false

 

func Divide(a, b int) (result int, success bool) {

→ 출력 값 int, bool에 이름을 정의하면, 함수 안에서 변수처럼 사용할 수 있음

* 모두 지정하거나, 모두 지정하지 않거나 해야 함.

result = 0

→ 위에서 정의한 타입 이름

 

success = false

→ 위에서 정의한 타입 이름

 

return

→ 위에서 정의한 출력 값의 값이 리턴 됨, 이름을 적었기 때문에 리턴 값을 적지 않아야 함.

 

 

재귀 호출(Recursive call)

- 함수 안에서 자기 자신 함수를 다시 호출 하는 것

package main

import "fmt"

func printNo(n int) {
	if n == 0 {
		return
	}
	fmt.Println(n)
	printNo(n - 1)
	fmt.Println("After", n)
}

func main() {
	printNo(3)
}

// 결과

3
2
1
After 1
After 2
After 3

 

재귀함수가 호출되는 순서

 

순서: 4)의 printNo()함수를 먼저 호출하여 1) 재귀 호출 탈출 조건을 확인하는데, n=0이 아니라면, PrintNo(n-1)을 다시 호출함

 

N=3

PrintNo(3)을 호출하여 n=3으로 조건 1을 실행  → 3 == 0 (false)

조건이 맞지 않아서 (n은 0이 아님) 아래 fmt.Println(n)을 출력 =3 

그리고 printNo(n-1)을 호출하여 N의 값이 2가 됨.

 

N=2

다시, PrintNo(2)을 호출하여 n=2으로 조건 1을 실행  → 2 == 0 (false)

조건이 맞지 않아서 (n은 0이 아님) 아래 fmt.Println(n)을 출력 =2 

그리고 printNo(n-1)을 호출하여 N의 값이 1이 됨.

 

N=1

다시, PrintNo(1)을 호출하여 n=1으로 조건 1을 실행  → 1 == 0 (false)

조건이 맞지 않아서 (n은 0이 아님) 아래 fmt.Println(n)을 출력 =1

그리고 printNo(n-1)을 호출하여 N의 값이 0이 됨.

 

N=0

다시, PrintNo(0)을 호출하여 n=0으로 조건 1을 실행  → 0 == 0 (true)

조건에 맞아서, return 되어 PaintNo(n-1)로 감 => 호출 된 자리로 돌아감.

 

PrintNo(1)이 PrintNo(0)을 호출했기 때문에 PrintNo(1)로 돌아가서 'After 1'이 출력

그리고 다시 return되어 PrintNo(2)가 PrintNo(1)을 호출했기 때문에 PrintNo(2)로 돌아가서 'After2'가 출력

또 다시 return되어 PrintNo(3)이 PrintNo(2)를 호출했기 때문에 PrintNo(3)으로 돌아가 'After3'이 출력

PrintNo(3)을 호출한 함수가 func main()이라서 main으로 돌아가면서 프로그램이 종료 됨.

 

 

재귀 호출에서 중요한 것은 재귀 호출 탈출 조건

→ 탈출 조건이 명확하지 않으면 강제 종료하기 전까지 무한대로 실행 됨.

 

재귀 호출을 할 때마다 Stack이 하나씩 쌓이고, Stack이 계속 늘어나다 어느 순간 정해진 사이즈를 넘기면 프로그램이 강제 종료 됨.

그러나 Go는 Stackdㅣ 자동증가 되는 것을 사용하고 있어서 계속 증가되며 메모리를 사용하며 메모리가 다 찰때까지 증가됨.

(다른 언어는 고정길이가 정해져있음)

 

 

 

'Language > Golang' 카테고리의 다른 글

Golang (Go언어) IF문 (조건문)  (4) 2024.10.30
Golang (Go언어) 상수(Constant)  (0) 2024.10.30
Golang (Go언어) 연산자(Operator)  (0) 2024.10.26
Golang (Go언어) fmt 패키지  (3) 2024.10.26
Golang (Go언어) 변수  (1) 2024.10.24