한눈에 끝내는 고랭기초 - 2

velbie·2020년 11월 2일
1
post-thumbnail

if else 조건문

조건식의 괄호는 생략 가능합니다.
조건문의 중괄호는 필수입니다.
괄호의 시작과 else문은 같은 줄에서 사용합니다.
조건식에서 Optional Statement 실행가능합니다.
그래서 변수선언이나 간단한 연산도 가능합니다.

package main

import "fmt"

func main() {
	var num int

	fmt.Print("정수입력 :")
	fmt.Scan(&num)

	if val := num * 2; val == 2 {
		fmt.Print("hello\n")
	} else if val := num * 3; val == 6 {
		fmt.Print("world\n")
	} else {
		fmt.Print("worng number..\n")
	}
}

switch

break 를 따로 입력하지 않아도 해당되는 case만 수행합니다.
Go 언어에서 switch 문이 비교적 쓰임이 넓습니다.

  • switch에 전달되는 인자로 태그 사용
  • switch에 전달되는 인자로 표현식 사용
  • switch에 전달되는 인자 없이 case에 표현식 사용(참/거짓 판별)

스위치 옆에를 보세요!

	switch fruit {
	case "apple":
		fmt.Println("RED")
	case "banana":
		fmt.Println("YELLOW")
	case "grape":
		fmt.Println("PURPLE")
	}

이게 됩니다.(아래)

	switch num / 10 { //표현식
	case 1:
		result = "A"
	case 2:
		result = "B"
	case 3:
		result = "C"
	default:
		fmt.Println("모르겠어요.")
		return
	}

이것도 됩니다.(아래)

	switch {
	case a > b:
		fmt.Println("a가 b보다 큽니다.")
	case a < b:
		fmt.Println("a가 b보다 작습니다.")
	case a == b:
		fmt.Println("a와 b가 같습니다.")
	default:
		fmt.Println("모르겠어요.")
	}

실습내용으로 간단한 계산기를 만들어봤습니다.
결과문을 나중에 빼서 쓰는건 알겠는데 default로 예외 처리해주는게 인상적입니다.

	switch sel {
	case 1:
		result = num1 + num2
	case 2:
		result = num1 - num2
	case 3:
		result = num1 * num2
	case 4: 
		result = num1 / num2
	default:
		fmt.Println("잘못된입력입니다.")
		return
	}
	fmt.Printf("%.1f\n", result)

배열

Go언어에서는 두 개 이상의 변수를 모아 놓은 것을 '컬렉션' 이라고 합니다.
(언어론에선 여러갠 컬렉션, 하난 스칼라 라고 배움)
배열의 크기는 자료형을 구성하는 한 요소입니다.
[3]int와 [5]int는 string과 float32처럼 타입 자체가 다른 것입니다.
그니깐 var arr1 자료형 인데 [5]int 자체가 자료형이니깐 일관성 있는것 같습니다.

package main

import "fmt"

func main() {
	var arr1 [5]int   //길이가 5인 int형 배열 arr1을 선언 (초기화되는거 중요! zero values)
	fmt.Println(arr1) //숫자를 선언하지 않고 출력해보기 

	arr1 = [5]int{1, 2, 3, 4, 5}        //선언과 동시에 배열 초기화 (이때자료형이 뒤로 가는거 쫌 어이없긴함)
	fmt.Println(arr1, arr1[0], arr1[4]) //배열 전체와 인덱스에 저장된 값들 출력해보기

	arr2 := [4]int{4, 5, 6, 7} //:= 를 이용해 선언
	arr2[0] = 32               //인덱스를 이용해 값을 초기화
	fmt.Println(arr2)          //arr2 전체 출력해보기

	var arr3 = [...]int{9, 8, 7, 6} //[...]을 이용한 배열 크기 자동 설정
	fmt.Println(arr3, len(arr3))    //arr3 전체와  배열 크기 출력해보기
}

첫번째꺼 zero values
위 예제에서 사용되는 모든 배열이 이후에 크기를 바꿀 수 없습니다. (정적배열이기 때문)
예제에서도 메모리를 정적으로 잡는 예제만 있습니다.

슬라이스

배열은 크기 증가 못시킵니다.
배열은 부분 배열 발췌 (파이썬에서 array[2:3] 같은거)를 못합니다.

