ProgrammingInObjectiveC:Chapter 05

From 흡혈양파의 번역工房
Jump to navigation Jump to search
5장
프로그램 반복문

5장 :: 프로그램 반복문

Objective-C 에서 코드를 반복 실행하는 방법이 몇 가지 있다. 이 장의 주제인 '반복하기' 를 하는 방법은 다음과 같이 세 가지다.

  • for 문
  • while 문
  • do 문


숫자를 세는 간단한 예제부터 해보자.

구슬 열다섯 개를삼각형 모양으로 정렬하려면, 그림 5.1과 같은 모양으로 정렬할 수 있다.

그림 5.1 삼각형 정렬의 예


삼각형의 첫째 줄에 구슬이 하나, 둘째 줄에 둘, 이런 식으로 계속 정렬된다. 정리하자면 n개의 줄로 구성된 삼각형을 만들기 위해서 구슬이 정수 1 부터 n까지의 합만큼 필요하다. 이 합을 '삼각수(triangular number)' 라고부른다.

1 에서 시작하면 넷째 삼각수는 연속되는 정수 1부터 4까지의 합(1 + 2 + 3 + 4), 즉 10이 된다.

여덟 번째 삼각수의 값을 계산하여 터미널에 결과를표시해 주는 프로그램을 만든다고 해보자 물론 머릿속에서 계산하는 게 어렵지는 않지만, Objective-C 프로그램을 작성해서 이 일을 처리하고 싶다고 하자. 프로그램 5.1은 이 프로그램의 예제다.


프로그램 5.1


#import <Foundation/Foundation.h>

// 여덟 번째 삼각수를 계산하는 프로그램

int main (int argc, char * argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int triangularNumber;

    triangularNumber = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8;

    NSLog (@"The eighth triangular number is %i", triangularNumber);

    [pool drain];
    return 0;
}

프로그램 5.1 의 출력결과


The eighth triangular number is 36


프로그램 5.1 에서 사용한 방법으로 간단한 삼각수는 계산할 수 있지만, 200번째 삼각수를 찾고자 한다면 어떨까? 프로그램 5.1을수정해서 1 부터 200 까지 정수를 하나하나 더한다면 매우 지겨울 것이다. 다행히 쉬운 방법이 있다.

특정 명령문을 반복 수행하는 것은 컴퓨터에 있는 기본 기능이다. 이 반복 기능을 써서 프로그래머는 코드를 수천, 수백만줄 작성해서 수행할 작업을 매우 짧은코드로 수행할 수 있다. Objective-C 에는 프로그램을 반복하는 명령문이 세 가지있다.


for 반복문

이제 for 문을 사용하눈 프로그램을 살펴보자. 프로그램 5.2는 200번째 삼각수를 계산하는 프로그램이다. 다음 코드에서 for 문이 어떻게 동작하는지 이해해 보자.


프로그램 5.2


// 200번째 삼각수를 계산하는 프로그램
// for 문의 소개

#import <Foundation/Foundation.h>

int main (int argc, char *argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int n, triangularNumber;

    triangularNumber = 0;

    for ( n = 1; n <= 200; n = n + 1 )
        triangularNumber += n;

    NSLog (@"The 200th triangular number is %i", triangularNumber);

    [pool drain];
    return 0;
}

프로그램 5.2 의 출력결과


The 200th triangular number is 20100


프로그램 5.2에는 설명이 좀 필요하다.200번째 삼각수를 계산하는 방법은 8번째 삼각수를 계산할 때와 사실은 동일하다. 정수 1부터 200까지 다 더해 주자. 변수 triangularNumber는 for 문이 시작되기 전에 O 으로 설정된다. 보통, 모든변수는 프로그램에서 사용되기 전에 특정 값으로 초기화해 주어야 한다. 나중에 배우겠지만, 특정 형의 변수들은 기본 초기값을 갖는데, 여기에 의존하기보다는 직접 초기화 하는편이 좋다.

for 문에서는 명시적으로 정수 1부터 200을 작성해 줄 필요가 없다. 어떤 의미에서는 이 코드가 이 숫자를 직접 만들어 준다고도 볼 수 있다.

for 문은 일반적으로 다음과 같은 형태다.

for ( init_expression; loop_condition; loop_expression )
    program statement


괄호 안에는 표현식이 세 가지 있다. 바로 init_expression, loop_condition, loop_expression 인데, 이것들을 이용해서 프로그램의 반복문이 작동하는 환경을 설정한다. 바로 뒤이어 나오는 명령문이 반복문의 몸체를 구성한다. 여기에는 유효한(당연히 세미콜론으로 끝나는) Objective-C '프로그램 명령문'이면 무엇이든 사용할수 있다.

init_expression이라고 이름 불은 for 문의 첫 구성 요소는 반복문이 시작될 때 초기값을 설정해 준다. 프로그램 5.2에서 이 부분은 n 의 초기값을 1 로 설정히는 데 사용했다. 코드에서 볼수있듯 대입은 유효한 표현식이다. f 문의 두 번째 구성 요소로 반복문이 계속되는 조건을 지정한다. 다른 말로하면, 이 조건이 만족되는 동안에는 계속 반복이 진행된다. 프로그램 5.2를 다시 보면 for 문의 loop_condition 은 다음 비교 표현식이다.

