Golang 기초 (6) : 조건문에 대하여

Eon Kim·2021년 12월 21일
2

Golang 기초 

목록 보기
6/14
post-thumbnail

안녕하세요, 주니어 개발자 Eon입니다.

이번 포스트는 조건문에 대해 다루겠습니다.

조건문

조건문은 특정한 조건을 만족했을 때 실행하는 문장(코드) 을(를) 말합니다.
golang에서 조건문은 if / switch 두 가지가 있습니다.

if문

조건을 만족하는지는 bool (true / false) 값으로 판단합니다.
조건문 if문은 아래와 같이 사용할 수 있습니다.

'if' : 만약에

조건문 '만일 어떠한 조건을 만족한다면~' {
'이 코드를 실행해라'
} 

'else if' : 아니면 이렇다면

아니면 이렇다면 {
'이 코드를 실행해라'
}

'else' : 아니면

아니면 {
'이 코드를 실행해라'
}

Golang에서는 아래와 같이 사용할 수 있습니다.

if condition1 {
	// TODO1
} else if condition2 {
	// TODO2
} else {
	// TODO3
}
// (단, condition1, condition2(은)는 각각 bool 값을 가짐)

Golang에서의 'else if'와 'else'는 항상 if문이 끝나는 라인에 함께 써주어야 합니다.


if만 사용하면 안 될까?

if만 사용하면 안 될까?
안 되는 건 없습니다.
다만 비효율적이고 그렇게 하지 말아야 한다고 배웠을 뿐입니다.
이유를 한 번 파헤쳐 봅시다.

조건 A, B에 대하여 모든 경우의 수에 그에 맞는 특정한 코드를 실행해야 합니다.

먼저 if-else문을 보겠습니다.

if A && B {
	// TODO AB
} else if A {
	// TODO A
} else if B {
	// TODO B
} else {
	// TODO nothing ..
}

A와 B 모두 참인지 확인했고,
모두 참은 아닐 때, A가 참인지를 확인했고,
아니라면 B가 참인지를 확인했고,
그것도 아니라면 TODO nothing .. 파트를 실행합니다.


다음은 if만 사용한 경우를 보겠습니다.

if A && B {
	// TODO AB
}
if A && !B {
	// TODO A
}
if !A && B {
	// TODO B
}
if !A && !B {
	// TODO nothing ..
}

A와 B 모두 참인지 확인했습니다.
A가 참이고 B가 거짓일 때를 확인했습니다.
A가 거짓이고, B가 참일 경우를 확인했습니다.
A와 B 모두 거짓일 때를 확인했습니다.


제가 쓴 문장들을 보시면 if-else에서는, '~했고, ~했고, ~했고'가 쓰였습니다.
연속된 것으로 볼 수가 있습니다.
또한, A에 대한 검증이 끝난 이후에는 A가 참인지 거짓인지는 확인하지 않았습니다.

반면에, if만 사용한 부분에서는, '~했습니다. ~했습니다. ~했습니다.'가 쓰였습니다.
연속적이지 않고, 모든 조건들이 독립되어 있음을 알 수 있습니다.
또한, A에 대한 검증이 끝난 것 같은데도 다시 A에 대하여 검증해야 했습니다.
A에 대한 검증을 다시 넣지 않았다면 A가 참이든 거짓이든 상관없이 B에 대한 검증만 하는 조건이 됐을 겁니다.


같은 조건에 대하여 중복된 검증은 좋지 않습니다.
조건이라 함은 조건 연산으로 이루어져 있으며, 연산은 중복해서 수행하면 정확도에 관계없이 당연히 속도가 낮고, 퍼포먼스 저하로 이어집니다.
따라서 모든 프로그램 언어에서 if만 사용하는 것은 권장하지 않습니다.


if문 예제

if 조건문을 사용하기에 앞서, 조건에 따라 다른 값을 반환하는 함수를 정의하고 시작하겠습니다.
입력값은 정수로 하고, 음수나 양수에 상관없이 양수를 반환하는 함수를 작성하겠습니다.

절댓값 반환 예제

func AbsoluteValue(input int64) int64 {
	if input < 0 {
		return -input
	}
	return input
}

