상세 컨텐츠

본문 제목

Go 언어에서 함수

Go lang

by techbard 2022. 3. 12. 19:52

본문

반응형
  1. 함수는 독립된 코드 블록
  2. 함수에 파라미터를 전달할 때는 값 전달과 주소 전달 방식 사용
// 함수에 값을 전달했고 함수에서 변수를 변경했지만 원래 변수값은 변하지 않았다.

package main

import (
	"fmt"
)

func main() {
	// Pass By Value
	str := "hello"

	fmt.Println("Before func:", str)    // 함수에 넘기기 전 변수값
	PassByValue("Func processed:", str) // 넘긴 이후 함수에서 변경
	fmt.Println("After func:", str)     // 함수에서 변경해도 원 변수값은 변하지 않음
}

func PassByValue(msg string, text string) {
	text = "hi"
	msg = msg + " " + text + " " + "world"
	fmt.Println(msg)
}

// 결과
// Before func: hello
// Func processed: hi world
// After func: hello

 

// 함수에 값을 전달했고 함수 안에서 전달한 변수를 변경했다.

package main

import (
	"fmt"
)

func main() {
	// Pass by reference
	str := "hello"

	fmt.Println("Before func:", str)                // 함수에 넘기기 전 변수값
	PassByReference("Func processed string:", &str) // 넘긴 이후 변경된 문자열 출력
	fmt.Println("After func:", str)                 // 넘긴 변수의 값도 변경되었다.
}

func PassByReference(msg string, text *string) {
	*text = "hi"
	msg = msg + " " + *text + " " + "world"
	fmt.Println(msg)
}

// 결과
// Before func: hello
// Func processed string: hi world
// After func: hi
  1. 함수의 가변 파라미터는 마지막 파라미터에만 사용
  2. 가변 파라미터도 주소를 넘길 수 있음
package main

import (
	"fmt"
)

func main() {
	var sum1, sum2 int

	sum1 = VariadicFunc(1, 2, 3) // 3개의 파라미터도 받는다.
	fmt.Println("sum1:", sum1)

	sum2 = VariadicFunc(1, 2) // 2개의 파라미터도 받는다.
	fmt.Println("sum2:", sum2)

	fmt.Println(" ")

	var (
		one   = 1
		two   = 2
		three = 3
	)

	VariadicFuncByReference(&one, &two, &three) // 변수의 주소를 넘김

	fmt.Println("one:", one) // 함수에 넘긴 원본 변수값이 변함
	fmt.Println("two:", two)
	fmt.Println("three:", three)
}

func VariadicFunc(nums ...int) int {
	var total int
	for _, n := range nums {
		total += n
	}
	return total
}

func VariadicFuncByReference(nums ...*int) { // 가변인자로 주소를 받음
	for _, n := range nums {
		*n -= 1 // 주소의 변수값을 읽어 처리
	}
}

// 결과
// sum1: 6
// sum2: 3
//
// one: 0
// two: 1
// three: 2

1. 함수 복수값 리턴

package main

import (
	"fmt"
)

func main() {
	count, total := sum(1, 2, 3, 4, 5)
	fmt.Println("count:", count, "total:", total)
}

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

// 결과
//
// count: 5 total: 15
  1. 리턴 파라미터명을 명시하는 함수 리턴
package main

import (
	"fmt"
)

func main() {
	count, total := sum(1, 2, 3, 4, 5)
	fmt.Println("count:", count, "total:", total)

	count2, total2 := named_return_sum(1, 2, 3)
	fmt.Println("count2:", count2, "total2:", total2)
}

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

func named_return_sum(nums ...int) (count int, sum int) { // 리턴 파라미터 명과 타입을 명시
	for _, n := range nums {
		sum += n
		count++
	}
	return // 명시한 리턴 파라미터를 반환
}

// 결과
//
// count: 5 total: 15
// count2: 3 total2: 6
  1. 익명함수
  2. 함수명이 없다.
  3. 함수를 변수에 할당 가능
  4. 함수를 다른 함수의 파라미터로 넘길 수 있다.
package main

import (
	"fmt"
)

// 익명함수 사용, 가변인자 사용, 리턴 파라미터 명과 타입을 여러 개 명시
func main() {
	anonymous_sum := func(nums ...int) (count int, sum int) {
		for _, n := range nums {
			sum += n
			count++
		}
		return
	}

	count, sum := anonymous_sum(1, 2, 3)
	fmt.Println("count:", count, "sum:", sum)
}

// 결과
//
// count: 3 sum: 6
  1. 익명함수를 변수에 넣어 전달
  2. 다른 함수의 파라미터로 함수 자체를 전달
package main

import (
	"fmt"
)