n <= 200


이 표현식은 "n이 200보다 작거나 같다"라고 읽어도 된다. 이 '작거나 같다' 연산자(=)는 Objective-C 언어가 지원하는 몇몇 비교 연산자중 하나다. 이 연산자들은 조건을 테스트하는 용도로 쓴다. 만일 조건이 만족되면 테스트 결과는 참(혹은 TRUE)이고, 조건이 만족되지 못하면 결과는 거짓(혹은 FALSE)이다.

표 5.1 Objective-C 의 비교 연산자를 모두 나열했다.

연산자 의미 예제
== 같음 count == 10
!= 같지 않음 flag != DONE
< 보다 작음 a < b
<= 보다 작거나 같음 low <= high
> 보다 크다 points > POINT_MAX
>= 보다 크거나 같음 i >= 0
표 5.1 비교 연산자


이 비교 연산자들은 모든 산술 연산자보다 우선순위가 낮다. 다음 표현식을 보자.

a < b + c


이것은 다음과 같이 계산된다.

a < (b + c)


이 코드는 예상대로 동작한다. a가 b와 C를 더한 값보다 작다면 결과 값은 참(TRUE)이고, 그 외에는 거짓(FALSE)이 된다.

'같음' 연산자{==)에는 주의를 기울이자. 대입 연산자(=)와 헷갈리지 말아야 한다. 다음 표현식을 보자.

a == 2


이 식은 a의 값이 2와 같은지 비교하지만, 다음 표현식은 이와다르다.

a = 2


이 식을 실행하면 변수 a에숫자 2가 대입된다.

어떤 비교 연산자를 사용할지는 어떤 테스트인지에 달려 있다. 때로는 사용자의 선호에 따라 달라지기도 한다. 다음 비교 표현식을 보자.

n <= 200


이 식은 다음 표현식과 동일하다.

n < 201


앞 예제로 돌아가서 for 문의 몸체를 살펴보자. triangularNumber += n; 은 비교테스트의 값이 참인 동안, 혹은 n의 값이 200 이하인 동안에는 반복 실행된다. 이프로그램 명령문은 triangularNumber 의 값에 n의 값을 더한다.

loop_condition에 있는 조건이 맞지 않을 때가 되면, 프로그램의 실행은 for 문의 바로 다음 명령문으로 넘어간다. 이 프로그램에서 반복문이 종료되고나면 NSLog 로프로그램의 실행이 넘어간다.

for 문의 마지막 구성 요소는 반복문의 몸체가 실행되고 나서 계산된다. 프로그램 5.2에서 이 loop_expression은 n의 값에 1 을 더한다. 따라서 triangularNumber 의 값에 n의 값이 더해질 때마다 그 다음에 n의 값을 1 씩 증가시킨다. 이는 n이 201 이 될 때까지 수행된다.

n에 들어갈 수 있는 마지막 값인 201은 triangularNumber에 추가되지 않는다. 이것은 반복 조건이 충족되지 않는 순간, 혹은 n이 201 이 되는 순간에 반복문이 종료되기 때문이다.


요약하면 for 문은 다음 순서대로 실행된다.

  1. 초기화 표현식이 먼저 평가된다. 이 표현식은 반복문 내에 사용되는 변수를 0 과 1 같은 초기값으로)설정하는데,보통 '인덱스' 변수라고부른다.
  2. 반복 조건이 평가된다. 만일 조건이 충족되지 못하면(즉, 표현식이 거짓이면) 반복문은 즉시 종료된다. 실행은 반복문 다음 명령문으로 넘어간다.
  3. 반복문에서 몸체를 구성하는 명령문이 실행된다.
  4. 반복 표현식이 평가된다. 보통 이 표현식은 인덱스 변수에 1 을 더하거나 1 을 뺀다.
  5. 2단계로 돌아간다.


반복문에 들어가면 반복문의 몸체가 실행되기 전에 반복조건부터 평가된다는 사실을 기억하라. 또한, 반복문괄호 뒤에 세미콜론을 넣지 않도록 주의한다. 만일 반복문괄호 뒤에 세미콜론을 넣는다면 반복문이 바로 종료될 것이다.

프로그램 5.2는 첫 번째 삼각수부터 200번째 삼각수까지 모두 계산한다. 따라서 이 숫자들로 표를 만들 수 있다. 공간을 절약하기 위해 처음 10번째까지의 삼각수만 표로 만들어 보자. 프로그램 5.3은 이 작업을 수행한다.

프로그램 5.3에서 맨 앞에 있는 NSLog 세 개는 그저 일반적인 제목과 각 컬럼의 이름표를 불이는 데에만 사용했다.

적절한 제목을 달고 나서, 프로그램은 맨 앞부터 10개 까지의 삼각수를 계산한다. 변수 n을 사용하여 현재 숫자를 세고, 1 에서 n까지 이르는 합을 계산하여 n번째 삼각수의 값을 변수 triangularNumber에 저장한다.

