한 눈에 끝내는 고랭기초
구름Edu에서 고랭배우고 있는데, 제가 자꾸 놓쳤던 부분을 메모해보려 합니다.
변수와 상수
이건 선언과 동시에 할당입니다. "선언"이 있다는 걸 까먹지 말아야겠습니다.
name := "velbie"
콘솔 출력과 입력
보통 Println() 을 사용하고, 좀더 자세히 찍을때 서식출력이 가능한 Printf() 를 사용했습니다.
입력은 주로 Scanf를 사용했습니다.
fmt.Println()
fmt.Print()
fmt.Printf()
var age, birth int
fmt.Scan(%age, &birth)
var height int
fmt.Scanf("%d\n", &height)
fmt.Scanfln()
%v 서식문자
이 문자를 봤을때 갸우뚱했습니다.
실수 출력할때 사용해야 겠습니다.
찾아보니깐 위에 코드처럼 여러 형식을 출력하기도 하고, 아래처럼 구조체 인스턴스를 출력할때도 사용됩니다.
구조체의 필드명까지 출력하려면 + 기호를 붙이고, 메타정보까지 출력하려면 #을 붙이면 됩니다.
fmt.Printf("모든 형식으로 출력: %v, %v\n", 54.234, "Hello")
type User struct {
name string
age int
}
func main() {
u := User{"Gopher", 10}
fmt.Printf("%v", u) // {Gopher 10}
}
fmt.Printf("%+v", u) // {name:Gopher, age:10}
fmt.Printf("%#v", u) // main.user{name:"Gopher", age:10}
루프
일반 for 문, for range 문이 있습니다. for range는 map은 슬라이스와 map 모두 사용가능합니다.
for _, v := range studentSlice {
fmt.Println(v.name, v.sex)
for k, v := range v.subject {
fmt.Println(k, v)
}
fmt.Println("----------")
}
배열과 슬라이스와 맵
슬라이스 선언법은 일반 선언법, make로 선언하는 법
맵 선언법도 일반 선언법과, make로 선언하는 법, 저는 아래와 같이 슬라이스는 make로, 맵은 그냥 선언합니다.
배열 append 하는게 없애고 새로 생성하는거여서 약간 특이합니다.
subject map[string]int = map[string]int{}
numbers []int = make([]int, 0, 0)
number = append(number, 1)
함수에 슬라이스 넘기고 받기
슬라이스를 num... 으로 펴서 넘기고 ...num 으로 모아서 받으면 됩니다.
func bubbleSort(nums ...int) []int{
for j:=0; j<len(nums)-1; j++ {
for i:=0; i<len(nums)-1; i++ {
if nums[i] > nums[i+1] {
var temp int
temp = nums[i+1]
nums[i+1] = nums[i]
nums[i] = temp
}
}
}
return nums
}
nums = bubbleSort(nums...)
함수반환형과 괄호
함수반환형이 하나면 소괄호 없어도 되고 여러개면 있어야합니다. 그리고 예외로 Named return parameter 는 값이 하나라도 괄호를 사용해야합니다.
func inputFruit() (list []string) {
return
}
함수넘기기와 일급함수
일급하수라는 의미는 함수를 기본타입을 사용하듯이 사용할수 있다는 것 입니다. String타입의 리터럴을 매개변수로 넘기고 반환할 수 있는 것 처럼, "함수타입의 리터럴을 매개변수로 넘기고 반환할 수 있습니다." -> 이게 말이 안되는데 일급함수에서는 말이 됩니다. 일급함수는 이급함수가 아닌 다른 타입과 동등한 일급이기 때문입니다.
이로써 클로져라는 개념이 가능해집니다.
익명함수: 가볍게 찾지 않고 필요한 곳에 바로 실행할수 있는 함수가 필요합니다. 어디에 어떻게 사용되나면 이벤트처리, 콜백함수!, 클로져 'defer', 'Go루틴'등 , 변수에 초기화 등 여러곳에 사용됩니다.
변수에 초기화되는것은 일급함수니깐 가능한것 입니다. 이로써 또 다른 기능들이 사용가능해집니다.
함수넘길때 너무 길어지니깐 type문을 사용한 함수 원형 정의 가 등장합니다.
package main
import "fmt"
//함수 원형 정의
type calculatorNum func(int, int) int
type calculatorStr func(string, string) string
func calNum(f calculatorNum, a int, b int) int {
result := f(a, b)
return result
}
func calStr(f calculatorStr, a string, b string) string {
sentence := f(a, b)
return sentence
}
func main() {
multi := func(i int, j int) int {
return i * j
}
duple := func(i string, j string) string {
return i + j + i + j
}
r1 := calNum(multi, 10, 20)
fmt.Println(r1)
r2 := calStr(duple, "Hello", " Golang ")
fmt.Println(r2)
}
클로져
클로져를 통해 상태라는 개념이 생겨납니다.
클로져는 뭔가 복잡한 분기문같은거 를 추상화해서 함수로 구분을 해서 코드가 직관적으로 변하는것 같습니다.
4개의 동전에 따라 계산하는 함수를 각각 만들면 낭비일 것입니다.
클로저를 이용해 함수를 한 개만 만들어보겠습니다.
package main
import "fmt"
/*타입문 작성은 선택입니다*/
type f func(int) int
func calCoin(cnt int) f {
sum := 0
return func(amount int) int{ //클로저
sum += cnt * amount
return sum
}
}
func main() {
var coin10, coin50, coin100, coin500 int
fmt.Scan(&coin10, &coin50, &coin100, &coin500)
if coin10 < 0 || coin50 < 0 || coin100 < 0 || coin500 < 0 {
fmt.Println("잘못된입력입니다.")
return
}
add10 := calCoin(coin10)
add50 := calCoin(coin50)
add100 := calCoin(coin100)
add500 := calCoin(coin500)
totalmoney := add10(10) + add50(50) + add100(100) + add500(500)
fmt.Println(totalmoney)
}
구조체
포인터 구조체(new) 말고 당분간 그냥 구조체 객체를 생성해서 사용해야 겠습니다. 생성자 함수도 있지만 지금은 잠깐 넘어가겠습니다. 구조체 안에 ,(콤마) 가 없습니다!
구조체는 사용자 임의로 하나 이상의 변수를 묶어 새로운 자료형을 정의한 것
package main
import "fmt"
type person struct {
name string
age int
contact string
}
func addAgeRef(a *person) { //Pass by reference
a.age += 4 //자동 역참조 * 생략
}
func addAgeVal(a person) { //Pass by value
a.age += 4
}
func main() {
var p1 = new(person) //포인터 구조체 객체 생성
var p2 = person{} // 빈 구조체 객체 생성
fmt.Println(p1, p2)
p1.age = 25
p2.age = 25
addAgeRef(p1) //&을 쓰지 않아도 됨
addAgeVal(p2)
fmt.Println(p1.age)
fmt.Println(p2.age)
addAgeRef(&p2) //&을 붙이면 pass by reference 가능
fmt.Println(p2.age)
}
구조체 메서드
student := sStudent{} 이걸로 생성 충격의 포인터 리시버와 밸류 리시버, 여기서도 당분간 벨류 리시버만 사용하겠습니다.
package main
import "fmt"
type triangle struct {
width, height float32
}
func (s triangle) triAreaVal() float32 { //Value Receiver
s.width += 10
return s.width * s.height / 2
}
func (s *triangle) triAreaRef() float32 { //Pointer Reciver
s.width += 10
return s.width * s.height / 2
}
func main() {
tri1 := new(triangle)
tri1.width = 12.5
tri1.height = 5.2
triarea_val := tri1.triAreaVal()
fmt.Printf("삼각형 너비:%.2f, 높이:%.2f 일때, 넓이:%.2f \n", tri1.width, tri1.height, triarea_val)
triarea_ref := tri1.triAreaRef()
fmt.Printf("삼각형 너비:%.2f, 높이:%.2f 일때, 넓이:%.2f ", tri1.width, tri1.height, triarea_ref)
}
인터페이스
추상화(class)가 있으면 직관적일수 없는것 같습니다 . 그래서 고는 class는 없애고 인터페이스로 직관성을 강조한것 같습니다.
매개변수로 인터페이스를 사용한다는 것은 구조체에 관계없이 인터페이스에 포함된 메소드를 사용하겠다는 뜻입니다.
printMeasure(r1, c1, r2, c2)
func printMeasure(m ...geometry) { //인터페이스를 가변 인자로 하는 함수
for _, val := range m { //가변 인자 함수의 값은 슬라이스형
fmt.Println(val.area()) //인터페이스의 메소드 호출
}
}
아래 코드처럼 사용해도 역시 정상 작동합니다. 신기한점은 선언할때는 메서드와 인터페이스간의 연관성이 없지만. 사용할때는 인터페이스로 받아서 인터페이스에 선언된 껍데기 함수로 메서드를 호출하는게 신기했습니다.
package main
import (
"fmt"
)
type geometry interface { //인터페이스 선언 Reat와 Circle 메도스의 area를 모두 포함
area() float64
}
type Rect struct {
width, height float64
}
func (r Rect) area() float64 {
return r.width * r.height
}
func main() {
r1 := Rect{10, 20}
printMeasure(r1)
}
func printMeasure(r Rect) { //"인터페이스를 가변 인자로" 하는 함수
var m geometry = r
fmt.Println(m.area())
// for _, val := range m { //가변 인자 함수의 값은 슬라이스형
// fmt.Println(val.area()) //인터페이스의 메소드 호출
// }
}