220517

HyeonKi Jo·2022년 5월 17일
0
post-thumbnail

2. 기본 데이터 타입과 선언

2.4 타입 지정 상수와 타입 미지정 상수

  • 프로그래밍 의도
  • 타입 미지정 상수는 리터럴과 같은 취급
  • 타입 지정 상수는 해당 타입 변수에만 할당해야 함

2.5 사용하지 않는 변수

  • 선언된 지역변수는 항상 사용되어야 한다. (필수)
  • 그러나 한번이라도 사용되면 이후 변경시에도 에러가 나지는 않는다.
  • 그러나 GO컴파일러는 const로 사용되지 않는 상수를 만드는 것을 허용한다.
    • Go에서 상수는 컴파일시 계산되며, 어떤 부작용도 만들지 않기 때문이다. 쉽게 제거될 수 있고, 컴파일된 바이너리에 포함하지 않으면 된다.

2.6 변수와 상수 이름짓기

  • Go는 문자나 밑줄로 시작하는 식별자 이름이 필요하다.
  • Go는 유니코드 문자를 허용한다.
    • 권장하지 않음
  • Go는 관용적으로 스네이크 표기법을 사용하지 않음
    • 낙타 표기법, 파스칼 표기법 사용
  • 패키지 상수중에 외부로 노출시키는 것은 첫글자를 대문자로 사용한다.
  • 내부 상수는 앞글자를 소문자를 사용하여 보호한다.
  • 함수 내에서는 짧은 변수 이름 선호
    • 변수의 의미보다 간결성을 선호하는 Go
    • 헝가리안 표기법
      • int iScore = 10
      • String strName
  • 짧은 이름
    • 코드 간결
    • 코드의 복잡도 판별 기준
      • 코드의 이름이 너무 길다면, 그 코드가 너무 많은 일을 하고 있다는 뜻이다.


3. 복합 타입

3.1 배열 (Array)

  • 같은 타입의 여러 데이터를 담음
  • 시퀀스 타입 (순서가 있다.)
  • Go에서는 선호하지 않음(특별한 경우에만 사용)
  • 선언 시 크기를 지정해야 함
  • 크기를 변경할 수 없다.
  • 배열 간 비교 가능

선언방법

    1. 배열의 크기와 배열 내 요소 타입 지정
      • 제로 값으로 선언 : 배열의 크기와 배열 내 요소 타입 지정
    1. 배열 초깃값 지정 : 배열 리터럴 사용
    1. 희소 배열: 대부분의 요소가 0인 배열
    • 위 와 같이지정하여 값을 넣은 원소를 제외한 나머지 요소가 0인 배열로 만들 수 있다.
    1. 배열 크기 지정하지 않고: 배열리터럴 필요
    1. 다차원 배열: []의 개수가 차원 수

배열 요소 접근.

  • [] 사용

배열 길이

  • len() 함수

GO에서 배열을 잘 사용하지 않는 이유

  • 배열의 크기가 배열의 타입을 결정하는데 사용되기 때문
  • 즉, 타입이 같은 배열이라도 크기가 다르면 다른 타입이다.
  • 크기(길이)가 다르면, 타입 변환도 불가능

3.2 슬라이스

  • 일련의 값(시퀀스)를 저장하는 자료구조
  • 순서 중요
  • 슬라이스의 크기는 타입의 일부가 아니다. (배열과의 큰 차이점)
  • 슬라이스 간 비교는 불가 (배열과 다른점)
  • 단, nil과는 비교 가능(==, !=)
      1. 슬라이스 초깃값 지정 리터럴
      1. 희소 슬라이스
      1. 다차원 슬라이스
      1. 제로 슬라이스: 슬라이스 리터럴 없이 선언만 하는 것
      • 슬라이스의 제로 값은 nil(값이 없는 상태)
      • nil : 값의 부재(absence of value) 상태, 값이 존재하지 않는다.
      • sql에서도 null값에 +1 해도 null이다.
      1. 비어있는 슬라이스: 슬라이스 리터럴에 초깃값이 없는 것
      1. make() 함수 사용하여 생성: 수용량 지정 가능

3.2.1 슬라이스의 길이

  • len() 함수 사용

