트랙
/
Go
Go
/
연습 문제
/
속도가 필요해
속도가 필요해

속도가 필요해

학습 연습 문제

소개

Go에서 struct는 필드 라고 부르는 이름 있는 요소들의 나열이고, 각 필드는 이름과 타입을 가져요. 필드의 이름은 구조체 안에서 유일해야 해요. 구조체는 객체 지향 프로그래밍 패러다임의 클래스 와 비교할 수 있어요.

구조체 정의하기

type과 struct 키워드를 사용해 새 구조체를 만들고, 필드의 이름과 타입을 명시적으로 정의해요. 예를 들어 여기서는 도형의 이름과 크기를 담는 Shape 구조체를 정의해요:

type Shape struct {
    name string
    size int
}

구조체의 필드 이름은 Go 관례를 따라요. 이름이 소문자로 시작하는 필드는 같은 패키지의 코드에서만 볼 수 있고, 대문자로 시작하는 필드는 다른 패키지에서도 볼 수 있어요.

구조체의 인스턴스 만들기

struct를 정의했다면, 필드 이름을 사용해 순서에 상관없이 필드를 지정하면서 새 인스턴스를 만들어야 해요:

s := Shape {
    name: "Square",
    size: 25,
}

인스턴스의 필드를 읽거나 수정하려면 . 표기를 사용해요:

// Update the name and the size
s.name = "Circle"
s.size = 35
fmt.Printf("name: %s size: %d\n", s.name, s.size)
// Output: name: Circle size: 35

초깃값이 할당되지 않은 필드는 제로 값을 가져요. 예를 들면요:

s := Shape{name: "Circle"}
fmt.Printf("name: %s size: %d\n", s.name, s.size)
// Output: name: Circle size: 0

필드를 순서대로 정의하기만 하면 필드 이름 없이도 struct의 인스턴스를 만들 수 있어요:

s := Shape {
	"Oval",
	20,
}

하지만 이런 문법은 깨지기 쉬운 코드 로 여겨져요. 필드가 추가될 때, 특히 새 필드의 타입이 다를 때 깨질 수 있거든요. 다음 예에서는 Shape에 필드를 하나 더 추가해요:

type Shape struct {
	name        string
	description string // new field 'description' added
	size        int
}

s := Shape{
    "Circle",
    20,
}
// Since the second field of the struct is now a string and not an int,
// the compiler will throw an error when compiling the program:
// Output: cannot use 20 (type untyped int) as type string in field value
// Output: too few values in Shape{...}

"New" 함수

구조체 인스턴스를 만드는 걸 도와주는 함수가 있으면 좋을 때가 있어요. 관례적으로 이런 함수는 보통 New라고 부르거나 이름이 New로 시작해요. 하지만 그냥 평범한 함수이기 때문에 원하는 이름을 아무거나 붙여도 돼요. 다른 언어의 생성자가 떠오를 수도 있지만, Go에서는 그냥 평범한 함수일 뿐이에요.

다음 예에서는 이런 New 함수 중 하나로 Shape의 새 인스턴스를 만들면서 도형의 size에 기본값을 자동으로 설정해요:

func NewShape(name string) Shape {
	return Shape{
		name: name,
		size: 100, //default-value for size is 100
	}
}

NewShape("Triangle")
// => Shape { name: "Triangle", size: 100 }

New 함수를 사용하면 다음과 같은 장점이 있어요:

  • 주어진 값의 유효성 검사
  • 기본값 처리
  • New 함수는 초기화하는 구조체와 같은 패키지에 선언되는 경우가 많아서, 구조체의 비공개 필드까지 초기화할 수 있어요

지침

이 연습 문제에서는 여러 종류의 무선 조종 자동차 사이의 경주를 준비해요. 각 자동차는 각자의 속도와 배터리 소모 특성을 가지고 있어요.

자동차는 완전히 충전된(100%) 배터리로 시작해요. 리모컨으로 자동차를 운전할 때마다 자동차는 속도만큼 미터를 이동하고, 남은 배터리 비율은 배터리 소모량만큼 줄어들어요.

자동차의 배터리가 배터리 소모량보다 낮으면 더 이상 그 자동차를 운전할 수 없어요.

각 경주 트랙은 고유한 거리를 가지고 있어요. 자동차는 배터리가 다 닳기 전에 트랙을 완주할 수 있는지 확인하는 방식으로 테스트해요.

1. 무선 조종 자동차 만들기

다음과 같은 int 타입 필드를 가진 Car 구조체를 정의해요:

  • battery
  • batteryDrain
  • speed
  • distance

NewCar 함수를 정의해서 무선 조종 자동차를 만들 수 있어요. 이 함수는 자동차의 속도(미터)와 배터리 소모량을 두 매개변수로 받고(둘 다 int 타입), Car 인스턴스를 반환해요:

speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
// => Car{speed: 5, batteryDrain: 2, battery:100, distance: 0}

2. 경주 트랙 만들기

정수 타입의 distance 필드를 가진 Track이라는 또 다른 구조체 타입을 정의해요. NewTrack 함수를 정의해서 경주 트랙을 만들 수 있어요. 이 함수는 트랙의 거리(미터)를 유일한 매개변수로 받아요(타입은 int):

distance := 800
track := NewTrack(distance)
// => Track{distance: 800}

3. 자동차 운전하기

자동차의 속도를 기준으로 주행한 미터 수를 갱신하고, 배터리 소모량에 따라 배터리를 줄이는 Drive 함수를 구현해요. 한 번 더 운전할 배터리가 부족하면 자동차는 움직이지 않아요:

speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
car = Drive(car)
// => Car{speed: 5, batteryDrain: 2, battery: 98, distance: 5}

4. 무선 조종 자동차가 경주를 완주할 수 있는지 확인하기

경주를 완주하려면 자동차가 경주의 거리만큼 운전할 수 있어야 해요. 즉, 결승선을 통과하기 전에 배터리가 다 닳지 않아야 해요. Car와 Track 인스턴스를 매개변수로 받아서, 자동차가 경주를 완주할 수 있으면 true를 반환하고, 그렇지 않으면 false를 반환하는 CanFinish 함수를 구현해요.

지금 경주의 출발선에 있고 경주를 위해 자동차의 시동을 걸었다고 가정해요. 경주를 시작할 때 자동차의 배터리가 반드시 완전히 충전되어 있지는 않을 수 있다는 점을 고려해요:

speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)

distance := 100
track := NewTrack(distance)

CanFinish(car, track)
// => true
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Go Exercism

속도가 필요해 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Go 트랙을 개념 34개연습 문제 165개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.