0518-Go(3)

망지·2022년 5월 18일
0
post-custom-banner

블록, 섀도, 제어 구조

1. 블록(block)

  • 선언문이 있는 각각의 공간
  • 관련 있는 문장을 묶은 것
  • 식별자의 스코프(scope)
    • 식별자의 Lifecycle(생명주기)
      : 블록 내에서 선언/정의될 때 식별자가 생성
      : 블록이 끝날때 식별자가 제거
      • 즉, 블록 내에서만 해당 식별자를 사용할 수 있고 블록 밖에서는 해당 식별자를 접근할 수 없다
  • 패키지 블록

    • 함수 외부에 선언된 것들이 속함
    • :=은 사용 불가
    • 대문자로 시작하는 식별자는 패키지 외부에 노출
      (expose)됨
    • 노출된 식별자는 점 연산자를 통해 접근
    • .go파일이 여러 개더라도 package 이름이 같으면 같은 패키지 블록에 포함된다.
  • 파일 블록

    • .go 파일에 선언된 것들
    • import 문이 포함된 파일
  • 함수 블록

    • 로컬 변수 : 매개변수(파라미터), 반환 변수도 포함
    • 모든 중괄호는 새로운(다른) 블록을 정의
    • 제어 구조도 자체의 블록을 가진다.
  • 유니버스 블록

    • 기본 타입, 내장 함수, true/false와 같은 미리 선언된 식별자가 정의된 블록
    • 다른 모든 블록을 포함하는 블록
    • 따라서 다른 블록에 의해 섀도잉될 가능성이 높다
  • 식별자 접근
    • 외부 블록에서는 내부 블록의 요소(식별자)에 접근 불가
    • 내부 블록에서는 자신과 외부 블록의 요소에 접근 가능

1.1 섀도잉 변수

  • 섀도잉(Shadowing)
    • 외부 블록과 내부 블록의 식별자가 같은 경우 내부 블록의 식별자로 처리
    • 내부 식별자에 의해 외부 식별자가 그림자에 가려 보이지 않음
  package main

import "fmt"

func main() {
    x := 10
    if x > 5 {
        fmt.Println(x)
        x := 5
        fmt.Println(x)
    }
    fmt.Println(x)
}
10
5
10
  • := 연산자
    • 여러 변수를 := 연산자로 초기화할 때
      : 현재 블록에서 선언된 변수들만 재사용된다
  • 임포트된 패키지가 섀도잉되지 않도록 주의
    • 임포트한 패키지 이름과 같은 식별자를 선언하지 말 것 ex)fmt

2. if 문

  • 조건을 괄호로 감쌀 순 있지만 감싸지 않는 것이 일반적
  • if 블록이나 else 블록내에서만 사용가능한 변수를 if 또는 else에서 바로 선언하는 방법
package main

import (
	"fmt"
	"math/rand"
)

func main() {
	if n := rand.Intn(10); n == 0 {
		fmt.Println("That's too low")
	} else if n > 5 {
		fmt.Println("That's too big:", n)
	} else {
		fmt.Println("That's a good number:", n)
	}
    
	fmt.Println(n) //n을 선언한 if문 밖이라 출력안됨. 
}

3. for 문

  • for 문의 4 가지 패턴
    1) C 언어와 동일한 방식의 for
    • 변수 초기화는 반드시 := 사용
    • for 에서 선언된 변수도 if 처럼 섀도잉 문제 발생 가능
package main

import "fmt"

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

변수 선언 i := 0;
조건 i < 10;
=> 조건 맞으면, 실행 fmt.Println(i)
실행 후 i++

3.1 완전한 for 구문

1) 선언부는 한 번만 실행
2) 조건부가 참일 경우에만 for 블록 실행, 거짓이면 for 문 종료
3) 증감부는 for 블록 실행이 끝나고 다시 2)으로 가기전에 실행

3.2 조건식만 사용하는 for 문

  • for 문을 while 문처럼 사용