슬라이스는 고정된 크기를 미리 지정하지 않고 이후에 필요에 따라 크기를 동적으로 변경할 수 있고, 부분 발췌가 가능합니다.
슬라이스는
var arr2 [3]int라고 입력하면 3개의 int타입의 변수가 들어갈 메모리를 만들고, 초기화하지 않았기 때문에 자동으로 0이 할당됩니다. 따라서 선언만 했을 뿐인데 len() 함수를 이용해 배열의 크기가 3이라는 것을 확인할 수 있습니다.

기본적으로 슬라이스는 아무런 값도 초기화하지 않아도 배열의 위치를 가리키는 ptr과 배열의 길이인 len, 전체크기의 cap 메모리를 가지고 있습니다.
l = array[2:5] 아래 처럼 됨 (from index 2 until index 5)

슬라이스의 기본적이 사용방법
일단 슬라이스는 [] 대괄호 안에 비웁니다. 그럼 슬라이스 인것 같습니다.

package main

import "fmt"

func main() {
	var a []int        //슬라이스 변수 선언 아무것도 초기화 되지 않은 상태
	a = []int{1, 2, 3} //슬라이스에 리터럴값 지정

	a[1] = 10 //값이 할당되어 메모리가 생겼기 때문에 이렇게 접근 가능

	fmt.Println(a)

	var b []int //nil slice 선언

	if b == nil {
		fmt.Println("용량이", cap(b), "길이가", len(b), " Nil Slice입니다.")
	}
}

make()

메모리가 잡힘
make() 함수를 이용한 슬라이스 선언
슬라이스를 생성하는 또 다른 방법으로는 Go언어의 내장 함수인 make() 함수를 이용한 선언입니다
"make(슬라이스 타입, 슬라이스 길이, 슬라이스의 용량)" 형태로 선언합니다. 용량은 생략해서 선언할 수 있습니다. 용량을 생략한다면 슬라이스의 길이와 똑같은 값으로 선언됩니다.
make()사용하면 모든 요소가 0인 슬라이스를 만들게 됩니다.

  • 길이: 실제 있는 개수
  • 용량: append 하는것 까지 포함해서 가능한 용량

중요

package main

import "fmt"

func main() {
	fmt.Println("Hello, world!")
	var s = make([]int, 3, 5) // length 3, capacity 5
	var n = len(s)            // n == 3
	var c = cap(s)            // c == 5
	fmt.Println(n, c)

	s = append(s, 3) // s is now []int{0, 0, 0, 3, 4}
	s = append(s, 4) // s is now []int{0, 0, 0, 3, 4}
	s = append(s, 6) // s is now []int{0, 0, 0, 3, 4, 6}
	n = len(s)       // n == 5
	c = cap(s)       // c == 10 자동으로 두배가 되네..
	fmt.Println(n, c)

}

신기하게도 용량이 자동으로 두배가 됩니다!
슬라이스는 용량이 초과는 경우에는 설정한 용량만큼 새로운 배열을 생성하고 기존 배열 값들을 모두 새 배열에 복제한 후 다시 슬라이스를 할당하는 방식입니다.
슬라이스에 슬라이스를 추가하려면 ...을 사용해야합니다.
(js에도 파이썬에도 있는 spread 연산자와 비슷하네요..)

package main
 
import "fmt"
 
func main() {
    sliceA := []int{1, 2, 3}
    sliceB := []int{4, 5, 6}
 
    sliceA = append(sliceA, sliceB...)
    //sliceA = append(sliceA, 4, 5, 6)
 
    fmt.Println(sliceA) // [1 2 3 4 5 6] 출력
}

배열은 복사고, 슬라이스는 참조입니다.

Map

'Hash table' 즉, 파이썬의 dictionary js의 객체.
슬라이스와 마찬가지로 '참조 타입(Reference type)' 입니다.
슬라이스랑 마찬가지로 선언만 하고 값을 초기화하지 않았다면 'Nil map'이 됩니다.
아래와 같이 사용할수 있습니다. (순서없습니다.)

package main

import "fmt"

func main() {
	var a map[int]string

	if a == nil {
		fmt.Println("nil map")
	}

	var m = map[string]string{ //key:value, 형식으로 초기화한다
		"apple":  "red",
		"grape":  "purple",
		"banana": "yellow",
	}

	fmt.Println(m, "\nm의 길이는", len(m))
}

map은 {}를 사용한 예제는 없네.. 주로 make 인가보네t

map 변수의 추가, 갱신, 삭제

맵 컬렉션에 메모리가 할당(make() 함수 혹은 {}를 사용한 값 초기화)됐으면 값을 추가, 갱신, 삭제할 수 있습니다. (메모리가 잡혀야지 CRUD 할수있다는것)
delete는 아래코드에 있습니다.

package main

import "fmt"

