함수

1. 함수 생성 방법

package main

func main(){
  a := 1
    say(a)
}

func say(a int) {
    println(a)
}

func키워드를 이용해서 함수를 선언할 수 있고 가장 기본적으로 만드는 main도 함수 이다.



2. 매개변수(인자)

1) 전달 방식

Java처럼 primitive자료형은 pass by value, reference자료형은 pass by refernece가 아닌 go는 C처럼 *을 이용해서 value인지 reference인지 정해서 넘겨줄 수 있다.

func main(){
	a := 1
	increase1(a)
	println(a)		  //1
	increase2(&a)
	println(a)		  // 2
}

func increase1(a int) {
	a+=1
}

func increase2(a *int) {
	*a+=1
}

포인터를 이용해서 변수의 주소를 넘겨주면 pass by reference로 함수에서도 해당 함수를 호출한 곳의 변수의 값을 변경할 수 있다.


2) 중복된 자료형 생략

package main

import "fmt"

func main() {
	variousVariables(1,2,true, 1.2)
}

func variousVariables( a, b int, c bool, d float32){
	fmt.Println(a,b,c,d)    //1 2 true 1.2
}

위처럼 매개변수가 같은 자료형을 갖는다면 변수선언 방식과 비슷하게 자료형을 생략해서 마지막에만 작성을 해주어도 된다.


3) 가변 인자 함수

매개변수의 개수가 정해지지 않고 여러개를 전달할때 사용할 수 있는 함수 기법으로 을 이용하여 표현할 수 있다.

func main() {
    say(1,2,3,4)
    say(10)
}

func say(nums ...int) {
    for _, num := range nums {
        println(num)
    }
}



3. 리턴 값

go는 다른 언어들과는 다르게 리턴값을 void나 하나가 아닌 여러개를 가질 수 있다.

func main() {
    total1 := sum1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    println(total1)         //55

    count, total2 := sum2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    println(count, total2)  //10 55
}

func sum1(nums ...int) int {
    sum := 0
    for _, num := range nums {
        sum += num
    }
    return sum
}


func sum2(nums ...int) (int, int) {
  s := 0
  count := 0
	for _, n := range nums {
		s += n
		count++
	}
	return count, s
}

return값이 여러개일 경우에는 () 로 묶어 표시해야 하고 return문을 , 을 이용해서 여러개의 값을 return하도록 작성해주면 된다.


1) Named Return Parameter

return값에 이름을 지정하는 Named Return Parameter도 제공한다.

func main() {
    count, total2 := sum2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    println(count, total2)  //10 55
}
func sum2(nums ...int) ( count int, s int) {
	for _, n := range nums {
		s += n
		count++
	}
	return
}

위처럼 return값에 자료형만 작성하는 것이 아니라 변수명을 작성해 줄 수 있는데 이는 매개변수처럼 하나의 변수로써 생성이 되기 때문에 zero value로 처음에 선언이 되고 함수내부에서 변수처럼 사용이 가능하다. 또한 return문을 작성할때 필요로하는 return값들을 모두 작성해줄 필요없이 return만 작성해도 변수에 할당된 값을 그대로 반환해주기 때문에 쉽게 작성이 가능하다.



4. 익명 함수

JS의 익명함수 처럼 함수의 이름이 없는 함수로써 그자리에서 한번만 바로 실행하는 함수를 작성하거나 변수에 할당하기 위한 방식의 함수 이다.

func main() {
    sum := func(n ...int) int {
        s := 0
        for _, i := range n {
            s += i
        }
        return s
    }

    result := sum(1, 2, 3, 4, 5) //익명함수 호출
    println(result)
}

함수의 선언 방식은 거의 똑같지만 func키워드 뒤에 함수명이 오지 않고 이렇게 선언한 함수를 바로 변수에 할당하는 것을 볼 수 있다.

변수에 할당하게 되면 그 변수이름으로 함수를 똑같이 사용이 가능하다.



5. 일급 함수

go에서 함수는 하나의 데이터 타입으로 취급되기 때문에 다른 함수의 파라미터로 전달하거나 리턴값으로 사용할 수 있다.

package main

func main() {
    add := func(a int, b int) int {
        return a + b
    }

    result1 := calc(add, 10, 20)
    println(result1)

    result2 := calc(func(a int, b int) int { return a + b }, 10, 20)
    println(result2)

}

func calc(f func(int, int) int, a int, b int) int {
    result := f(a, b)
    return result
}

calc() 의 매개변수로 func키워드를 이용해 함수를 매개변수로 받을 수 있으며, 이때 매개변수타입과 reutrn타입을 작성해주어야 한다. 이렇게 함수를 매개변수로 받으면 해당 함수 내에서 인자로받은 함수를 사용할 수 있다.

함수의 매개변수로 함수를 넘겨줄때는 result1처럼 익명함수를 통해 선언된 함수타입 변수를 넘겨주거나 result2처럼 바로 익명함수를 작성해서 넘겨주는 방식도 가능하다.

1) 함수 type 정의

함수의 매개변수로 함수타입을 받을 때 매개변수의 타입과 return타입을 매번 작성해주는 것이 번거로울 수 있는데, C/C++의 typedef처럼 go도 type을 이용해 함수 원형을 정의 하여 반복코드를 줄일 수 있다.