3.3 for 문을 이용한 무한루프

  • for 문의 조건식을 생략한 형태

3.4 break와 continue

  • 파이썬과 같은 개념
  • 코드를 간결하게 보기좋게 만드는데 기여

3.5 for-range 문

  • 내장 타입의 요소(문자열, 배열, 슬라이스, 맵)를 순회하며 루프 수행
  • range가 두 개의 값을 반환
    • (index, value) 쌍
package main

import "fmt"

func main() {
	evenVals := []int{2, 4, 6, 8, 10, 12}
	for i, v := range evenVals {
		fmt.Println(i, v)
	}
}

만약 for 문에서 1개만 받을경우 (i) 인덱스만 출력됨. 위와같이 2개 받는 경우 인덱스와 슬라이스 값이 함께출력됨. (결과는 아래와 같음)

0 2
1 4
2 6
3 8
4 10
5 12

cf.) Go에서는 반환되는 값을 사용할 의도나 필요가 없다면 _(underscore)로 받는다. (Python과 같은 개념)

 package main

import "fmt"

func main() {
	evenVals := []int{2, 4, 6, 8, 10, 12}
	for _, v := range evenVals {
		fmt.Println(i, v)
	}
}
  • 키 값만 순회하는 대표적인 예
    • 맵 타입을 집합 타입으로 사용한 경우
    • 맵의 값보다는 유니크한 키 값이 중요 ⇒ 집합(셋)
  • 맵을 for range로 순회
    • 요소를 처리하는 순서가 일정하지 않다 (보안 이슈)
package main

import (
	"fmt"
)

func main() {
	m := map[string]int{
		"a": 1,
		"c": 3,
		"b": 2,
	}

	for i := 0; i < 3; i++ {
		fmt.Println("Loop", i)
		for k, v := range m {
			fmt.Println(k, v)
		}
	}
}
  • 문자열을 for-range 순회

    • 룬을 순회, 바이트 순회 아님
  • for-range의 값은 복사본

    • for-range 값으로 원본을 수정할 수 없다

3.6 for 문 레이블링

  • 레이블: 프로그램 코드 위치에 이름 붙인 것
  • break, continue, goto 문의 대상이 됨

3.7. 4 가지 중 알맞은 for 문 선택

4 switch 문

  • switch, case, default, [break]
  • 다른 언어의 switch
    • fallthrough 기능 존재
  • Go는 fallthrough가 기본적으로 금지, fallthrough 키워드로 활성화

[SYNTAX]
switch [selector] {
case value|condition:
문장1
문장2
...
case

case

}

  • switch의 fallthrough
    • break 문을 만날때 까지 다른 case의 내용도 실행

5. 공백 스위치

  • case에 불리언 결과(조건식, 불값을 반환하는 함수)를 사용합니다. selector 없음

6. if 문과 switch 문 중 선택

7. goto 문

  • Go에서 지원은 하지만 없다고 생각하세요

함수

1. 함수 선언과 호출

  • 1급 객체

  • main 함수

    • 프로그램 진입점
    • 실행 파일을 만들려면 필요
    • 인자 X
    • 반환값 X
  • 함수 선언
    1) func 키워드
    2) 함수 이름
    3) 입력 파라미터: 반드시 타입 명시 (Go는 정적 언어)
    4) 반환값의 타입
    ⇒ 함수의 시그니처

  • return 키워드

  • 여러 입력 파라미터가 같은 타입이라면 콤마로 파라미터를 구별하고 타입은 마지막에 기술할 수 있다.

1.1. 이름이 지정된 파라미(named parameters)터와 선택적 파라미터 대응

  • Go는 키워드 파라미터(=named parameters)를 지원하지 않음
  • 선택적 파라미터: 파라미터 기본값 X
  • Go는 함수를 호출할 때 함수에서 정의한 파라미터를 생략할 수 없음
  • 구조체를 사용하면 named parameter 및 선택적 파라미터 방식을 간접적으로 실현