func main() {
	//지역번호와 지역 저장
	var m = make(map[string]string)

	m["02"] = "서울특별시"
	m["031"] = "경기도"
	m["032"] = "충청남도"
	m["053"] = "대구광역시"

	fmt.Println(m)

	//동일한 key값으로 value값을 저장하면 갱신이 된다
	m["032"] = "인천"

	fmt.Println(m)

	//m에 있는 "031"key의 value와 함께 삭제
	delete(m, "031")

	fmt.Println(m)
}

'맵이름[key]'는 key에 저장돼 있는 value 값을 반환할 뿐만 아니라, 해당 키에 값이 존재하는지 안 하는지 즉, 사용하고 있는 key 값인지 아닌지 판별해주는 true/false 값도 반환합니다.

package main

import "fmt"

func main() {
	//지역번호와 지역 저장
	var m = make(map[string]string)

	m["02"] = "서울특별시"
	m["031"] = "경기도"
	m["032"] = "인천"
	m["053"] = "대구광역시"

	fmt.Println(m["032"])
	fmt.Println(m["042"], "빈 칸입니다.") //string형태로 존재하지 않는 key값은 ""가 출력된다

	val, exist := m["02"] //존재하는 key
	fmt.Println(val, exist)

	val, exist = m["042"] //존재하지 않는 key
	fmt.Println(val, exist)

	val = m["053"] //value 값만 반환
	fmt.Println(val)

	_, exist = m["053"] //true/false 값만 반환
	fmt.Println(exist)

	//맵도 똑같이 len() 함수를 사용할 수 있다 하지만 cap() 함수는 사용할 수 없다
	fmt.Println(len(m))
}

map을 for range문으로 전체 출력하면 순서대로 출력되지 않습니다. 슬라이스와 배열과 같은 컬렉션은 인덱스가 0부터 순서대로 지정돼 연결되어 저장되고 순서가 중요하지만 map 컬렉션은 인덱스도 사용자 지정이라 순서가 따로 지정되지 않습니다. 따라서 range문을 이용한다고 해서 순서대로 출력되는것은 아닙니다.

배열 시행착오

2차원 배열 선언하려는데 자꾸 에러가 발생했습니다.
제가 작성한 2차원 배열(아래)

var a [2][2]int{{7,3}, {5,2}}

정상적인 2차원 배열 선언방법

var multiArray [2][3][4]int //3차원 배열 선언 후
multiArray[1][1][2] = 10  // 인덱스를 이용한 값 초기화

= 이 있어야함

    var a = [3][3]int{
		{1, 2, 3},        
		{4, 5, 6},
		{7, 8, 9}, //3x3배열 초기화
    }
	
    fmt.Println(a[1][2]) //2행 3열의 값 출력

아래는 콤마가 없어서 에러;;

var a [2][2]int{
	{7,3},
    {5,2}
}

배열 선언이 var a [2][2] = {{1,2},{3,4}} 이게 안되는이유

나는 위에처럼 사용하는게 일관성 있어보이는데
원래는

// 방법 1
var name [2]string = [2]string{"ychae", "leah"}
// 방법 2
var age = [4]int{34,12,25,11}

방법 1을 축약한거니 그럴수 있다치자
그러면 왜 방법1 에서 앞에를 생략했지? 나는 뒤에를 생략해야한다고 생각하는데..
array literal 이란 개념을 알면 이해가 됩니다.

아래는 슬라이스에 대해서 강사님이 남긴 코멘트

그런데 아주 깊게 생각해서 하나만 생각해보자면 슬라이스를 생성하는 부분입니다. 슬라이스의 요소를 처음부터 append로 추가하려는 생각으로 길이를 0으로 지정하는 것은 좋습니다. 그런데 용량은 대략 어느정도 길이의 슬라이스를 사용할 것인가를 지정하는 것이기 때문에 메모리 관리에 있어서 중요할 수 있습니다.
슬라이스의 용량을 크게 할당하면 요소가 추가될 때마다 메모리를 새로 할당하지 않아도 되므로 성능상 이점이 있습니다 하지만 처음부터 메모리 공간을 많이 차지하게 됩니다. 반대로 슬라이스 용량을 적게하거나 지정하지 않으면 처음부터 메모리 공간은 적게 차지하지만, 요소가 추가될 때마다 메모리를 새로 할당하게 되므로 성능이 떨어질 수 있습니다. 따라서 상황에 따라 올바른 용량을 설정하는 것이 메모리 관리에 효율적입니다.

profile
안녕하세요

0개의 댓글