if input < 0 : 함수의 입력값 'input'이 0보다 작을 경우, {괄호} 안의 코드를 실행함
return -input : 음수일 경우에 실행되는 코드이므로, 앞에 '- (마이너스)'를 붙여 양수로 만들고 반환함
return input : if 조건문이 조건에 맞지 않아 실행되지 않을 때 (입력값이 양수일 때), 실행됨

이렇게 사용할 수 있습니다.
그럼 if 하나 당 조건 하나만 체크할 수 있는가?

아닙니다. && 연산자로 아래와 같이 여러 조건을 동시에 만족할 경우를 작성할 수 있습니다.

절댓값 반환 예제 + 타입 변환

func AbsoluteValue(input int64) uint32 {
	if input < 0 && -input <= 4294967295 {
		return uint32(-input)
	}
	if input < 0 && -input > 4294967295 {
		return uint32(4294967295)
	}
	if input >= 0 && input <= 4294967295 {
		return uint32(input)
	}
	if input >= 0 && input > 4294967295 {
		return uint32(4294967295)
	}
	return uint32(input)
}

'4294967295'는 uint32의 최댓값입니다.
4294967295에 1을 더해, 4294967296이 되면 오버플로우가 발생해, 값이 0이 됩니다.
4294967295에 2를 더한 결과값은 1이 됩니다.

if input < 0 && -input <= 4294967295 : input이 0보다 작은 음수이며, 이를 절댓값으로 만들었을 때의 결과값이 uint32의 최댓값과 같거나 작을 경우에만 실행함


switch문

switch는 표현식의 값을 case절의 표현식과 일치하는지 체크하고, 일치한다면 해당 case절을 실행시킵니다.
golang에서 특이한 점은, break문을 사용하지 않아도 case 한 개만 실행하고 탈출한다는 점입니다.
break문은 컴파일 단계에서 컴파일러가 추가해 줍니다.

switch문 예제

값이 일치할 때 실행하는 switch

첫 번째로 볼 예제는 값이 일치할 때 실행하는 switch문입니다.

package main

import "fmt"

func main() {
	var name string = "vamos"
	switch name {
	case "vamos":
		fmt.Printf("name is %s\n", name)
		fmt.Println("but it is not a name")
	case "eon kim":
		fmt.Printf("name is %s\n", name)
		fmt.Println("yes, it is your name")
	default:
		fmt.Println("other..")
	}
}
  • var name string = "vamos" : name 변수를 string 타입으로 선언하고, 그 값으로 "vamos"를 넣었습니다.
  • switch name {...} : switch문의 시작함
    (여러 가지 작성법 중에 한 가지입니다.)
  • case "vamos": : name과 case절의 표현식의 값이 일치하면 해당 case절을 실행함
    (이 예제에서는 name과 case절의 "vamos"가 서로 같은 값을 가지고 있어, 이 case절을 실행합니다.)
  • case "eon kim": : name의 값이 "eon kim"이 아니므로, 이 case절은 무시함
  • default: : 위의 case절 모두 일치하는 게 없으면 실행함
    (default절은 없어도 상관없습니다.)

표현식을 사용하는 switch

두 번째로 볼 예제는 표현식을 사용하는 switch문입니다.

package main

import "fmt"

func main() {
	var x uint8 = 1
	switch fx := x*3 + 2; fx {
	case 14:
		fmt.Println(fx)
		fmt.Println("1st case")
	case 11:
		fmt.Println(fx)
		fmt.Println("2nd case")
	case 8, 5:
		fmt.Println(fx)
		fmt.Println("3rd case")
	default:
		fmt.Println("other..")
	}
}
  • switch fx := x*3 + 2; fx {...} : switch문 안에서만 사용할 수 있는 변수 fx를 선언하고, 표현식 x*3 + 2를 지정합니다. 그리고, case절과 일치시킬 값으로 fx를 그대로 사용함
    (여기서 fx를 그대로 사용하지 않고 싶다면 fx + 1이든 어떠한 식의 변형을 해서 사용할 수도 있습니다.)
  • case 8, 5: : 마찬가지로 fx의 값이 일치할 경우에 실행합니다. 여기서 case절은 두 개의 값의 경우 모두에 대하여 해당 case절을 실행함
    ('8 || 5'와 같다고 보시면 됩니다.)