3.2.2 append

  • 슬라이스에 새로운 요소 추가
  • x 슬라이스가 바뀌는 것이 아니라, qppend에서 리턴해서 y같은 다른 슬라이스로 받아줘야 한다.
  • make로 받았을 때, 안에 0으로 초기화 되기 때문에 append하면 끝 원소가 바뀌는 것이 아닌 새로운 원소가 새로 들어가게 된다.

3.2.3 수용력(capacity)

  • 예약된(미리 준비된) 연속적인(consecutive) 메모리 공간의 크기
  • 길이와 수용력
  • append시 기존값의 뒤에 붙는다.
    • 그러다 최대 수용력에 길이가 같아지면 수용력이 더 커진다
  • 수용력 >= 길이
  • 요소가 추가되면 길이는 커지고 결국 수용력과 같아진다.
  • 길이와 수용력이 같은 상태에서 요소가 추가되면 Go 런타임이 더 큰 수용력을 가지는 새로운 슬라이스를 할당, 원본 슬라이스의 값들은 새 슬라이스에 복사됨, 추가된 값은 새 슬라이스에 append되고 이 새 슬라이스가 반환된다.
  • 수용력은 조금씩 증가하는것이 아니라, 1024(2^10)보다 작을 때는 2배씩 증가한다.
    • 최대 수용력이 10이면 20~ 40~80... 으로 증가하게 된다.
  • 1024보다 클 때는 25%씩 확장된다.
  • cap() vs len()
    • cap()함수는 현재 슬라이스의 수용력을 반환한다.
      • 대부분 cap은 새로운 데이터공간을 확인하거나, 새로운 슬라이스를 할당하기 위해 make함수를 호출하는 경우에 사용된다.
    • len()함수는 현재 사용중인 길이를 반환한다.

3.2.4 make()

  • make(타입, 길이) 로 슬라이스를 만들어준다.
    • 그냥 빈 슬라이스를 선언했을 때,
    • make로 int형 슬라이스를 길이 5로 만들었다.
    • make를 이용해, 슬라이스에 길이가 0이지만 수용력(capacity)를 정해줄 수 있다.
    • 그러나 길이가 수용력보다 길다면, 위와 같은 에러가 나타난다.

3.2.5 슬라이스 선언

  • var data []int => nil
  • var data = []int{}=> nil(x)
  • 슬라이스를 아래 방식 중 어떤 걸로 생성할 지 정리해 봅시다.
    • 제로 슬라이스
    • 슬라이스 리터럴
    • make()

3.2.6 슬라이스 선언

  • 슬라이스 연산자(:)
    • 슬라이스로부터 슬라이스 생성
  • 슬라이스 연산자를 사용하면 복사본을 만들지 않고 메모리를 공유한다.
  • 슬라이싱과 append를 함께 사용하면 혼란이 가중된다.
  • 하위 슬라이스의 수용력
    • 원본 슬라이스의 수용력 - 하위 슬라이스시작 오프셋
  • 슬라이싱을 했을 때, 슬라이싱 한 y에 값을 추가하면, y는 x의 0~2의 주소를 가지고 있으니 추가하면 x의 값도 바뀌게 된다.
    • 그래서 원본 1, 2, 3, 4에서 1, 2, 30, 40이 된다.
    • 그러나 x의 수용력을 벗어날 떄, y는 새로운 리스트로 보여져 새로운 공간을 할당받게 되낟. 그래서 999를 추가했을 때, x는 변화가 없고, y에는 추가되게 된다.
  • 위 예시도 비슷하다.
    • 하위 슬라이스와 append를 아무 생각 없이 사용하면 혼란이 가중된다.
    • 의도치 않은 값 변경이 발생
    • 완전한 슬라이스 표현식 (full slice expression)으로 해결한다.

완전한 슬라이스 표현식

  • 하위 슬라이스에 얼마나 많은 메모리를 공유할 것인지를 지정
  • 슬라이스 연산 때 콜론을 한번 더 사용하여 세번째 인자에 원본 슬라이스에서 하위 슬라이스의 마지막 요소의 위치 지정
  • 슬라이스 연산의 세 번재 인자의 값을 두 번째 인자의 값과 같도록 설정