for 문은 변수 n의 값을 1로 설정하는 것에서 실행되기 시작한다. 앞에서 언급했듯이, for 문바로다음에 나오는 명령문이 반복문의 몸체를구성한다. 그런데 반복 실행하고 싶은 프로그램 명령문이 한 줄이 아니라 여러 줄이라면 어떻게 해야할까? 그때는 반복 실행하기를 원하는 프로그램 명령문 전부를 중괄호({ })로 감싸면 된다. 시스템은 이 명령문 그룹 혹은 '블록'을 한 개체로 처리한다. 블록을 중괄호로 열고 닫기만 잘해준다면 Objective-C 프로그램내에 명령문을 허용하는 어느곳에서든 명령문 블록을 쓸 수 있다.


프로그램 5.3


#import <Foundation/Foundation.h>

int main (int argc, char *argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int n, triangularNumber;

    NSLog (@"TABLE OF TRIANGULAR NUMBERS");
    NSLog (@"n Sum from 1 to n");
    NSLog (@"-- --------");

    triangularNumber = 0;

    for ( n = 1; n <= 10; ++n ) {
        triangularNumber += n;
        NSLog (@"%i             %i", n, triangularNumber);
    }

    [pool drain];
    return 0;
}

프로그램 5.3 의 출력결과


TABLE OF TRIANGULAR NUMBERS
n   Sum from 1 to n
--  -----------------
1   1
2   3
3   6
4   10
5   15
6   21
7   28
8   36
9   45
10  55


따라서 프로그램 5.3에서는 triangularNumber에 n을 더하는 코드와 그 다음 NSLog 모두 반복문의 몸체다. 프로그램 명령문이 들여쓰기된 방식에 주의를 기울이자. 한눈에 어느 부분이 for 문인지 알아볼 수 있다. 프로그래머마다 코딩 스타일이 다른데, 어떤사람들은다음 방식으로반복문을작성하는 것을선호한다.

for ( n = 1; n <= 10; ++n )
{
    triangularNumber += n;
    NSLog (@"%i %i" , n, triangularNumber];
}


