데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      (go-06) Go If, For, Switch, Function

      seungkyua 22.11.03
      1,231 6 2

      언어를 배울 때 가장 기본적인 것 중에 하나가 비교문(if), 반복문(for), 함수(function) 이다.

      If 문

      if 는 비교 구문에서 사용되는 키워드 이다. 변수 선언과 동시에 비교를 할 수 있는데 이 때 선언된 변수는 해당 if 문 block 안에서만 유효하다. 그렇기 때문에 마지막 라인은 num 변수는 인식하지 못해 에러가 난다.

      func main() {
      	if num := 5; num == 0 {
      		fmt.Println("False")
      	} else if num < 10 {
      		fmt.Println("True")
      	} else {
      		fmt.Println("Big number")
      	}
      	fmt.Println(num)
      }
      
      --- output ---
      ./main_05_for_function.go:17:14: undefined: num

      if 문 밖에서 선언한 변수는 if 문 안과 밖 모두에서 사용할 수 있다. 하지만 if 문 안에서 같은 변수명을 재정의 하면 해당 변수 값은 오버라이드 된다.

      func main() {
      	num := 10
      	if num > 0 {
      		fmt.Println("outer num =", num)
      		num := 0
      		fmt.Println("inner num =", num)
      	}
      	fmt.Println("outer num =", num)
      }
      
      --- output ---
      outer num = 10
      inner num = 0
      outer num = 10

      For 문

      for lloop 는 반복문이며 slice 나 map 타입에 대해서 range 를 사용하여 item 을 하나씩 가져온다. slice 를 range 로 가져오면 index, value 2개의 값으로 넘어온다. value 만을 사용하고 싶으면 index 부분은 _ 로 처리하여 버릴 수 있다. 또한 index 1개의 값만 가져올 수 있다.

      func main() {
      	nums := []int{10, 20, 30, 40, 50}
      	for i, v := range nums {
      		fmt.Println("index =", i, ",", "value =", v)
      	}
      	for _, v := range nums {
      		fmt.Println("value =", v)
      	}
      	for i := range nums {
      		fmt.Println("index =", i)
      	}
      }
      
      --- output ---
      index = 0 , value = 10
      index = 1 , value = 20
      index = 2 , value = 30
      index = 3 , value = 40
      index = 4 , value = 50
      value = 10
      value = 20
      value = 30
      value = 40
      value = 50
      index = 0
      index = 1
      index = 2
      index = 3
      index = 4

      map 도 slice 와 마찬가지로 range 로 가져올 수 있다. 이 때 return 되는 값은 key, value 이다.

      func main() {
        m := map[string]int{
      		"a": 1,
      		"b": 2,
      		"c": 3,
      	}
      	for k, v := range m {
      		fmt.Println("key =", k, ",", "value =", v)
      	}
      	for _, v := range m {
      		fmt.Println("value =", v)
      	}
      	for k := range m {
      		fmt.Println("key =", k)
      	}
      }
      
      --- output ---
      key = c , value = 3
      key = a , value = 1
      key = b , value = 2
      value = 2
      value = 3
      value = 1
      key = a
      key = b
      key = c

      Switch

      선택문은 switch 로 가능하다. 각 case 마다 break 문이 없어도 되며, case 에는 , 로 여러 개의 값을 지정할 수 있다.

      func main() {
        alpha := []string{"a", "ab", "abc", "abcd", "abcde", "abcdef", "abedefg"}
      	for _, a := range alpha {
      		switch l := len(a); l {
      		case 1, 2:
      			fmt.Println(a, "length = 2 or 3")
      		case 3:
      			fmt.Println(a, "length = 3")
      		case 4, 5, 6:
      			fmt.Println(a, "length = 4, 5 or 6")
      		default:
      			fmt.Println(a, "length > 6")
      		}
      	}
      }
      
      --- output ---
      a length = 2 or 3
      ab length = 2 or 3
      abc length = 3
      abcd length = 4, 5 or 6
      abcde length = 4, 5 or 6
      abcdef length = 4, 5 or 6
      abedefg length > 6

      Function

      함수는 func, function name, parameter, return value 로 만들 수 있다. 아래 2개의 숫자를 받아서 나누는 div 함수는 아래와 같이 정의 할 수 있다. parameter 가 같은 타입이면 앞의 타입은 생략할 수 있다.

      func main() {
        result := div(4, 2)
      	fmt.Println(result)
      }
      
      // func div(n int, d int) int { 는 n, d 파라미터의 타입이 같아 아래와 같이 바꿀 수 있음
      func div(n, d int) int {
      	if d == 0 {
      		return 0
      	}
      	return n / d
      }
      
      --- output ---
      2

      , 로 구분하여 여러 개의 아규먼트를 넘길 수 있으며 이 때 받는 함수에서는 ... 를 활용하여 가변 파라미터로 받을 수 있다. 또한 slice... 와 같이 slice 를 가변인자로 보낼 수 있다.

      func main() {
        fmt.Println(addTo(1))
      	fmt.Println(addTo(1, 2))
      	fmt.Println(addTo(1, 2, 3))
      	i := []int{4, 5}
      	fmt.Println(addTo(3, i...))   // slice를 가변인자로 보내기
      	fmt.Println(addTo(6, []int{7, 8, 9}...))
      }
      
      // 가변인자 (variadic parameter)
      func addTo(base int, vals ...int) []int {
      	result := make([]int, 0, len(vals))
      	for _, v := range vals {
      		result = append(result, base+v)
      	}
      	return result
      }
      
      --- output ---
      []
      [3]
      [3 4]
      [7 8]
      [13 14 15]

      Function 의 리턴 값을 여러 개로 할 수 있다. (multi return values)

      func main() {
        result, remainder, err := multiReturnValues(10, 5)
      	if err != nil {
      		fmt.Println(err)
      	}
      	fmt.Println(result, remainder)
      }
      
      // multiple retrun values
      func multiReturnValues(n, d int) (int, int, error) {
      	if d == 0 {
      		return 0, 0, errors.New("cannot divid by zero")
      	}
      	return (n / d), (n % d), nil
      }
      
      --- output ---
      2 0

      Function 은 변수에 담을 수 있다. cal 함수 내부에 보면 map 의 value 로 Function 타입을 선언하여 할당한 것을 볼 수 있다. Function 을 변수로 선언할 수 있고, 파라미터로 받을 수 있고 return value 로도 사용할 수 있다.

      func main() {
        exp := []string{"3", "+", "4"}
      	result, err := cal(exp)
      	if err != nil {
      		fmt.Println(err)
      	}
      	fmt.Println(result)
      }
      
      func cal(exp []string) (int, error) {
      	// Function Type
      	type opFuncType func(int, int) int
      	var opMap = map[string]opFuncType{
      		"+": add,
      		"-": sub,
      	}
      
      	a, err := strconv.Atoi(exp[0])
      	if err != nil {
      		fmt.Println(err)
      		return 0, err
      	}
      
      	b, err := strconv.Atoi(exp[2])
      	if err != nil {
      		fmt.Println(err)
      		return 0, err
      	}
      
      	op := exp[1]
      	opFunc, ok := opMap[op]
      	if !ok {
      		fmt.Println("unsupported operator: ", op)
      		return 0, err
      	}
      	return opFunc(a, b), nil
      }
      
      func add(a, b int) int {
      	return a + b
      }
      
      func sub(a, b int) int {
      	return a - b
      }
      
      --- output ---
      7

      struct 와 마찬가지로 Function 도 anonymous 로 만들 수 있다.

      func main() {
        for i := 0; i < 5; i++ {
      		func(j int) {
      			fmt.Println(j)
      		}(i)
      	}
      }
      
      --- output ---
      0
      1
      2
      3
      4

      Function 안에는 Function 을 중첩해서 선언할 수 있는데 이 것을 clousure (클로저)라 한다. 클로저는 앞에서 설명한 Function 을 파라미터와 리턴 값으로 사용할 수 있는 성질을 많이 이용한다.


      클로저는 변수 값이 사라지지 않고 계속 유지되는 특성을 갖는다. 아래 multiple(2) 을 호출 할 때 생성된 파라미터 base 변수 에는 2가 할당 되고 funcA(3) 을 호출 할 때도 계속 값이 남아 있어 base * factor 의 값은 2 * 3 이 되어 6 이 출려된다.

      func main() {
        funcA := multiple(2)
      	fmt.Println(funcA(3))
      
      	funcA = multiple(4)
      	fmt.Println(funcA(5))
      }
      
      // high-ooder fuctnion
      func multiple(base int) func(int) int {
      	return func(factor int) int {
      		return base * factor
      	}
      }
      
      --- output ---
      6
      20

      함수를 호출할 때 모든 것은 Call by Value 이다. 아래과 같이 기본 타입이나 Struct 타입은 함수를 통해 넘겨받은 변수의 값을 변경하더라도 원래의 값은 변경되지 않는다. 즉, 아래와 같이 person 을 modify 함수를 통해 넘긴 다음, modify 함수에서 값을 수정해도 원래의 person 의 값은 수정되지 않는다.

      func main() {
        i := 1
      	s := "Hello"
      	p := person{}
      	modify(i, s, p)
      	fmt.Println(i, s, p)
      }
      
      type person struct {
      	name string
      	age  int
      }
      
      // Call by Value (여기서 수정해도 원래 변수 값은 수정되지 않는다)
      func modify(i int, s string, p person) {
      	i = i + 1
      	s = "modified"
      	p.name = "ask"
      	p.age = 100
      }
      
      --- output ---
      1 Hello { 0}

      그러나 포인터로 넘기면 원래의 값도 수정할 수 있다. 포인터는 다음에 설명하기로 하고 여기서는 slice 와 map 이 포인터와 같이 작동한다고 이해하면 된다.


      한가지 중요한 것이 있다. modSlice(s) 에서의 s 변수 가 가리키는 주소와 modSlice(s []int) 함수에서 s 변수 가 가리키는 주소는 동일하다. (마치 포인터 처럼 동작한다) 하지만 s 변수 의 경우 modSlice 함수 통해 넘기는데 처음 생성할 때 길이:2, 크기:2 로 생성했다. 즉, 길이와 크기가 같아 modSlice 함수에서 append 로 item 을 추가할 경우 크기를 늘린 새로운 slice 를 만들어서 리턴 된다.


      s = append(s, 3) 에서 s 가 가리키는 주소는 처음 modSlice(s []int) 일 때 가리키는 주소가 아니라 크기가 늘어나 새롭게 생성된 slice 를 가리키게 된다. 그래서 그 이전에 수정된 s 의 값은 변화가 있고, 그 이후에 수정된 s 의 값은 변화가 없다.

      func main() {
        m := map[string]int{
      		"one": 1,
      		"two": 2,
      	}
      	modMap(m)
      	fmt.Println(m)
      
      	s := []int{1, 2}
      	modSlice(s)
      	fmt.Println(s)
      }
      
      // Call by Value but slice and map like pointer
      func modMap(m map[string]int) {
      	m["two"] = 20
      	m["three"] = 3
      	delete(m, "one")
      }
      
      func modSlice(s []int) {
      	s[0] = s[0] * 10
      	s = append(s, 3)  // 길이 == 크기 이므로 새로운 크기의 slice 가 만들어져서 리턴된다
      	s[1] = s[1] * 10
      }
      
      --- output ---
      map[three:3 two:20]
      [10 2]

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      seungkyua 님의 최신 블로그

      더보기
      동영상 기고하기