3.2.7 배열을 슬라이스로 변환

  • 배열에 슬라이스 연산 적용
  • 메모리 공유 문제 존재.

3.2.8 copy

  • copy() 내장함수
  • 배열을 그대로 복사한다.
    • 이때, 복사본을 수정해도, 원본이 변하지 않는다.
  • 복사를 하더라도, 자체 크기만큼 복사된다. 즉, 원본보다 크기가 작으면 자기 크기만큼만 복사할 수 있다.
  • copy의 첫 번째 인자는 슬라이스이어야 함.

3.3 문자열과 룬 그리고 바이트

  • 문자열은 룬으로 이루어진 것은 아니다.
  • 문자열은 바이트의 시퀀스이다.
  • 문자열의 길이는 바이트 수
  • 문자열에 슬라이스 연산 사용 가능
    • 문자열은 수정불가(immutable)이므로 슬라이스의 메모리 공유 문제가 없음
    • 유니코드로 구성되므로 슬라이스를 했을 때 문자가 깨지는 우려가 있음
  • 문자열은 바이트 슬라이스 또는 룬 슬라이스로 변환 가능

문자열

  • Go는 문자열을 표현하기 위해, 바이트를 연속으로 사용한다.
  • 문자열은 으로 이루어진 것은 아니다.
  • 문자열은 byte의 시퀀스이다.
    • 한글은 유니코드로 3byte를 사용한다.
  • 문자열에 슬라이스 연산 사용 가능
    • 문자열은 수정불가(immutable)이므로 슬라이스의 메모리 공유 문제가 없음
    • 유니코드로 구성되므로 슬라이스를 했을 떄 문자가 깨지는 우려가 있음

룬(rune)

  • 룬 자체는 int32이기 때문에 32비트를 사용한다.
  • Hello, 🌞를 byte와 rune으로 받았을 때, 출력이 다르게 나옴을 볼 수 있다.
    • byte는 Hello, 까지 동일하게 담을 수 있지만, 🌞는 4개의 byte로 담아야 한다.
    • rune은 32bit(4byte)를 사용하기 때문에 🌞를 한번에 담을 수 있다.
  • 마찬가지로 위 Hello, 세상아 문장도, byte로 표시하는 string과 byte에서 16개의 길이를 보이지만, 최대 4byte까지 표시할 수 있는 rune(int32)는 한글의 한글자를 하나로 표시할 수 있어 10의 길이를 보여준다.

3.4 맵

  • 순서없는 데이터 처리 유용
  • (Key, Value) Pair
  • [SYNTAX]
    • var 변수명 map[키타입]값타입
  • 값: 어느 타입도 가능
  • 키: 비교 가능한 타입만 가능(맵, 슬라이스는 탈락)
  • 생성
      1. nil맵(제로 값 할당) : map의 제로 값은 nil
      • nil맵은 길이 0
      • nil맵의 값을 읽으면 맵 값이 되는 타입의 제로 값
      • nil맵에 값을 쓰려고 하면 패닉 발생
      1. 비어있는 맵 리터럴 : 비어 있는 맵 생성
      • nil 맵과 다르다.
      • 길이는 0
      • 비어있는 맵 리터럴이 할당된 맵을 읽고 쓸 수 있다.
      1. 값이 있는 맵 리터럴
      • 키와 값을 콜론으로 구분
      • 마지막 요소(키, 값)의 끝에 콤마(,)를 붙인다.
      1. make() 함수로 생성
      • 맵의 요소 개수를 안다면
      • 길이는 0 (make()로 슬라이스 만드는 것과 다름)
      • 초기 지정 개수 이상으로 커질 수 있다.
      • 맵의 제로 값은 nil
      • 맵은 비교 불가능, 단 nil과 같은지 다른지는 비교 가능