1.2 가변 입력 파라미터와 슬라이스

  • 임의 개수의 입력 파라미터 처리
  • 가변 파라미터는 반드시 함수의 입력 파라미터 목록에서 마지막에 위치
  • 타입 이름 앞에 ...
  • 가변 파라미터는 함수 내에서는 해당 타입의 슬라이스이다
  • 슬라이스를 파라미터로 Unpack하려면 슬라이스뒤에 ...을 붙여준다

base int, vals - 파라미터
vals - 가변 파라미터

1.3 다중 반환값

  • 함수 정의시 반환값의 타입을 콤마로 구분하고 괄호로 묶어준다

  • return 시 반환값을 괄호로 묶지 않는다

  • 일반적으로 다중 반환값을 받을 때는 := 사용

1.4 다중 반환값은 다중값

  • 다중 반환값은 개별 변수로 받아야 함
  • 하나의 변수로 받으면 에러

1.5 반환되는 값 무시

  • 함수의 반환값 개수와 받는 변수 개수가 다르면 컴파일 에러
  • 일부만 무시하려면 _(unserscore) 사용
  • 모든 반환값을 무시할 수 있다

1.6 이름이 지정된 반환값(naked return)

  • 함수 정의 시 반환값 타입뿐만 아니라 반환값 파라미터 선언
  • 해당 함수의 로컬 변수로 간주
  • 코너 케이스
    1) 섀도잉 문제
    2) 해당 변수들을 반환할 필요가 없다.

1.7 빈 반환(blank return)

  • return 문에 반환값을 사용하지 않은 것
    1) 반환값이 없는 함수
    2) 이름 지정된 반환값을 반환하고자 할 때
    – 이때 return을 생략하면 컴파일 에러

2. 함수는 값이다

  • 함수는 일급 객체다

2.1 함수 타입 선언

  • Go는 강타입 정적 타입 언어이므로
    • 함수를 저장하는 변수는 해당 함수의 타입으로 선언되어야 함
  • 함수 타입: type 키워드로 정의
    [SYNTAX]
    type 함수타입명 함수시그니처(함수 이름과 파라미터 이름 제외)

type opFuncType func(int, int) int

2.2 익명 함수

  • 이름 없는 함수 ⇒ 재사용 하지 않겠다 = 한 번만 쓴다
  • 변수에 저장할 용도가 아니므로 인라인으로 작성한다.
  • 익명 함수를 호출해야 하므로 인라인 작성에서 함수 몸체 정의가 끝나고나서 괄호를 사용하여 인자를 넘겨줘야 한다

    (i) -> 인자 넘겨줌


이게 더 간결하긴 함 (show에 할당한 경우)

3. 클로저(Closure)

  • 함수 내부에 선언된 함수 = 내부 함수(inner function)
  • 외부 함수에 의해 반환되는 내부 함수, 이 내부 함수는 외부 변수를 참조함

3.1 파라미터로 함수를 전달

(클로저는 아님. 함수를 1급 객체로 사용)

3.2 함수에서 함수 반환

package main

import "fmt"

func makeMult(base int) func(int) int {
//int형을 받고(base) int 반환
	return func(factor int) int {
		return base * factor
	}
}
//내부함수가 외부변수(base)사용해서 return -> closure
// makeMult 함수가 끝나고 사라져도 base는 메모리에 있음.  ->closure 때문에.외부변수를 계속 접근 할 수 있게 되는.


func main() {
	twoBase := makeMult(2)
	threeBase := makeMult(3)
	for i := 0; i < 3; i++ { //factor가 됨 
		fmt.Println(twoBase(i), threeBase(i))
	}
}

기타

  • 파일 블록 > 패키지 블록 > 함수 블록

  • 식별자 ; 이름 붙이는 거 캐멀케이스, 뭐 이런거..

  • 파이썬에서 클로저는 데코레이터 할 때 쓰임

profile
꾸준히, 차근차근
post-custom-banner

0개의 댓글