표현식을 사용하는 case

세 번째로 볼 예제는 표현식을 사용하는 case절입니다.

package main

import "fmt"

func main() {
	var x uint8 = 3
	var y uint8 = 25
	switch y {
	case x*3 + 2:
		fmt.Println(y)
		fmt.Println("1st case")
	case x*5 + 3:
		fmt.Println(y)
		fmt.Println("2nd case")
	case x*7 + 4, x*9 + 5:
		fmt.Println(y)
		fmt.Println("3rd case")
	default:
		fmt.Println("other..")
	}
}
  • case x*3 + 2: : 표현식 x*3 + 2 를 case절에 사용했고, y의 값이 case절의 값과 같으면 실행함
  • case x*7 + 4, x*9 + 5: : 여기서 y값은 x*7 + 4의 결과와 값이 같으므로, 이 부분의 case절을 실행함

case절에 조건식을 사용하는 switch

네 번째로 볼 예제는 case절에 조건식을 사용하는 switch문입니다.

package main

import "fmt"

func getGrade(score uint8) (grade string) {
	switch {
	case score > 100:
		grade = "Try again.. "
	case score >= 90:
		grade = "A"
	case score >= 80:
		grade = "B"
	case score >= 70:
		grade = "C"
	case score >= 60:
		grade = "D"
	case score < 60:
		grade = "F"
	}
	return
}

func main() {
	fmt.Print("Your score : ")
	var myScore uint8
	fmt.Scanf("%d", &myScore)
	myGrade := getGrade(myScore)
	fmt.Println(myGrade)
}
  • fmt.Scanf("%d", &myScore) : 정수를 입력 받아, myScore에 저장함
  • myGrade := getGrade(mySocore) : getGrade()함수의 파라미터로 myScore를 사용하고, 반환값을 myGrade에 넣음
  • switch {...} : switch문임을 나타내고, case절의 조건에 따라 실행합니다.
  • case score > 100: : 입력받아, 파라미터로 넘겨받은 score값이 100이 넘어가면 해당 case절을 실행함

fallthrough를 사용하는 switch

다섯 번째로 볼 예제는 fallthrough를 사용하는 switch문입니다.
golang에서는 break문을 작성하지 않으면 컴파일러가 추가하여 자동으로 case절 하나만 실행하고 탈출한다고 했습니다.
연속적으로 case절 여러 개를 실행해야 할 때는 fallthrough를 사용합니다.

package main

import "fmt"

func main() {
	var test uint8 = 3
	switch test {
	case 1:
		fmt.Println(test)
		fmt.Println("1st case")
	case 2:
		fmt.Println(test)
		fmt.Println("2nd case")
	case 3:
		fmt.Println(test)
		fmt.Println("3rd case")
		fallthrough
	case 4:
		fmt.Println(test)
		fmt.Println("4th case")
		fallthrough
	default:
		fmt.Println("other..")
	}
}
  • fallthrough: 해당 case절이 실행되고, 바로 다음 case절도 실행함

Type switch

여섯 번째로 볼 예제는 Type을 case절로 판단하여 실행하는 switch문입니다.
Effective go : Type Switch

package main

import "fmt"

func functionOfSomeType() int {
	return 0
}

func main() {
	var t interface{}
	t = functionOfSomeType()
	switch t := t.(type) {
	default:
		fmt.Printf("unexpected type %T\n", t) // %T prints whatever type t has
	case bool:
		fmt.Printf("boolean %t\n", t) // t has type bool
	case int:
		fmt.Printf("integer %d\n", t) // t has type int
	case *bool:
		fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
	case *int:
		fmt.Printf("pointer to integer %d\n", *t) // t has type *int
	}
}
  • t.(type) : t의 type을 값으로 하여 case절을 실행함
    (type assertion이라고 하며, 위는 이를 사용하는 방법 중 하나입니다.)

위와 같이 type으로도 case절을 실행할 수 있습니다.


이번 포스트는 조건문에 대한 내용이었습니다.
감사합니다.👍

profile
주니어 개발자

0개의 댓글