해쉬

  • 만약 10000개의 데이터 중에서 하나를 찾는다고 가정하자,
    • 최악의 경우 10000번째 검색에서 발견할 것이다.
    • 그러나 10000개를 % 10하여 10개의 방에 나눠 담는다면
    • 하나를 찾을 때 %10 하여 나머지를 구해 그 값의 방에서 검색한다면 1000개의 방에서 찾기만하면 될 것이다.
    • 당연히 10000개의 방을 찾는것보다 1000개의 방에서 더 빨리 찾을 것이다.
  • 충돌, %10 해서 나머지 연산을 한다고 해도 1000개는 한방에 들어간다. 이를 충돌이라 한다.
    • 충돌은 최소화해야 하고, 또, 충돌해도 그 양이 적어야 한다.

  • 파이썬과는 다르게 없는 키값을 입력하면 에러가 나오지 않고, 제로값이 나온다.

3.4.1 맵 읽고 쓰기

  • := 연산자는 사용 불가
  • 아직 설정되지 않은 키에 할당된 값을 읽으면 값 타입의 제로 값이 반환된다.

3.4.2 콤마 OK 관용구(idiom)

  • 맵의 키에 대응되는 값이 없어도 제로 값이 리턴되지만
  • 맵의 키가 존재하는지 확인할 필요가 있을 때 주로 사용하는 패턴
    • 맵에 키가 없어서 제로 값이 반환되는 건지
    • 실제로 키가 있는데, 해당 값이 제로인 건지
    • 키가 존재하면 값과 boolean이 나온다.
    • 그러나 키가 존재하지 않는다면, 제로값과, False boolean이 나온다.

3.4.3 맵 (요소) 삭제

  • delete 내장함수 사용.
  • 키가 존재하지 않거나 nil맵인 경우 아무것도 일어나지 않음
  • delete()함수는 반환값이 없음

3.4.4 맵을 셋(집합)으로 이용

  • 집합 (셋)
    • Uniqueness (중복제거), 순서없음
  • Go는 집합형을 직접 지원하지 않고, 맵을 통해 간접적으로 지원
    • 집합의 원소로 쓰고 싶은 타입을 맵의 키 타입으로
    • 맵의 값을 불리언으로 설정

3.5 구조체

  • 맵은 배열처럼 키의 타입과 값의 타입이 정해져있기 때문에, 여러 타입의 키와 값을 담을 수 없다.
  • 여러 데이터 타입을 한데 묶어서 다루고 싶을 때 사용
  • struct
  • struct 키워드, type 키워드
  • 구조체는 사용자 정의 타입
  • 따라서 바로 사용할 순 없고
    1. 구조체 정의 -> 2. 구조체를 타입으로 하는 변수 선언
  • 구조체 항목들은 콤마로 구분하지 않는다.
  • 구조체는 어떤 블록 레벨에서도 정의 가능
  • 값을 넣지 않고 출력하니,구조체의 제로값이 출력되었다.
  • 구조체의 제로 값
    • 구조체를 구성하는 항목들의 제로 값
  • 구조체 리터럴
    • 첫 번쨰 방법
      • 구조체 항목 값은 구조체 정의한 순서대로 나열
      • 구조체 항목 값은 콤마로 구분하고 마지막 항목에서 콤마를 붙여야 함.
    • 두 번째 방법
      • 맴 리터럴과 유사
      • 순서 무관
      • 생략할 경우 제로 값으로 설정
    • 위 두 방법을 혼용할 수 없다.
  • 제로 구조체와 비어있는 구조체는 차이점이 없다.
  • 구조체의 멤버(항목)을 접근할 때는 인덱싱이 아니라 점 표기법을 사용.

3.5.1 익명 구조체

  • 한 번만 사용할 구조체
  • type 생략
  • 구조체 변수만 존재
  • 이름이 없기 때문에 다시 사용할 수 없다. (다른사람을 같이 담을 수 없다.)
  • 익명구조체는 주로
    • 외부 데이터를 구조체로 전환하는 경우
    • 구조체를 외부 데이터(JSON, 프로토콜 버퍼)로 전환하는 경우
    • 이런 경우를 마샬링(marshaling, unmarshaling)이라 부른다.

3.5.2 구조체 비교와 변환

  • 구조체 비교는 항목에 따라 다름
  • 두 개의 구조체가 같은 이름, 순서, 타입으로 구성되어 있으면 구조체 간에 타입 변환 가능.
profile
Talking Potato

0개의 댓글