func main() {
	p := fmt.Println

	anon_sum := func(x int, y int) int {
		return x + y
	}

	// 익명함수를 변수에 넣어 전달
	c1 := calc(anon_sum, 1, 2)
	p("sum of c1:", c1)

	// 다른 함수의 파라미터로 함수 자체를 전달
	c2 := calc(func(x int, y int) int { return x + y }, 1, 2)
	p("sum of c2:", c2)
}

func calc(f func(int, int) int, x int, y int) int {
	return f(x, y)
}

// 결과
//
// sum of c1: 3
// sum of c2: 3
  1. 함수 원형을 type으로 정의
package main

import (
	"fmt"
)

func main() {
	f := func(x int, y int) int {
		return x + y
	}

	r1 := calc(f, 1, 2)
	fmt.Println(r1)
}

// 함수 원형을 정의
type sum func(int, int) int

func calc(f sum, x int, y int) (ret int) {
	ret = f(x, y)
	return
}


// 결과
//
// 3
  1. 클로저
  2. 익명함수가 자신이 가지지 않은 변수를 리턴
  3. 자신이 가지지 않은 변수이므로 호출되어도 그 변수값이 유지
package main

import (
	"fmt"
)

func main() {
	c := addString() // 함수를 변수에 저장
	fmt.Println(c("hello"))
	fmt.Println(c("world"))
	fmt.Println(c("friend"))
}

// addString() 함수의 리턴이 익명함수
// 리턴된 익명함수의 리턴값은 string
func addString() func(text string) string {
	strs := ""
	// 익명함수를 리턴하지만 그 익명함수가 다시 지역변수를 리턴
	return func(text string) string {
		strs = strs + "|" + text
		return strs
	}
}

// 결과
//
// |hello
// |hello|world
// |hello|world|friend
  1. 클로저로 지역 변수를 유지하는 방식에 대한 나의 생각
  2. 구조체를 지역 변수처럼 써도 유지되지 않을까?
package main

import (
	"fmt"
)

func main() {
	c := count{} // 구조체를 변수에 할당
	fmt.Println("Initial counter:", c.counter)
	addCounter(&c)                                // 함수에 구조체 주소 넘김
	addCounter(&c)                                // 함수 안에서 구조체 값 증가
	fmt.Println("After func counter:", c.counter) // 결국
}

func addCounter(c *count) {
	c.counter++
}

type count struct {
	counter int
}

// 결과
//
// Initial counter: 0
// After func counter: 2
  1. 함수와 메서드
  2. 함수: 파라미터, 리턴값
  3. 메서드: 리시버, 파라미터, 리턴값
  4. 리시버: 구조체와 함수를 연결시키는 정의 (구조체 타입을 받는 함수)
package main

import (
	"fmt"
)

func main() {
	e1 := &employees{
		name: "mike", age: 30, country: "USA",
	}

	e1.ageUp()
	fmt.Println(e1.age)
	e1.ageDown()
	fmt.Println(e1.age)
}

type employees struct {
	name    string
	age     int
	country string
}

func (e *employees) ageUp() {
	e.age++
}

func (e *employees) ageDown() {
	e.age--
}

// 결과
//
// 31
// 30
  1. 함수와 메서드의 구분
Method Function
It contain receiver. It does not contain receiver.
It can accept both pointer and value. It cannot accept both pointer and value.
Methods of the same name but different types can be defined in the program. Functions of the same name but different type are not allowed to define in the program.
It cannot be used as a first-order object. It can be used as first-order objects and can be passed.
  1. Go 언어에서는 non-struct type의 receiver도 지원한다.
package main

import (
	"fmt"
)

type myint int

func (i1 myint) add(i2 myint) (out myint) {
	out = i1 + i2
	return
}

func main() {
	v1 := myint(1)
	v2 := myint(2)
	ret := v1.add(v2)
	fmt.Println("result:", ret)
}

// 결과
//
// result: 3

  • 함수의 리턴으로 주소를 전달
package main

import (
	"fmt"
)

func main() {
	s := sum(1, 2, 3, 4, 5)
	fmt.Println("The sum is", *s)
}

func sum(values ...int) *int {
	fmt.Println(values)
	ret := 0
	for _, v := range values {
		ret += v
	}
	return &ret
}

// 결과
//
// [1 2 3 4 5]
// The sum is 15

  • Go에서 함수 에러 처리의 관례
package main

import (
	"fmt"
)

func main() {
	d, err := divide(5.0, 0.0)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(d)
}

func divide(a, b float64) (float64, error) {
	if b == 0.0 {
		return 0.0, fmt.Errorf("Cannot divide by zero")
	}
	return a / b, nil
}

// 결과
//
// Cannot divide by zero

package main

import (
	"fmt"
)

func main() {
	g := greeter{
		greeting: "Hello",
		name:     "Go",
	}
	g.greet()
}

type greeter struct {
	greeting string
	name     string
}

