프로그래밍

종이 자르기

그림처럼 보이는 것도
결국 한 줄씩 찍는 것입니다.

학습 목표

  1. 여러 줄로 된 출력을 「줄의 반복」으로 바꾸어 설계할 수 있다.
  2. 반복되는 것과 한 번만 나오는 것을 구분할 수 있다.
  3. 같은 조각을 여러 번 이어 붙여 한 줄을 만드는 방법을 쓸 수 있다.
  4. 칸 수와 선 수가 하나 차이 난다는 것을 알고 그대로 구현할 수 있다.

무엇을 만드나

격자를 그리려면 줄을 하나씩 찍어야 합니다.

주어진 문제

큰 종이를 가로 W칸, 세로 H칸으로 자르려고 합니다. 자를 자리를 표시한 격자 모양을 화면에 그려 주는 프로그램을 만듭니다.

가로 4칸, 세로 3칸이면 이렇게

+---+---+---+---+
|   |   |   |   |
+---+---+---+---+
|   |   |   |   |
+---+---+---+---+
|   |   |   |   |
+---+---+---+---+

그림처럼 보이지만 화면은 위에서 아래로 한 줄씩 찍힐 뿐입니다. 그러니 이 문제는 「어떤 줄을 몇 번 찍을 것인가」로 바꿔 놓아야 풀립니다. 모양을 줄의 반복으로 바꾸고 나면 나머지는 그 줄을 찍는 일입니다.

단계 여기서 답할 질문
1 요구 분석 무엇을 입력받고 어떤 기호로 그릴 것인가
2 설계 몇 종류의 줄이 각각 몇 번 나오는가
3 구현 한 줄을 어떻게 만들고 반복을 어디서 끊을 것인가
4 테스트 칸이 하나뿐일 때도 모양이 맞는가

먼저 개발 문서 양식을 내려받으세요

읽기만 하지 말고 직접 채워 보세요. 단계마다 자기 답을 적어 두면 그 기록이 요구 사항 명세서이자 설계서가 됩니다. 답을 보기 전에 먼저 적어 봅니다.

요구 분석과 설계는 언어와 상관없습니다. 두 양식의 앞부분은 같고, 구현을 적는 자리만 언어에 맞춰 다릅니다. 수업에서 쓰는 언어에 맞는 것을 받으면 됩니다. 이 강의노트의 코드 예시는 파이썬으로 되어 있습니다.

1단계 · 무엇을 만들 것인가

모양을 말로 정확히 적어 두어야 합니다.

되물어야 할 것들

  • 한 칸의 크기는? 가로로 몇 글자, 세로로 몇 줄인가요
  • 어떤 기호를 쓰나요? 모서리와 가로선과 세로선을 각각 무엇으로
  • 0칸이 들어오면? 아무것도 안 그리나요
  • 얼마나 큰 값까지? 화면을 넘어가면 어떻게 하나요

정리한 요구 사항

  1. 가로 칸 수와 세로 칸 수를 한 줄에 공백으로 띄어 정수로 입력받는다.
  2. 모서리는 +, 가로선은 -, 세로선은 |로 그린다.
  3. 한 칸의 가로 폭은 세 글자, 세로 높이는 한 줄로 한다.
  4. 칸 안쪽은 공백 세 칸으로 비워 둔다.
  5. 격자의 맨 위와 맨 아래에도 경계선이 있어야 한다.
  6. 가로와 세로 모두 1 이상이 들어온다고 본다.

3번과 4번을 안 적으면 그림이 어긋납니다

가로선이 세 글자인데 칸 안쪽을 한 칸만 비우면 위아래 선이 서로 어긋나 격자로 보이지 않습니다. 눈으로 보면 바로 알지만 말로 적어 두지 않으면 틀리기 쉬운 종류의 요구 사항입니다. 「보기 좋게」 같은 말로는 이것을 못 정합니다.

스스로 확인

요구 사항 5번을 「칸마다 위에 경계선을 하나씩」이라고만 적으면 어디가 빠질까요? 가로 4칸, 세로 3칸 그림에서 세어 보세요.

2단계 · 두 종류의 줄이 번갈아 나온다

줄을 세어 보면 규칙이 보입니다.

토막 무엇을 정하는가
입력 가로 칸 수와 세로 칸 수, 정수 두 개
처리 두 종류의 줄을 만들어 정해진 횟수만큼 번갈아 찍는다
출력 여러 줄 — 경계선과 칸 줄이 번갈아 나온다

줄은 두 종류뿐이다

종류 어떻게 만드나 (가로 4칸일 때) 몇 번 나오나
경계선 +---를 가로 칸 수만큼 이어 붙이고 끝에 + 하나 세로 칸 수 + 1번
칸 줄 |와 공백 세 칸을 가로 칸 수만큼 이어 붙이고 끝에 | 하나 세로 칸 수만큼

두 줄 다 「같은 조각을 여러 번 + 마지막에 하나」라는 같은 모양입니다. 칸이 넷이면 조각이 넷이고 마감이 하나 — 울타리 기둥이 칸보다 하나 많은 것과 같습니다.

경계선이 하나 더 많습니다

