Go는 for 반복문이라는 단 하나의 반복 구조를 가집니다.
func main() {
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
}
for 문 구성 요소는 3개로 나뉘며 c언어에서 사용하는 방법과 동일합니다.
초기화 구문: 첫 번째 iteration 전에 수행됩니다.
조건 표현: 매 모든 iteration 이전에 판별됩니다.
사후 구문: 매 iteration 마지막에 수행됩니다.초기화 구문과 사후 구문은 필수가 아닙니다.
func main() { sum := 1 for ; sum < 1000; { sum += sum } fmt.Println(sum) }
> **주의**
C나 Java, JavaScript와 같은 다른 언어들과 달리, Go는 for문의 세 가지 구성 요소를 감싸는 괄호가 없고, { } 괄호가 항상 필수입니다.
### while
; 을 생략할 수 있다는 점에서 C의 while 은 Go에서 for 로 쓰입니다.
```go
func main() {
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
}
Go의 if 문은 ( ) 괄호로 둘러쌓일 필요는 없지만, { } 괄호는 필수입니다.
if 문의 조건문 전에 수행될 짧은 구문으로 시작될 수 있습니다.
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
}
return lim
}
그 짧은 구문에서 선언된 변수들은 오직 if 문의 끝까지만 스코프가 존재합니다.
만약 스코프 밖에서 v변수를 사용한다면
에러를 유발합니다.
짧은 if문 안에서 선언된 변수들은 모든 else 블럭에서 사용할 수 있습니다.
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
// can't use v here, though
return lim
}
하지만 if, else 스코프 밖에선 사용할 수 없습니다.
switch 구문은 연속적인 if - else 구문을 짧게 사용하는 방안입니다.
Go의 switch는 뒤이어 오는 모든 case를 실행하는 것이 아니라 오직 첫 번째로 선택된 케이스 만을 실행한다는 점을 제외하곤 C, C++, Java, Javascript, PHP와 유사합니다.
즉, Go에서는 자동으로 break를 제공합니다.
만약 Go에서 자동으로 제공하는 break를 사용하지 않고, 다른 케이스로 넘어가고 싶다면 (다른 언어에서 break를 사용하지 않은 것 처럼)
fallthrough
키워드를 case 맨 마지막에 넣어주면 됩니다. 단, 마지막 case에는fallthrough
를 사용할 수 없습니다.
조건이 없는 Switch는 switch true 와 동일합니다.
이 구조는 긴 if-else 체인을 작성하는 데에 아주 깔끔한 방식일 수 있습니다.
func main() {
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}
t 시간을 찍어보니
playground 기본 시간대로 잡혀서 "Good evening"이 출력되네요
defer 문은 자신을 둘러싼 함수가 종료할 때 까지 어떠한 "함수"의 실행을 연기합니다.
연기된 호출의 인자는 즉시 평가되지만 해당 함수 호출은 자신이 속한 함수가 종료할 때 까지 수행되지 않습니다.
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
연기된 함수들의 호출은 스택에 쌓이며, 한 함수가 종료될 때 해당 함수 내부에서 연기된 함수의 호출들은 후입선출 순서로 수행됩니다.
func a() {
i := 0
defer fmt.Println(i) // 0
i++
return
}
return 될 때 i의 값은 1이지만 0으로 평가되어 0이 출력됩니다.
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i) // 3210
}
}
0, 1, 2, 3순서로 호출이 연기되며 i가 평가 받았지만, 순서는 후입선출로써 뒤바뀝니다.
func c() (i int) {
defer func() { i++ }()
return 1
}
위 출력이 2가 나온다
return시 1이 return 변수 i를 1로 바꾸고, defer가 실행되며 2로 증가시키기 때문인 것으로 보인다.
chat gpt 무료버전에 이유를 물어보니 계속 이상한 소리를 하여 내가 확인한 방법이 이유라고 생각된다..
package main
import "fmt"
func c() (i int) {
fmt.Println("0", i)
defer func() {
fmt.Println("1", i)
i++
fmt.Println("2", i)
}()
fmt.Println("3", i)
return 1
}
func main() {
fmt.Println("4", c())
}
이렇게 순서와 i의 값을 찍어 보았다.
i를 func()에서 i++하기전 return 에 의해 1로 바뀐 것을 확인 할 수 있다.
defer 문은 호출을 목록에 push합니다.
panic은 일반적인 제어 흐름을 중지하는 내장함수입니다.
만약 함수 F에서 panic을 만나게되면 defer 등의 이유로 지연된 함수가 실행된 다음 F는 호출자에게 반환됩니다.
recover는 패닉에 빠진 고루틴의 통제권을 되찾는 내장 함수입니다. 복구는 지연된 함수 내에서만 유용합니다. 정상적인 실행 중에 복구 호출은 nil을 반환하고 다른 효과는 없습니다. 현재 고루틴이 패닉 상태이면 복구 호출이 패닉에 지정된 값을 캡처하고 정상적인 실행을 재개합니다.
package main
import "fmt"
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}
recover()는 지연된 함수 내에서만 유용하기때문에, 7번처럼 동작합니다.