// syntatic sugar
// func (g *greeter) greet() {
func (g greeter) greet() {
	fmt.Println(g.greeting, g.name)
	g.name = ""
}

// 결과
//
// Hello Go

Parameters

- Comma delimited list of variables and types
- func foo(bar string, baz int)
- Parameters of same type list type once
- func foo(bar, baz int)
- When pointers are passed in, the function can change the value in the caller
- This is always true for data of slices and maps
- Use variadic parameters to send list of same types in
- Must be last parameter
- Received as a slice
- func foo(bar string, baz ...int)
- Retuen values
- Single return vales just list type
- func foo() int
- Multiple return value list types surrounded by parentheses
- func foo() (int, error)
- The (result type, error) paradign is a very common idom.
- Can use names return values
- Initializes returned variable
- Return using return keyword on its own
- Can return addressed of local variables
- Automatically promoted from local memory (stack) to shared memory (heap)
- Anonymous functions
- Functions don't have names if they are
== func() {
	...
	}()
- Assigned to a variable or passed as an argument to a function
== a := func() {
	...
	}
	a()
- Functions as types
- Can assign functions to variables or use as argument and return values in functions
== var f func(string, string, int) (int, error)
- Methods
=== Function that executes in context of a type
- Format
== func (g greeter) greet() {
	...
	}
- Receiver can be value or pointer
== Value receiver gets copy of type
== Pointer receiver gets pointer to type

  • 익명 함수를 리턴
package main

import (
	"fmt"
)

func main() {
	sum := adder()
	for i := 0; i < 10; i++ {
		fmt.Println(sum(i))
	}
}

func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

// 결과
//
// 0
// 1
// 3
// 6
// 10
// 15
// 21
// 28
// 36
// 45

  • 함수 이름을 다른 함수에 인자로 넘김
package main

import (
	"fmt"
)

func main() {
	cycleNames([]string{"cloud", "tifa", "barret"}, sayGreeting)

}

func sayGreeting(name string) {
	fmt.Printf("Good morning %v\n", name)
}

func sayBye(name string) {
	fmt.Printf("Goodbye %v\n", name)
}

func cycleNames(name []string, f func(string)) {
	for _, v := range name {
		f(v)
	}
}

// 결과
//
// Good morning cloud
// Good morning tifa
// Good morning barret

  • Multiple Return Values
package main

import (
	"fmt"
	"strings"
)

func main() {
	fn1, sn1 := getInitials("tifa lockhart")
	fmt.Println(fn1, sn1)

	fn2, sn2 := getInitials("cloud strife")
	fmt.Println(fn2, sn2)

	fn3, sn3 := getInitials("barret")
	fmt.Println(fn3, sn3)
}

func getInitials(str string) (string, string) {
	s := strings.ToUpper(str)
	strs := strings.Split(s, " ")
	var initials []string
	for _, v := range strs {
		initials = append(initials, v[:1])
	}

	if len(initials) > 1 {
		return initials[0], initials[1]
	}

	return initials[0], "_"
}

// 결과
//
// T L
// C S
// B _

  • Receiver Functions with Pointer
package main

import (
	"fmt"
)

type bill struct {
	name  string
	items map[string]float64
	tip   float64
}

// format the bill
func (b *bill) format() string {
	fs := "Bill breakdown: \n"
	var total float64 = 0

	// list items
	for k, v := range b.items {
		fs += fmt.Sprintf("%-25v ...$%v \n", k+":", v)
		total += v
	}

	// add tip
	fs += fmt.Sprintf("%-25v ...$%v \n", "tip:", b.tip)

	// total
	fs += fmt.Sprintf("%-25v ...$%0.2f", "total:", total+b.tip)

	return fs
}

func main() {
	myBill := newBill("mario's bill")
	myBill.addItem("onion soup", 4.50)
	myBill.addItem("veg pie", 8.95)
	myBill.addItem("toffee pudding", 4.9)
	myBill.addItem("coffee", 3.25)
	myBill.updateTip(10)
	fmt.Println(myBill.format())
}

// make new bills
func newBill(name string) bill {
	b := bill{
		name:  name,
		items: map[string]float64{"pie": 5.99, "cake": 3.99},
		tip:   0,
	}
	return b
}

// update tip
func (b *bill) updateTip(tip float64) {
	b.tip = tip
}

// add an item to the bill
func (b *bill) addItem(name string, price float64) {
	b.items[name] = price
}

// 결과
//
// Bill breakdown:
// onion soup:               ...$4.5
// veg pie:                  ...$8.95
// toffee pudding:           ...$4.9
// coffee:                   ...$3.25
// pie:                      ...$5.99
// cake:                     ...$3.99
// tip:                      ...$10
// total:                    ...$41.58
반응형

관련글 더보기

댓글 영역