세로 3칸이면 칸 줄은 3개인데 경계선은 4개입니다. 칸 사이마다 하나씩에 맨 위와 맨 아래까지 있기 때문입니다. 그래서 「경계선 → 칸 줄」을 세로 칸 수만큼 반복하면 마지막 경계선 하나가 모자랍니다. 반복 밖에서 한 번 더 찍어야 합니다.

푸는 순서

  1. 가로 칸 수와 세로 칸 수를 입력받는다.
  2. 세로 칸 수만큼 반복한다.
    • 경계선을 한 줄 찍는다.
    • 칸 줄을 한 줄 찍는다.
  3. 반복이 끝나면 경계선을 한 줄 더 찍는다.
스스로 확인

가로 4칸의 경계선에는 +가 몇 개 들어갈까요? 그리고 그 수가 4와 어떤 관계인지 말해 보세요.

3단계 · 파이썬으로 옮기기

같은 조각을 여러 번 이어 붙이는 방법이 있습니다.

먼저 스스로 적어 본 뒤에 코드를 보세요. 설계가 짧아 코드도 짧습니다.

종이.기본.py

# 가로 칸 수와 세로 칸 수를 입력받는다
w, h = map(int, input().split())

for _ in range(h):
    print("+---" * w + "+")  # 경계선
    print("|   " * w + "|")  # 칸 안쪽

# 마지막 경계선은 반복 밖에서 한 번 더 찍는다
print("+---" * w + "+")

문자열에 곱하기를 하면 반복됩니다

수끼리 곱하면 계산이 되지만 문자열에 정수를 곱하면 그만큼 이어 붙습니다. 그래서 조각 하나를 칸 수만큼 곱하고 끝에 마감을 더하면 한 줄이 완성됩니다. 반복문을 하나 더 쓰지 않아도 되는 것이 이 방법의 이점입니다.

반복이 도는 값을 쓰지 않는 경우

반복이 도는 숫자를 번호로 쓰는 경우가 많습니다. 여기서는 몇 번째인지가 필요 없습니다 — 그저 세로 칸 수만큼 반복하면 됩니다. 그래서 이름을 지어 주지 않고 밑줄 하나로 두었습니다. 「이 값은 안 쓴다」는 뜻을 읽는 사람에게 알려 주는 관례입니다.

스스로 확인

마지막 경계선을 찍는 줄을 반복 안으로 들여쓰면 가로 4칸 세로 3칸에서 몇 줄이 나올까요? 모양은 어떻게 될까요?

4단계 · 넣어 보고 확인하기

줄 수를 세어 보면 대부분의 실수가 잡힙니다.

모양이 나오는 프로그램은 눈으로 보면 맞아 보이기 쉽습니다. 줄 수와 글자 수를 세어 확인하는 편이 안전합니다.

넣은 값 나와야 할 줄 수 무엇을 보려는 값인가
4 3 7줄 경계선 4 + 칸 줄 3 — 기본 예시
1 1 3줄 가장 작은 격자 — 네모 하나
5 1 3줄 가로로만 긴 경우
1 5 11줄 세로로만 긴 경우
10 2 5줄, 한 줄에 41글자 글자 수까지 세어 보는 경우

결과가 어긋났다면 여기부터 봅니다

  • 맨 아래 선이 없다 — 반복 밖의 마지막 경계선이 빠졌습니다
  • 경계선이 두 줄씩 붙어 나온다 — 마지막 경계선이 반복 안에 들어갔습니다
  • 오른쪽 끝이 열려 있다 — 줄 끝의 마감 기호를 안 붙였습니다
  • 위아래 선이 어긋난다 — 칸 안쪽 공백이 세 칸이 아닙니다
  • 경계선 한 줄만 나온다 — 세로 칸 수가 0으로 들어갔거나 반복 범위가 틀렸습니다
스스로 확인

가로 10칸일 때 한 줄이 41글자인 이유를 설명해 보세요. 조각 하나가 몇 글자이고 마감이 몇 글자인가요?

더 해 보기

모양을 바꾸려면 무엇을 고쳐야 할까요?

칸 크기를 입력받는다

칸의 가로 폭을 3으로 고정한 것을 풀어 봅니다. 폭이 바뀌면 가로선과 칸 안쪽 공백이 함께 바뀌어야 합니다. 둘 중 하나만 고치면 격자가 어긋납니다.

칸에 번호를 넣는다

칸 안쪽을 비우는 대신 1부터 차례로 번호를 넣어 봅니다. 칸 줄을 곱하기 하나로 만들 수 없게 되므로 반복이 하나 더 필요해집니다.

0칸을 막는다

요구 사항 6번을 풀어 0이나 음수가 들어오면 알리도록 합니다. 이 프로그램은 0을 넣어도 멈추지 않습니다. 그래서 더 조심해야 합니다.

맨 위와 맨 아래 선만 다르게

맨 위와 맨 아래 경계선만 다른 기호로 그려 봅니다. 맨 아래 선은 이미 반복 밖에서 따로 찍고 있지만, 맨 위 선은 반복 안에서 찍히므로 첫 회차를 따로 구분해야 합니다.

스스로 확인

「0을 넣어도 멈추지 않는다」고 했습니다. 가로 0칸 세로 0칸을 넣으면 무엇이 나올까요? 그리고 그것이 왜 위험할까요?