History
210223 ~ 1.4 assert 활용
210301 ~ 1.5 제어문
TODO: 210302 ~ 1.6 함수
읽기 좋은 코드 "의도를 명확하게 전달할 수 있는 코드"
다양한 기술을 사용한다 해도 의도를 제대로 전달할 수 없다면 의미가 없다.
읽기 좋은 코드를 작성하는 요령을 간단하게 설명하면 '복잡한 코드 문제를 작게 나누고, 읽기 좋은 이름을 붙여 정리하자' 이다.
for (int i=0; i < 4; ++i) {
for( intj=0; j< 4; ++j) {
matrix[i][j] = 0;
}
}
enum State {
STATE_WALK,
STATE_JUMP,
STATE_ATTACK
}
const bool isJump = y > 0.0f;
const bool isDamage = state == STATE_DAMAGE;
const bool isDash = (speed >= 10.0f) && !isJump && !isDamage;
bool isJump() {
return y > 0.0f;
}
bool isDamage() {
return state == STATE_DAMAGE;
}
bool isDash() {
if(speed < 10.0f) return false;
if(isJump()) return false;
if(isDamage()) return false;
return true;
}
if(isDash or isDash()) {
...
}
하나의 의미를 나타내는 코드라면 비록 한 줄이라고 해도 함수화해야 하는 후보가 된다.
void display_week(int week) {
static const char* name[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
assert(0 <= week && week <= 6 && "정해지지 않은 요일");
std::cout << name[week];
}
코드를 복잡하게 만드는 문제의 근원은 if 조건문 또는 for 반복문 등의 제어문 이다.
int clamp(int x, int low, int high) {
assert(low<=high);
return std::min(std::max(x, low), high);
}
x = clamp(x, 0, 10);
if(x >= 10) x = 0;
x = x % 10;
if(state == STATE_FULL && wait_timer > WAIT_TIME) fall();
if(state == STATE_MOVE && wait_timer > WAIT_TIME) move();
if(wait_timer > WAIT_TIME) {
if(state == STATE_FULL) fall();
if(state == STATE_MOVE) move();
}
초보자는 조건이라는 말을 들으면 일단 if 조건문이 필요하다고 먼저 생각하기 쉽다.
하지만 코드의 보수성을 높이려면 if 조건문을 사용하지 않고 해결할 방법을 생각하는 것이 중요하다.
//플레이어가 존재하는지 확인
if(player != nullptr) {
//존재한다면 이동
player->move();
}
...
if(player != nullptr) {
player->draw();
}
class Actor {
...
virtual void move() = 0;
virtual void draw() = 0;
};
class NullActor : public Actor {
...
virtual void move() override {}
virtual void draw() ovrride {}
}
player = new NullActor();
player->move();
player->draw();
for 반복문의 기본 원칙은 '1개의 작업만 반복' 이다.
//for_each
std::for_each(actors.begin(), actors.end(), [](Actor* actor) { actor->draw(); });
//find_if
auto player = std::find_if(actors.begin(), actors.end()
[](Actor* actor) { return Actor->id() == PLAYER;});
if(player != actors.end()) {
attack(*player);
}
//min_element, max_element
//all_of
//count_if
//accumulate
...etc
//반복
for(iter i = actors.begin(); i != actors.end(); ++i) {
//검색
if((*i)->distance(position) <= 5) {
attack(*i);
break;
}
}
//검색부분 함수화
auto target = findTarget(position, 5);
if(target != nullptr) {
attack(target);
}
반복문 내부의 불필요한 조건 분리
- 외부로 뺄 수 있는 조건문은 빼준다.
반복문 분할
코드를 짧게 만들기 위해 1개의 반복문에 여러 개의 처리를 집어넣으면 코드가 복잡해져 버린다.
단순한 for 반복문으로 나누고 STL 알고리즘으로 치환해주자.
람다식을 활용한 반복문 일반화