Liemani

blog

홈으로

반복하며 작업하는 코드의 작성(1)

October 10, 2021


Index

  1. 작업에 순서가 존재하는 반복 코드
  2. 조건에 따라 특정 작업을 하는 반복 코드
  3. 조건에 따라 가변적 횟수 동안 특정 작업을 하는 반복 코드


작업에 순서가 존재하는 반복 코드


한 문자열을 다른 문자열 뒤에 복사해 붙이는 코드가 필요하다고 하자. 이를 라이브러리 함수 없이 구현한다면 아래와 같을 것이다. (이 때, 문제에 집중하기 위해 target_buf 은 문자열 source_str 을 뒤에 붙이기(append) 에 충분한 메모리 공간을 할당받았다고 하자)

void	append_str(char *target_buf, const char *source_str)
{
	while (*target_buf != '\0')
		++source_str;
	while ((*target_buf++ = *source_str++) != '\0')
		;
}

위의 구현에서는 while 문을 2 번 사용했지만, 사실 전체적으로 보면 target_buf 을 끝날 때까지 반복하고 있다. 하지만 이 경우는 작업의 순서가 정해져 있기 때문에 굳이 반복문 한 번으로 코드를 복잡하게 만들 필요 없이, 반복문을 2 번 사용하는 것으로 쉽게 구현이 가능하다. 더 생각해볼 만한 부분이 있다면, ‘가독성을 높이기 위해 두 번째 while 문을 조금 더 길게 쓸 것인가’ 와 ‘마지막 null character(‘\0’) 는 어디서 복사되는가?’ 일 것이다. 위의 방법은 while 의 조건문에서 값을 복사하기 때문에 마지막에 ‘\0’ 를 복사한 후 조건문을 검사한다. 그래서 추가적으로 ‘\0’ 를 마지막에 넣어 줄 필요가 없다. 그런데 만약 2 번째 while 문을 다음과 같이 작성한다면,

	while ((*target_buf++ = *source_str++) != '\0')
		;

	|
	V

	while (*source_str != '\0')
		*target_buf++ = *source_str++;
	*target_buf = '\0';

값 복사를 while 문의 조건문(condition) 위치가 아닌 몸통(body)에서 진행하기 때문에, source_str 의 마지막 ‘\0’ 는 복사되지 않고 while 문을 빠져나오게 된다. 그래서 target_buf 의 마지막 ‘\0’ 는 직접 넣어 주어야 한다.


조건에 따라 특정 작업을 하는 반복 코드


이번엔 조금 더 들어가 본다. 반복되는 와중에 처리 방식이 그때 그때 달라질 수 있는 코드를 보자. 예를 들어, source_strtarget_buf 에 복사하는데, 만약 문자가 소문자면 대문자로 바꿔준다고 하자. 그럼 아래와 같이 어렵지 않게 구현될 것이다.

int	is_lowercase(char ch)
{
	return ('a' <= ch && ch <= 'z');
}

void	strcpy_lower_to_upper(char *target_buf, const char *source_str)
{
	while (*source_str != '\0')
	{
		if (is_lowercase(*source_str))
			*target_buf++ = 'A' + (*source_str++ - 'a');
		else
			*target_buf++ = *source_str++;
	}
	*target_buf = '\0';
}

위의 while 문은 문자열 source_str 을 처음부터 끝까지 순차적으로 접근할 것이다. 그리고 매 상황(대문자인지 소문자인지) 에 따라 첫 번째 동작 이나 두 번째 동작을 수행한다. 이 때 각 문자는 직접 *source_str 값을 읽기 전까지는, 이전까지의 상황만으로 대문자일지 소문자일지 알 수 없다. 때문에 한 while 문 안에서, 상황에 따라 원하는 동작을 수행하도록 코드를 작성할 수밖에 없다.


조건에 따라 가변적 횟수 동안 특정 작업을 하는 반복 코드


이번 예시 역시 두 문자열 주소 source_strtarget_buf 가 있다. source_str 의 문자들을 하나씩 접근하면서, 만약 문자가 알파벳이었다면 알파벳이 아닐 때까지 문자를 target_buf 으로 복사해 온다. 그리고 복사해 온 단어 사이에는 space character 를 하나 추가한다.

int	is_alpha(char ch)
{
	return (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
}

void	cpy_all_word(char *target_buf, const char *source_str)
{
	while (*source_str != '\0')
		if (is_alpha(*source_str))
		{
			while (is_alpha(*source_str))
				*target_buf++ = *source_str++;
			*target_buf++ = ' ';
		}
	*target_buf = '\0';
}

제일 안쪽의 while 은 do while 을 사용하는게 더 효율적일 수 있겠지만 그 부분은 넘어가기로 하겠다. 더불어 문제에 집중하기 위해 마지막 단어 뒤에 ‘ ‘ 가 불필요하게 추가되는 것은 무시하도록 하겠다.

완성된 코드를 보면 복잡해보였던 설명에 비해 꽤나 간소하게 느껴진다. 2 중 while 문으로 구현되서 생각보다 짧은 코드로 구현이 되었다. 그런데 내가 오늘 말하고자 했던 핵심은 사실 바로 이 다음이다. 여기서 만약 안쪽의 while 부분을 따로 떼서 함수로 구현하고 싶다면 어떻게 될까? 문자열에서 알파벳이 아닌 문자가 나올 때까지, 연달은 알파벳을 다른 위치로 복사하는 기능은 함수로 구현해 놓는다면 어쩌면 다른 곳에서 또 쓰일지도 모른다. 그래서 한 단어를 복사하는 함수를 따로 작성해놓고, 우리의cpy_all_word() 가 그 함수를 호출해서 사용하는 것은 좋은 생각인 것 같다. 하지만 글이 길어지고 있으니 이번 포스트는 여기까지 하고 다음 포스트에 이어서 진행해 보도록 하겠다.


>> 다 음 에 이 어 서 <<