package main

type funcII func(int,int) int

func main() {
	add := func(a int, b int) int {
		return a + b
	}
	result1 := calc(add, 10, 20)
	println(result1)
}

func calc(f funcII, a int, b int) int {
	result := f(a, b)
	return result
}

이렇게 함수원형을 정의하고 함수를 다른 함수에 전달하고 리턴받는 기능을 Delegate라고 부른다.





Reference

『Tucker의 Go 언어 프로그래밍』 스터디 요약 노트

Tags :

Related Posts

오브젝트: 코드로 이해하는 객체지향 설계

오브젝트: 코드로 이해하는 객체지향 설계

  • Books
  • 2021년 11월 12일

1. 객체지향 설계 설계란 코드를 배치하는 것이다. 좋은 설계란 오늘 요구하는 기능을 온전히 수행하면서 내일의 변경을 매끄럽게 수용할 수 있는 설계 요구사항은 항상 변하기 마련이다. 2. 객체지향 프로그래밍 부모 클래스에 기본적인 알고리즘의 흐름을 구현하고 중간에 필요한 처리를 자식 클래스에게 위임하는 디자인 패턴을 TEMPLATE METHOD 패턴 이라고 한다. 자식 클래스가 부모 클래스를 대신 하는 것이 업캐스팅 다형성이란 동일한 메시지를 수신했을 때 객체의 타입에 따라 다르게 응답할 수 있는 능력 상속은 구현 상속이 아니라 인터페이스 상속을 위해 사용해야 한다. 대부분의 사람들은 코드 재사용을 상속의 주된 목적이라고 생각하지만 이것은 오해다. 인터페이스를 재사용할 목적이 아니라 구현을 재사용할 목적으로 상속을 사용하면 변경에 취약한 코드를 낳게 될 확률이 높다. 상속의 가장 큰 문제점은 캡슐화를 위반한다는 것. 상속의 두번째 단점은 설계가 유연하지 않다는 것 3. 역할, 책임, 협력 코드를 재사용하는 경우에는 상속보다 합성을 선호하는 것이 옳지만 다형성을 위해 인터페이스를 재사용하는 경우에는 상속과 합성을 함께 조합해서 사용할 수 밖에 없다. 객체지향 패러다임의 관점에서 핵심은 역할, 책임, 협력이다. 객체지향 설계에서 가장 중요한 것은 책임이다. 객체에게 얼마나 적절한 책임을 할당하느냐가 설계의 전체적인 품질을 결정한다. 역할을 구현하는 가장 일반적인 방법은 추상 클래스와 인터페이스를 사용하는 것 협력의 관점에서 추상 클래스와 인터페이스는 구체 클래스들이 따라야 하는 책임의 집합을 서술한 것이다. 추상 클래스는 책임의 일부를 구현해 놓은 것이고 인터페이스는 일체의 구현 없이 책임의 집합만을 나열해 놓았다는 차이가 있지만 협력의 관점에서는 둘 모두 역할을 정의할 수 있는 구현 방법이라는 공통점을 공유한다. 4. 메시지와 인터페이스 강조하고 싶은 것은 소프트웨어 설계에 법칙이란 존재하지 않는다 라는 것이다. 원칙을 맹신하지 마라. 원칙이 적절한 상황과 부적절한 상황을 판단할 수 있는 안목을 길러라. 설계는 트레이드오프의 산물이다. 소프트웨어 설계에 존재하는 몇 안되는 법칙 중 하나는 경우에 따라 다르다 라는 사실을 명심해라. 프로시저는 부수효과를 발생시킬 수 있지만 반환할 수 없다. 함수는 값을 반환할 수 있지만 부수효과는 발생시킬 수 없다. 5. 객체 분해 하향식은 이미 완전히 이해된 사실을 서술하기에 적합한 방법이다. 그러나 하향식은 새로운 것을 개발하고, 설계하고, 발견하는 데는 적합한 방법이 아니다. 이것은 수학과 아주 유사하다. 수학 교과서는 계산의 과정을 논리적인 순서로 서술한다. 공인되고 증명된 이론이 뒤이은 이론을 증명하기 위해 사용된다....

Read More
AVL Tree

AVL Tree

BST (이진 탐색 트리)를 기반으로 둔 Tree. 어떤 노드를 기준으로 하더라도 왼쪽자식의 깊이와 오른쪽 자식의 깊이 차이가 1을 넘지 않는 트리 1. 용어 개념 정리 균형치 (Balance factor) : 자식노드의 깊이 차이 ( 왼쪽 서브트리의 높이 – 오른쪽 서브트리의 높이 ) BF는 -1, 0, 1이 기준이며, 이 범위를 벗어난다면, 그 트리의 균형은 깨진것이다. 2. 특징 BST의 모든 특징을 갖는다....

Read More
Spring 요청처리 내부구조

Spring 요청처리 내부구조

내가 이해하고 나중에 보기 위해서 정리하는 스프링 요청 처리 구조 내부 구조 요약 1. Tomcat (Servlet Container) 톰캣은 WAS로써 미들웨어역할을 하지만 아파치의 일부분 기능을 서비스(httpd(웹서비스 데몬) native 모듈 포함)하고있어 Web Server역할도 수행할 수 있다....

Read More