#include <stdio.h>
#include <assert.h>
/*
* 문자열을 int 형으로 바꾼다.
*
* 로직의 핵심은 파싱이 가능한 곳까지 전진(forward) 하고,
* 다시 되감기(rewind) 하면서 10씩 곱하여 합산한다.
*
* -----
*
* 처음 접근방식은 '전진'하면서 구현했었다. 그런데 , 1의 자리, 10의 자리, 100의 자리 처리가 어렵다는 것을 알게되었다.
* 그래서 파싱이 가능한 부분까지 '전진'을 한뒤에, 반대방향으로 '되감기'를 하면서 처리를 하는 것이 맞겠다고 생각이 들었다.
*
* '아니 어떻게 저런 접근방법을 생각해냈지?' 의문이 들텐데,
* 공책에 '123'를 써넣고, '이것을 어떻게 123 라는 숫자형으로 바꿀까?' 라는 물음에서 시작했다.
* '100 + 20 + 3' 를 떠올렸다. 자연스러운 생각의 흐름이다. 기억을 더듬어보면 수학시간에 자릿수 계산을 그렇게 배웠다.
*
* 그리곤 그것은 곧 '1 * 100 + 2 * 10 + 3 * 1'와 동일하다는 것도 떠올릴 수 있었다.
* 그렇다. 핵심은 100, 10, 1 자릿수를 결정짓는 숫자다.
* 그러면, 자릿수를 위해 곱해지는 100, 10, 1 이라는 숫자는 어떻게 알수 있을까?
*
* 정방향으로 시작하면 100을 곱할지, 10을 곱할지, 1을 곱할지 알수가 있나? ==> '아니.. 어렵다'
* 그렇다! 조금 더 관찰해보니 100, 10, 1은 역방향 순이라는 것을 알수가 있었다. ==> '아하, 반대로 탐색하면 되겠구나!'
*
* 이러한 생각의 흐름이 문제를 해결하는 방법이자, 접근법이다.
* 즉, 나는 논리적인 사고의 흐름에 따라 문제를 해결할 수 있었다.
* 그리고 초등학교때 배운 약간의 수학 지식이 사용되었다.
*
* 내가 생각해낸 방법을 프로그래밍 언어로써 표현한 것이다.
* 약간의 포인터 연산 트릭이 동원되었다. (이런 트릭은, 다른 많은 코드를 접하면서 자주 보이는 패턴들이다.)
*
*/
int atoi (const char* str) {
/*
* 처음 시작점 찾기 - 공백 건너뛰기
* " -123 4 "
* ---^
*/
while (*str == ' ') {
str++;
}
/*
* 음수여부 검사 (char을 사용하여 공간절약)
* " -123 4 "
* ^
*/
char sign_base = *str == '-' ? -1 : 1;
/*
* 첫 문자가 부호문자라면 한칸 전진 (forward)
* " -123 4 "
* ^
*/
if (*str == '-' || *str == '+') {
str++;
}
/*
* 문자열 파싱을 위함 이동 포인터.
* (주목!!)
* - ptr은 앞으로 파싱을 수행한 이동 포인터다
* - str은 시작점이다 (추후 while 조건에서 rewind의 종료 조건으로 사용되니 주의바람)
*
* " -123 4 "
* ^------------ [ptr]
*/
const char* ptr = str;
/*
* 숫자가 아닌 곳까지 전진 (forward)
* 파싱할 수 있는 끝까지 전진한 뒤 되감기(rewind)하면서 합산할 예정이다
*
* " -123 4 "
* ---^
*/
while ('0' <= *ptr && *ptr <= '9') {
ptr++;
}
/*
* 지수 (1, 10, 100, 1000, ...)
*/
int base = 1;
/*
* 파싱한 결과값
*/
int result = 0;
/*
* 파싱할 수 있는 곳까지 전진(forward)한 포인터 ptr을 다시 되감기(rewied)하면서 base를 곱하여 합산한다
* 되감기를 해야 base 를 계산하기 편하다.
* 반복 조건은 str과 같아질 때까지 되감는다. (-- 연산자 위치에 주의 바람)
*
* " -123 4 "
* ^^^
*/
while (str <= --ptr) {
result += (*ptr - '0') * base;
base *= 10;
}
/*
* 위에서 미리 구한 음수여부를 곱하여 계산을 마친다.
*/
return result * sign_base;
}
void main() {
// 빈 문자열
assert(atoi("") == 0);
assert(atoi(" ") == 0);
assert(atoi(" ") == 0);
// 숫자
assert(atoi("0") == 0);
assert(atoi("1") == 1);
assert(atoi("12") == 12);
assert(atoi("123") == 123);
// 부호 (+)
assert(atoi("+") == 0);
assert(atoi("+0") == 0);
assert(atoi("+1") == 1);
assert(atoi("+12") == 12);
assert(atoi("+123") == 123);
// 부호 (-)
assert(atoi("-") == -0);
assert(atoi("-0") == -0);
assert(atoi("-1") == -1);
assert(atoi("-12") == -12);
assert(atoi("-123") == -123);
// 처음에 문자포함
assert(atoi("x0") == 0);
assert(atoi("x1") == 0);
assert(atoi("x12") == 0);
assert(atoi("x123") == 0);
// 중간에 문자열 포함
assert(atoi(".123") == 0);
assert(atoi("1.23") == 1);
assert(atoi("12.3") == 12);
assert(atoi("123.") == 123);
assert(atoi("-.123") == 0);
assert(atoi("-1.23") == -1);
assert(atoi("-12.3") == -12);
assert(atoi("-123.") == -123);
// 처음 공백포함
assert(atoi(" 123") == 123);
assert(atoi(" +123") == 123);
assert(atoi(" -123") == -123);
assert(atoi(" 1 23") == 1);
assert(atoi(" +1 23") == 1);
assert(atoi(" -1 23") == -1);
// 뒤에 공백포함
assert(atoi("123 ") == 123);
assert(atoi("+123 ") == 123);
assert(atoi("-123 ") == -123);
assert(atoi("1 23 ") == 1);
assert(atoi("+1 23 ") == 1);
assert(atoi("-1 23 ") == -1);
// 앞뒤 공백포함
assert(atoi(" 123 ") == 123);
assert(atoi(" +123 ") == 123);
assert(atoi(" -123 ") == -123);
assert(atoi(" 1 23 ") == 1);
assert(atoi(" +1 23 ") == 1);
assert(atoi(" -1 23 ") == -1);
assert(atoi(" 123 ") == 123);
assert(atoi(" +123 ") == 123);
assert(atoi(" -123 ") == -123);
assert(atoi(" 1 23 ") == 1);
assert(atoi(" +1 23 ") == 1);
assert(atoi(" -1 23 ") == -1);
// 의미 없음
assert(atoi(" -") == 0);
assert(atoi(" +") == 0);
assert(atoi(" x") == 0);
assert(atoi("- ") == 0);
assert(atoi("+ ") == 0);
assert(atoi("x ") == 0);
assert(atoi(" - ") == 0);
assert(atoi(" + ") == 0);
assert(atoi(" x ") == 0);
printf("OK");
}