이 코드에서 여는 중괄호(()는 for 다음 줄에 자리 잡았다. 여는 중괄호가 어디에 있는지는 취향의 문제이며 프로그램 자체에는 아무런 영향을 끼치지 않는다.

이전 삼각수에 n의 값을 더하면 다음 삼각수가 간단히 계산된다. 맨 처음 for 문이 시작될 때, 이전 삼각수의 값은 0이므로 n의 값이 1 일 때, triangularNumber의 새 값은 n 값,즉 1 이다. 그후 포맷 스트링에 빈칸이 적절한 수만큼 추가되어 n과 triangularNumber의 값이 각 컬럼 제목 아래에 줄 맞춰 표시된다.

반복문의 몸체가 실행된 후 반복 표현식이 평가된다. 그런데 이 for 문 안의 표현식이 약간 이상해 보일 수 있다. n = n + 1 을 입력하려다가 실수로 다음처럼 웃기게 생긴 코드를 작성했다는 생각마저 들지 모른다.

++n


그러나 ++n 은 아무문제 없는 유효한 Objective-C 표현식이다. 이 표현식에서는 '증가 연산자'라는 새로운 연산자를 소개한다. ++ 기호, 즉 증가 연산자는 피연산자에 1 을 더해 준다. 프로그램에서 1 을 더하는 연산이 하도 자주 사용되기 때문에 이를 위해 특별한 연산자가 만들어진 것이다. ++n 은 n = n + 1 과 동일하다. 처음에는 n = n + 1 이 더 눈에 잘 들어오겠지만 익숙해지면 이런 간결한 표현이 있다는 것에 감사를 표하게 될 것이다. 그렇다면 1 을 더하는 연산자만 제공할까?

당연히 값에서 1 을 빼는 연산자도 있다. 쉽게 짐작할 수 있듯이 이 연산자의 이름은 '감소 연산자' 이고, 뺄셈 부호 두 개로 표시한다. 다음 Objective-C 표현식을 보자.

bean_counter = bean_counter - 1


감소 연산자를 써서 이 식을 작성해 보면 다음과 같다. 결과는 동일하다.

--bean_counter


n++ 나 bean_counter-- 같이 ++ 나 -- 를 변수 뒤에 쓰는 것을 선호하는 프로그래머도 있다. 이 방법도 사용 가능하며, 무엇을 사용할지는 개인이 선호하는 쪽을 선택하면 된다.

프로그램 5.3의 출력 결과를보자. 마지막줄이 위와 맞지 않는다. 이 작은 골칫거라는 프로그램 5.3에서 해당하는 NSLog 명령문을 다음 명령문으로 대체하면 고칠수있다.

NSLog ("%2i %i" , n, triangularNumber);


이렇게 코드를 변경하여 문제가 해결되었는지 알아보기 위해 수정한 프로그램(프로그램 5.3A)을 출력해 보자.



프로그램 5.3A 의 출력결과


TABLE OF TRIANGULAR NUMBERS
n   Sum from 1 to n
--  -----------------
1   1
2   3
3   6
4   10
5   15
6   21
7   28
8   36
9   45
10  55


NSLog 명령문에 필드너비 설명이 추가되었다. %2i 는 정수값을 특정 위치에 표시하고 싶다는 의미만이 아니라 최소 두 컬렴을 차지하여 표시하겠다는 뜻도 된다. 컬럼만 한 칸 차지하는 정수 값(0 ~ 9)은 빈칸 다음에 나타난다. 이런 방식을 '오른쪽정렬' 이라고한다.

따라서 %2i 로 필드 너비를 지정하면 n의 값을 표시하는 데 최소 두 컬럼이 사용되고 triangularNumber 의 값도 정렬되어 표시될 것이다.


키보드 입력

프로그램 5.2는 200번째 삼각수를 계산하는 일만 한다. 만일 50번째, 100번째 삼각수를 계산해야 한다면 어떻게 해야할까? 그럴 때는, for 문이 적절한 횟수만큼 반복하도록 프로그램을 수정해야할 것이다. 또한 NSLog 명령문도 수정하여 적절한 메시지를 표시하게 해야한다.

더 쉬운 방법은 사용자가 계산하고 싶은 삼각수가 무엇인지 프로그램이 물어보도록 하는 것이다. 답을 받고 나면, 프로그램은 원하는 삼각수를 계산할 수 있을것이다. scanf 라는 루틴을 사용하여 이 방법을 쓸 수 있다. scanf 는 개념 측면에서는 NSLog 와 유사하다. NSLog 루틴이 값을 표시하는 데 사용되는 반면, scanf 루틴은 프로그램에 값을 입력해 넣을수 있도록 한다. 물론 코코아 혹은 아이폰 애플리케이션 같은 GUI를 사용하는 Objective-C 프로그램에서는 NSLog 나 scanf 가 전혀 사용되지 않을 것이다.

프로그램 5.4는 사용자에게 어떤 삼각수를 계산할지 물어보고, 삼각수를 계산하고 결과를 표시한다.


프로그램 5.4


#import <Foundation/Foundation.h>

int main (int argc, char *argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int n, number, triangularNumber;

    NSLog (@"What triangular number do you want?");
    scanf ("%i", &number);

    triangularNumber = 0;

    for ( n = 1; n <= number; ++n )
        triangularNumber += n;

    NSLog (@"Triangular number %i is %i\n", number, triangularNumber);

    [pool drain];
    return 0;
}


출력 결과에서 사용자가 입력한 값(100)은 프로그램이 출력한 결과와 구분하기 위해 볼드체로 표시했다.



프로그램 5.4 의 출력결과


What triangular number do you want?
100
Triangular number 100 is 5050


출력 결과에 따르면, 사용자는 숫자 100을 입력하였다. 그 후, 프로그램은 100번째 삼각수를 계산하고 결과 값 5050을 터미널에 표시한다. 사용자가 원하는 삼각수가 무엇이든 쉽게 입력만 하면 결과값을 얻을수 있다.

프로그램 5.4 에서 첫 NSLog 명령문은 사용자에게 숫자를 입력해 달라고 요청한다. 물론,사용자에게 입력해 주길 원하는 값을 알려주는 편이 좋다. 이 메시지가 표시된 뒤 scanf 루틴이 호출된다. scanf 의 첫 번째 인수는 포맷 스트링인데 @으로 시작하지 않는다. NSLog의 첫 번째 인수가 언제나 NSString 인 것과 달리, scanf 는첫 인수가 C 스타일의 스트링이다. 이 책의 앞부분에서 언급했듯이 C 스타일 캐릭터는 @ 문자로 시작하지 않는다.

포맷 스트링은 scanf 가 어떤 종류의 값을 콘솔(터미널 프로그램으로 프로그램을 컴파일하는 경우라면 터미널 창)에서 입력받을 것인지 지정한다 NSLog의 경우와 마찬가지로, %i 문자는 정수값을 지정하는 데 쓴다.

scanf 루틴의 두 번째 인수는 사용자가 입력한 값이 어디에 저장될지 지정한다. 변수 number 앞에는 & 문자가 반드시 붙어야 한다. 일단 지금은 & 기능에 대해 궁금해하지 말자. 연산자의 일종인 이 문자는 13장 「하부 C 언어 기능」 에서 포인터를 다룰때 상세히 설명한다.

이제 프로그램 5.4에서 scanf 호출이 정수 값을 읽어 변수 number 에 저장함을 이해할수 있을 것이다. 이 값은 사용자가 계산하고싶은 특정 삼각수가 된다.

사용자가 이 숫자를 입력하고(입력이 끝났다는 의미로 엔터 키를누르면)프로그램은 요청된 삼각수의 값을 계산한다. 삼각수의 값은 프로그램 5.2와 동일한 방식으로 계산되는데, 오직 다른 점은 200 대신 number 를 한계 값으로 사용한다는 것이다.

원하는 삼각수를 계산하고 나서 결과가 표시된다. 그런 다음, 프로그램의 실행이 종료된다.


중첩 for 문

프로그램 5.4에서 사용자가 원하는 삼각수를 계산할 수 있도록하였다. 그러나 사용자가 삼각수 다섯개를 계산하고 싶다면 어떻게해야할까? 이럴때는사용자가 프로그램을 다섯번 실행해서 매번 원하는 삼각수를 입력해도 된다.

동일한 목적을 달성하면서도 Objective-C 학습 측면에서 훨씬 흥미로운 방법이 있다. 프로그램이 이 상황을 처리하도록 하는 것이다. 프로그램에 반복문을 추가하여 전체 계산을 다섯 번 반복하도록 한다. 이 반복문에는 for 문을 사용할 수 있다. 프로그램 5.5와 출력 결과에서 이 기법을 설명한다.


프로그램 5.5


#import <Foundation/Foundation.h>

int main (int argc, char *argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    int n, number, triangularNumber, counter;

    for ( counter = 1; counter <= 5; ++counter) {
        NSLog (@"What triangular number do you want?");
        scanf ("%i", &number);

        triangularNumber = 0;

        for ( n = 1; n <= number; ++n )
            triangularNumber += n;

        NSLog (@"Triangular number %i is %i", number, triangularNumber);
    }

    [pool drain];
    return 0;
}

프로그램 5.5 의 출력결과


What triangular number do you want?
12
Triangular number 12 is 78

What triangular number do you wnat?
25
Triangular number 25 is 325

Waht triangular number do you want?
50
Triangular number 50 is 1275

What triangular number do you want?
75
Triangular number 75 is 2850

What triangular number do you want?
83
Triangular number 83 is 3486


프로그램에는 for 문이 두단계로 들어가 있다. 다음은 바깥쪽 for 문이다.

for ( counter = 1; counter <= 5; ++counter )


이 코드는 프로그램 반복문을 정확히 다섯번 수행한다. counter 의 값은 처음에 1 로 설정되고, 5 와 같거나 작은 동안에만(다른 말로 하면, 6에 도달할 때까지) 1 씩 증가된다.

앞 예제들과 달리, 변수 counter 는 프로그램 내의 어디에서도 사용되지 않는다. 오로지 for 문의 반복횟수 계산용으로만 사용된다. 그러나이 역시 변수이기 때문에 프로그램 내에서 선언해 주어야만 한다.

남은 프로그램 코드가 이 프로그램 반복문에 다 들어가 있고, 이를 괄호로감쌌다. 이 프로그램이 동작하는 방식을 다음과 같이 개념적으로 설명하면더 쉽게 이해할수있을것이다.[1]

5번 반복한다.
{
    사용자에게서 숫자 입력을 받는다.
    요청받은 삼각수를 계산한다.
    결과를 표시한다.
}


반복문의 '요청받은 삼각수를 계산한다' 부분에서는 변수 triangularNumber 를 0 으로 설정하고, '삼각수를 계산하는 for 문'을 포함한다. 따라서 for 문이 실제로 다른 for 문 내에 들어가 있는 것이다. 이렇게 반복문을 중첩해서 쓰는 방식은 Objective-C 에서 아무 문제 없이 사용할 수 있고, 유효하다. 중첩하는 단계는 원하는 만큼 깊어질 수도 있다.

중첩된 for 문처럼 복잡한 프로그램 구조를 다룰 때는 들여쓰기의 중요성이 더욱 커진다. 들여쓰기를 해서 한눈에 어느 명령문이 어느 for 문에 속한지를 알 수 있다.


for 문 변형

for 문에 대한 논의를 끝내기 전에 이 반복문이 허용하는 문법 형태에 대해 조금 더 짚어보고 가자. for 문을 작성할 때, 반복문 시작점 에서 변수를 둘 이상 초기화 하고 싶을때도 있고, 반복문이 돌때마다 표현식을 둘 이상 실행시키고 싶을때도 있다. for 문에 있는 어느 필드에든 콤마(,)로 구분 지어 여러 표현식을 포함시킬수 있다. 다음 for 문을보자.

for ( i = 0, j = 0; i < 10; ++i )
...


i와 j의 값은 반복문이 시작되기 전에 0 으로 설정된다. i = O 과 j = O 는 콤마로 구분되지만 둘 다 반복문 안에 있는 init_expression 필드의 일부이다. 다른 예로, 다음 for 문을보자.

for ( i = 0, j = 100; i < 10; ++i, j -= 10 )
...


반복문의 시작 전에 변수 i와 j는 각각 0, 100 으로 초기화한다. 반복문의 몸체가 매번 실행되고나면, i 값은 1 씩 증가하고, j 값은 10씩감소한다.

for 문에 있는 특정 필드에 표현식을 둘 이상 넣고 싶을 때도 있겠지만, 때로는 필드를 생략하고 싶기도 할 것이다. 그때는 그저 필드를 생략하고 그곳에 바로 세미콜론을 넣어주면 된다. for 문 안에 있는 필드 중 초기값을 설정하는 부분이 가장 흔히 생략된다. 초기 표현식을 계산할 필요가 없을 때는 init_expression 필드를 그냥 빈칸으로 두고 세미콜론만 입력하면 된다.

for ( ; j != 100; ++j )
...


반복문이 시작되기 전에 이미 j의 값이 특정 초기값으로 설정되어 있다면 위 코드처럼 초기값을 설정하는 필드를 생략해도 된다.

loop_condition 이 생략된 for 문은 무한반복 된다. 이런 반복문은 반복문을 종료할 다른 방법 (return, break 혹은 goto 와 같은 명령문)이 있을 때 사용해야 한다.

또한 for 문의 초기 표현식에서 변수를 정의할 수도 있다. 앞에서 변수를 정의하는 일반적인 방식과 동일하다. 예를 들어, 다음처럼 for 문에서 정수 변수 counter 를 선언하고 동시에 값 1 을 설정해도 된다.

for ( int counter = 1; counter <= 5; ++counter )


변수 counter는 for 문이 실행되는 동안에만 사용할 수 있고 반복문 외부에서는 접근할수 없다(이런 변수를 '지역 변수'라고 부른다). 다른 예를보자. 다음 for 문은 정수 변수를 두개 정의하고 각각 값을 설정한다.

for ( int n = 1, triangularNumber = 0; n <= 200; ++n )
    triangularNumber += n;


마지막으로 볼 for 문의 변형은 '빠른 열거 (fast enumeration)'로, 객체 컬렉션에서 사용한다. 이것은 15장 「숫자,스트링, 컬렉션」에서 다룬다.


while 문

while 문은 Objective-C 가 지닌 반복 기능을 확장한다. 이 문법은 보통 다음과 같이 작성한다.

while ( expression )
    program statement


괄호 안에 있는 expression 을 평가해서 참(TRUE)이면 바로 따라오는 program statement 가 실행된다. 이 명령문(중괄호로 둘러싸여 있다면 명령문들)이 실행된 다음, 다시 expression 이 평가된다. 이 과정은 expression 의 값이 거짓(FALSE)으로 평가될 때까지 되풀이된다. 그 후 프로그램의 실행은 program statement 다음 명령문으로 이어진다.

어떻게 사용하는지 예를 살펴보자. 다음 프로그램은 while 반복문을 사용하여 1 에서 5 까지 센다.


프로그램 5.6


// while 문을 소개하는 프로그램

#import <Foundation/Foundation.h>

#import <stdio.h>

int main (int argc, char *argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int count = 1;

    while ( count <= 5 ) {
        NSLog (@"%i", count);
        ++count;
    }

    [pool drain];

    return 0;
}

프로그램 5.6 의 출력결과


1
2
3
4
5


프로그램은 count 의 초기값으로 1을 설정한다. 그 후 while 문이 수행된다. count 의 값이 5 보다 작거나 5 와 같기 때문에, 바로 while 문의 몸체가 실행된다. 중괄호로 감싼 NSLog 문과 count 의 값을 증가시키는 명령문 모두 while 문의 몸체다. 프로그램의 출력 결과를 보면, 이 반복문은 count 의 값이 5 가 될 때까지 다섯번 수행되었다.

이미 눈치챘겠지만 동일한 기능을 for 문으로도 구현할 수 있다. 사실 for 문과 while 문은 언제든 서로 동등하게 바꿔 써도 된다. 그 예로, 다음 for 문을 보자.

for ( init_expression; loop_condition; loop_expression )
    program statement


이것은 다음과 같이 while 문으로 동둥하게 표현할수 있다.

init_expression
while ( loop_condition )
{
    program statement
    loop_expression;
}


일단 while 문에 익숙해지고 나면, for 문이 적합할 때와 while 문이 적합할 때를 구분할 수 있다. 정해진 횟수만큼 반복하는 상황에는 대개 for 문이 적합하다. 또한 초기 표현식, 반복 표현식, 반복 조건이 모두 동일한 변수를 사용한다면 for 문이 더 나은선택일 것이다.

while 문을 사용하는 다음 프로그램은 두 정수의 최대공약수(gcd)를 계산한다. 두 정수의 최대공약수는 두 정수를 모두 나눌 수 있는 가장 큰 정수다. 예를 들어 10 과 15 의 최대공약수는 5다.

두 정수의 최대공약수를 계산하는 절차 혹은 알고리즘은 기원전 300년경에 유클리드가 만든 절차를 바탕으로 한다. 다음과 같이 표현할 수 있다.


문 제:양의정수 u 와 v 의 최대공약수를 찾는다.

단계 1: 만일 v가 0이라면, 최대공약수는 U다.

단계 2: temp = u % v 와 u = v, v = temp 를 계산한 다음,단계 1로 돌아간다.


이 알고리즘이 어떻게 동작하는지에 따른 상세한 사항은 신경 쓰지 말고 일단 믿고 사용하자. 지금은 알고리즘을 분석하기보다 최대공약수를 찾는 프로그램을 개발하는 일이 더 중요하다.

최대공약수를 찾는 문제에 대한 해결책의 알고리즘을 표현하고 나면 프로그램을 개발하는 작업이 훨씬 쉬워진다. 알고리즘의 단계를 분석해 보면 v 값이 0 이 될때까지 단계 2가 계속 반복됨을 알 수 있다. 이 사실을 알고 나면 자연스레 while 문을 사용하여 Objective-C 프로그램을 구현하게 된다.

프로그램 5.7은 사용자가 입력한 두개의 양의 정수에서 최대공약수를 찾는다.


프로그램 5.7


// 두 양의 정수의 최대공약수를 찾는 프로그램

#import <Foundation/Foundation.h>

int main (int argc, char *argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    unsigned int u, v, temp;

    NSLog (@"Please type in two nonnegative integers.");
    scanf ("%u%u", &u, &v);

    while ( v != 0 ) {
        temp = u % v;
        u = v;
        v = temp;
    }

    NSLog (@"Their greatest common divisor is %u", u);

    [pool drain];

    return 0;
}

프로그램 5.7 의 출력결과


Please type in two nonnegative integers.
150 35
Their greatest common divisor is 5

프로그램 5.7 의 출력결과(재실행)


Please type in two nonnegative integers.
1026 540
Their greatest common divisor is 54


두 정수 값이 입력되어 변수 u, v 에 저장된 후, 프로그램은 while 문에 들어가 최대공약수를 계산한다(부호 없는 정수 값을 읽으려면 %u 포맷 문자를 사용한다). while 문이 종료된다음, 원래 u 와 v 의 최대공약수인 u 의 값이 적절한 메시지와 함께 표시된다.

7장 「클래스에 대해서」 에서 분수를 다룰 때 최대공약수를 찾는 알고리즘을 다시 사용할 것이다.

이번에는 while 문을 사용하여 터미널에서 입력받은 정수를 거꾸로 표시하는 프로그램을 생각해 보자. 예를 들어, 사용자가 1234를 입력하면 프로그램은 이 숫자를 뒤집어 4321 을 표시한다.


objc2_notice_01
NSLog 를 사용하면, 각 자릿수가 매번 새로운 줄에 출력될 것이다. print 함수에 익숙한 C 프로그래머는 숫자를 연속해서 표시하는 데 이 함수를 사용하는 편이 좋겠다.


이런 프로그램을 작성하려면 먼저 앞서 말한 작업을 수행할 알고리즘을 고안해야 한다. 숫자자리를 반대로 뒤집는 작업은 간단히 '오른쪽에서 왼쪽으로 연속해서 각 자릿수를 읽는다' 로 표현할 수 있다. 그러면 맨 오른쪽부터 자릿수를 계속 추출해 내는 처리 절차를 만들어 내어 프로그램이 각 자리에 있는수를 읽게 하면 된다. 추출된 수는 터미널에 반대 순서로 연속 표시된다.

정수의 맨 오른쪽 자리 수는 10으로 나눈 나머지로 뽑아낼 수 있다. 예를 들어 1234 % 10 의 값은 4 이다. 이것은 1234 에서 맨 오른쪽자리의 수이고, 뒤집힌 수 에서 첫째(가장 왼쪽) 자리에 올 숫자다. 다음 자리의 수도 정수를 10 으로 나눈 다음 (정수의 나눗셈이 어떻게 동작하는지 기억해 보자), 동일한 방식으로 처리하면 된다. 따라서 1234/10 의 결과는 123 이 되고 123 % 10 의 결과인 3이 뒤집힌 수의 다음자리에 있게 된다.

마지막 자리에 놓일 수를 추출할 때까지 이 절차를 되풀이한다. 보통 10 으로 나눈 정수 나눗셈의 값이 0이 되면, 나머지 연산의 결과가 마지막 자리에 들어갈 수 이다.

프로그램 5.8은 사용자에게 숫자를 입력해 달라고 요청한 후, 숫자를 오른쪽에서 왼쪽으로 자릿수를 읽어 변환한 결과를 표시한다.


프로그램 5.8


// 자릿수를 뒤집는 프로그램

#import <Foundation/Foundation.h>

int main (int argc, char *argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int number, right_digit;

    NSLog (@"Enter your number.");
    scanf ("%i", &number);

    while ( number != 0 ) {
        right_digit - number % 10;
        NSLog (@"%i", right_digit);
        number /= 10;
    }

    [pool drain];
    return 0;
}

프로그램 5.8 의 출력결과


Enter your number.
13579
9
7
5
3
1


do 반복분

지금껏 논의했던 두 가지 반복문 구조는 모두 반복문 실행 전에 조건부터 확인한다. 따라서 조건이 만족되지 않으면 반복문의 몸체가 단 한 번도 실행되지 않을 수 있다. 프로그램을 개발하다 보면, 반복문의 맨 마지막에서 반복 조건을 확인해야 할 때가 있다. Objective-C 는 이런 상황에 쓸 수 있도록 do 구문을 제공한다. do 구문은 다음과 같은 형태다.

do
    program statement
while ( expression );


do 문은 다음과 같이 실행된다. 먼저 program statement 가 실행된다. 그 후 괄호안의 expression 이 평가된다. 평가 결과, 표현식이 참이면 반복되어 program statement 가 한 번 더 실행된다. 표현식이 참인 동안에는 program statement 가 계속 반복된다. 표현식을 평가한 결과가 거짓이면, 반복문이 종료되고 프로그램은 반복문 다음을 이어서 실행한다.

do 구문은 while 문의 순서를 바꾸어 놓고, 반복 조건이 반복문의 앞이 아니라 뒤에서 평가되도록 한 것이다.

프로그램 5 8 에서는 while 문을 사용하여 숫자자리를 반대로 변경하였다. 이 프로그램으로 돌아가서 만일 사용자가 13579 대신 O 을 입력하면 어떻게 되는지 시도해 보자 while 문의 몸체는 한 번도 실행되지 않으므로 결과로 아무것도 표시되지 않을 것이다. 여기서 while 대신 do 구문을 사용한다면, 반복문의 몸체가 적어도 한 번은 실행되어 한자릿수가 입력 되더라도 결과가 표시된다. 프로그램 5.9 는 이와 같은 do 문을 사용하는 법을 보여 준다.


프로그램 5.9


// 자릿수를 뒤집는 프로그램

#import <Foundation/Foundation.h>


int main (int argc, char *argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    int number, right_digit;

    NSLog (@"Enter your number.");
    scanf ("%i", &number);

    do {
        right_digit = number % 10;
        NSLog (@"%i", right_digit);
        number /= 10;
    }
    while ( number != 0);

    [pool drain];
    return 0;
}

프로그램 5.9 의 출력결과


Enter your number.
135
5
3
1

프로그램 5.9 의 출력결과(재실행)


Enter your number.
0
0


프로그램을 출력해 본 결과에서 알 수 있듯이 0 이 입력되더라도 프로그램은 정상적으로 0 을 표시해준다.


break 문

반복문이 수행되고 있을 때, 특정 조건에 따라 반복문을 빠져나오게 만들어야 할 때가 있다. 예를 들어, 오류가 발생하거나 예상보다 빨리 데이터의 마지막 지점에 도달했을 때이다. 이때 break 문을 사용하면 된다. break 문은 프로그램이 어떤 반복문(for, while, do) 을 실행하든 그에 상관없이 즉시 설행에서 빠져 나오도록 해준다. 반복문 내에 있는 다른 명령문은 건너 뛰고 바로 반복문이 종료된다. 프로그램의 실행은 반복문 다음에 있는 명령문으로 넘어간다.

만일 break 문이 중첩된 반복문에서 실행되었다면, break를 갖고 있는 가장 내부 반복문만 종료된다.

break 문은 다음과 같이 키워드 break에 세미콜론만 추가해 실행시킨다.

break;


continue 문

continue 문은 break 문과 유사하지만, 반복문을 종료하지는 않는다. continue 문이 실행되면 그 다음에 있는 모든 명령문을 건너뛰고 반복문의 맨앞으로 되돌아 간다. 그 후 다시 반복문을 평상시와 마찬가지로 정상 실행한다.

continue 문은 특정 조건에 따라 반복문 내의 명령문 그룹을 건너 뛰려 하지만 반복문을 종료하고 싶지는 않을 때 주로 사용한다. continue 문은 다음처럼 쓴다.

continue;


프로그램 반복문을 작성하고 적시에 종료하는 일에 매우 능숙해지기 전까지는 break 나 continue 를 사용하지 말자. 이 구문들은 오남용되기 쉽고, 잘못하면 프로그램의 흐름을 따라가기 어렵게 한다.


요약

이제 Objective-C 언어의 기본적인 반복 구조를 모두 배웠다. 앞으로는 프로그램이 실행되는 동안 의사결정 하도록 해주는 구문들을 배울 것이다. 다음 장은 이 의사결정에 대해 좀 더 상세히 다룬다.


연습문제

1. 정수 n 이 1과 10 사이에 있을 때 n과 n(2제곱)을 표시하는 표를 생성하는 프로그램을 작성하라. 적절한 컬럼 제목을 달아보라.


2. 삼각수는 정수값 n 을 다음 식으로 생성할수 있다.

triangularNumber = n (n + 1) / 2

예를 들어 10번째 삼각수인 55는 이 식에서 n 에 10 을 대입하여 계산해 낸다. 이 식을 사용하여 삼각수 표를 생성하는 프로그램을 작성하라. 5와 50사이에서 다섯 번째 숫자마다 삼각수를 생성하라(5, 10, 15, ..., 50).


3. n!로 표현되는 정수 n의 팩토리얼은 1부터 n까지 숫자를 전부 곱한 결과다. 예컨대, 5 팩토리얼은 다음과 같이 계산한다.

5! = 5 x 4 x 3 x 2 x 1 = 120

1 팩토리얼에서 10 팩토리얼까지 계산하고 표에 결과를 표시하는 프로그램을 작성하라.


4. 필드폭 지정자앞에 뺄셈 부호(-)를 붙이면 해당 필드가 왼쪽으로 정렬된다. 프로그램 5.3 에서 다음 NSLog 문에 대응하는 부분을 대체하여 실행시킨 다음, 두 출력 결과의 차이점을 비교해 보라.

NSLog (@"%-2i %i", n, triangularNumber);


5. 프로그램 5.5 에서는 사용자가 숫자를 다섯 개 입력해야 한다. 이 프로그램을 수정하여, 사용자가 계산할 삼각수의 개수를 입력할수 있도록하라.


6. 프로그램 5.2 부터 5.5 까지 for 문을 동일한 결과를 내는 while 문으로 수정하라. 각 프로그램을 실행하여 두 버전이 동일한지 확인하라.


7. 프로그램 5.8에 음수를 입력하면 어떤 일이 발생하는가? 시도해 보고 결과를 관찰하라.


8. 정수의 각 자릿수를 더한 프로그램을 작성하라. 예를 들어 2155 의 각 자릿수 합은 2 + 1 + 5 + 5, 즉 13이다. 이 프로그램은 사용자가 아무 정수나 입력할 수 있도록 해야한다.


Notes

  1. (옮긴이) 이와 같이 글로 풀어 적은코드를 의사(psudo)코드라고 한다. 영어의 경우 실제 프로그램을 작성하는데 더 크게 도움이 되지만, 일단 한글로 작성해도 개념을 잡기에는 매우 유용하다.