<스터디 시작에 앞서>
coalla 스터디의 모든 과정은 python 환경에서 이루어집니다.
일부 개념에 대한 설명이 다른 개발 환경과 차이가 있을 수 있습니다.
백준 채점서버 파이썬 버전: python 3.11.5 (2024.08.04. 기준)
1. 기본 입출력
PS(problem solving)는 주어진 조건에 맞춰 작업을 수행하는 프로그램을 만들어 프로그램이 입력을 받고, 처리하고, 출력을 해내는지를 두고 경쟁하는 분야입니다. 프로그램이 할 일은 순서대로 "입력-처리-출력"입니다. 본격적인 알고리즘의 영역인 "처리"단계를 다루기 전에 전처리인 "입력"과 결과를 뱉어내는 "출력"을 올바르게 수행하는 연습을 해봅시다.
백준 온라인 저지의 입출력은 표준 입출력(standard input/output)에서 이루어집니다. 그러니까, 프로그램 내에서 입력은 반드시 표준 입력(stdin)으로 받고, 출력은 반드시 표준 출력(stdout)으로 나가야 합니다. 파이썬에서 표준 입력과 표준 출력은 각각 input(), print() 으로 수행합니다.
1) 출력
어떤 프로그래밍 언어를 배울 때 가장 먼저 작성하는 프로그램이 "Hello World!"입니다. 파이썬에서는 print() 함수로 표준 출력을 할 수 있는데, "Hello, World"는 문자들의 집합인 문자열이므로 반드시 따옴표로 묶어주어야 합니다. 파이썬에서 따옴표로 묶지 않은 문자들은 모두 변수로 봅니다.
print("Hello, World!")
# Hello, World!
하지만 대부분의 문제가 한 번의 출력으로 끝나지 않습니다. print() 함수로 표준 출력을 실행하면 한 줄에 걸쳐 출력이 이루어집니다. 한 번 더 print() 함수를 호출하게 되면 개행하고 새로운 줄에 출력을 합니다. 따라서 여러 줄에 걸쳐 출력을 해야하는 경우, print() 함수를 여러 번 호출하는 방법을 사용할 수 있습니다. print() 함수 안에서 엔터키로 개행하면 어떻게 될까요?
print("한 줄"
"두 줄")
# 한 줄두 줄
제가 사용하는 pycharm에서는 위와 같이 따옴표를 분리시켜줍니다. 하지만 실행 결과는 개행되지 않은 채로 나타납니다. 이유는 당연한 말이지만 "개행" 과정이 없었기 때문입니다. 우리가 자주 사용하는 문서 편집기에서는 엔터키로 개행을 할 수 있습니다. 개행되는 줄의 끝을 보면 아무 것도 없는 것처럼 보이지만 실은 개행문자가 숨겨져 있습니다. 파이썬에서 개행문자는 "\n" 입니다.
print("한 줄\n두 줄")
# 한 줄
# 두 줄
이제 정상적으로 개행이 이루어지는 것을 확인할 수 있습니다. 이렇게 여러 줄의 출력을 처리할 수 있습니다.
다음 코드도 실행해보고 차이를 비교해봅시다.
print("abcdef")
print("abc","def")
print("ab","cd","ef")
print("abc"+"def")
print("ab"+"cd"+"ef")
2) 입력
다양한 문제를 실행할 때 가장 우선해야 하는 것은 입력입니다. 작업 순서 상 가장 먼저 일어나는 일이기 때문이죠. 입력이 주어지는 형태는 문제마다 제각각입니다. 우선 가장 쉬운 입력 처리부터 해봅시다. 파이썬의 표준 입력은 input() 함수로 수행합니다. 입력 받은 데이터는 메모리를 할당해 저장해야 나중에 접근할 수 있습니다. 그래서 입력은 메모리 할당, 변수 선언, 저장과 동시에 이루어집니다. 뭔가 복잡한 이야기 같지만, 코드를 보면 그리 어렵지 않습니다. 다음 코드는 변수 명 a로 할당된 메모리에 표준 입력을 저장하는 프로그램입니다.
a = input()
따옴표 없이 등장한 문자 a는 파이썬에서 메모리를 저장하는 "변수"입니다. 이렇게 새로운 변수 이름을 정해 써주면 변수가 선언되고 이 변수를 위한 메모리가 할당됩니다. input() 함수가 받은 데이터를 등호로 a와 연결해 곧바로 a에 입력받은 데이터를 저장합니다. 그럼 입력이 제대로 되고 있는지 이렇게 확인해볼까요?
a = input()
>>> 안녕하세요.
print(a)
# 안녕하세요.
실행하고 콘솔 창에 무엇이든 입력해주면 그대로 출력되는 것을 확인할 수 있습니다. 이제 표준 입력으로 데이터를 저장하는 것에 성공했습니다. 이제 문제를 하나 해결해봅시다.
지금까지 입력받은 값은 숫자가 아닌 문자였습니다. 수의 기능이 여러 가지인 것을 생각해보면 12라는 수를 양을 나타내는 수로 볼 것인지, 번호를 나타내는 것으로 볼 것인지에 따라 처리 방법도 달라집니다. 12에 34를 더한다는 것이 46을 말하는 것인지, 1234를 말하는 것인지 상황에 따라 다릅니다.
a = 12
b = 34
print(a+b)
# 46
a = input()
>>> 12
b = input()
>>> 34
print(a+b)
# 1234
위 코드를 실행해봅시다. 3째 줄의 print() 함수는 숫자 12와 34를 더한 46을 출력합니다. 이어서 a와 b에 다시 표준 입력을 받습니다. 여기서 각각 12와 34를 입력해주면 결과는 46이 아닌 1234가 나옵니다. 이렇게 되는 까닭은 표준 입력의 기본 처리 방법이 "숫자"가 아닌 "문자"이기 때문입니다. 표준 입력을 숫자로 처리하는 방법은 int() 또는 float() 함수를 사용하는 것입니다.
a = int(input())
>>> 12
b = int(input())
>>> 34
print(a+b)
# 46
이번에는 정상적으로 12+34가 46으로 나오는 것을 확인할 수 있습니다.
어떤 문제에서는 여러 번 입력을 주기도 합니다. print() 함수와 마찬가지로 여러 줄에 걸친 입력은 input() 함수를 여러 번 호출해 처리할 수 있습니다. 하지만 한 줄에 여러 개의 입력을 받는 경우는 조금 까다롭습니다. 이런 경우, 공백으로 나누어진 여러 문자 또는 숫자를 입력받습니다. 이때는 특정 문자를 기준으로 문자를 분리시켜주는 split() 함수를 사용합니다. 다음은 공백으로 구분 된 두 낱말을 한 줄로 입력받는 코드입니다.
a, b = input().split()
>>> Hello World
print(a)
# Hello
print(b)
# World
a와 b에 각각 "Hello", "World"가 저장된 것을 알 수 있습니다. 만약 어떤 문제가 쉼표로 구분된 한 줄 입력을 준다면 split(',')과 같이 split 함수 내에 분리시킬 기준이 되는 문자를 넣어주면 됩니다. 그럼 여러 개의 숫자를 받는 입력은 어떻게 할까요? 이건 조금 더 까다롭습니다. 여러 자료의 형식을 동시에 지정해줄 때는 map() 함수를 사용합니다. 다음은 공백으로 구분 된 세 정수를 한 줄로 입력받는 코드입니다.
a, b, c = map(int,input().split())
>>> 1 2 3
print(a)
# 1
print(b)
# 2
print(c)
# 3
map() 함수는 기본적으로 두 개의 자리가 있습니다.
map("바꾸려는 자료형", "바꾸려는 값")
정수 형태의 값으로 바꾸고 싶다면 앞쪽 칸에 int를, 실수 형태의 값으로 바꾸고 싶다면 앞쪽 칸에 float를 넣고 뒤쪽 칸에는 바꾸려는 값들이 들어갑니다. 지금 우리는 받은 입력을 split() 함수로 쪼개놓았으니 여러 개의 데이터가 들어가는 셈입니다.
2. 산술 연산자
산술 연산자는 입력-처리-출력의 과정에서 처리의 기본이 됩니다. 우리가 아는 가감승제 연산이 모두 사용되고 여기에 더불어 나머지를 구하는 모듈러 연산이 있습니다. 각 연산의 기호는 다음과 같습니다.
덧셈 | 뺄셈 | 곱셈 | 나눗셈(몫) | 나눗셈(실수) | 나눗셈(나머지) |
+ | - | * | // | / | % |
이 연산자들이 산술에서만 사용되는 것은 아니지만 지금은 기본적인 산술 연산 기능만 알아보겠습니다. 덧셈, 뺄셈, 곱셈은 특별한 유의점이 없지만 세 가지 나눗셈은 주의할 점이 있습니다. 우선 당연히 0으로 나누어서는 안됩니다. 0으로 나누기를 시도한다면 파이썬 인터프리터가 곧바로 코드에서 문제를 발견하고 ZeroDivision 에러를 발생시킵니다. 그리고 "//" 연산의 결과는 소수점이 없는 "정수형(int)"이고, "/" 연산의 결과는 소수점이 있는 "실수형(float)"이라는 것입니다. 출력 형식을 엄격하게 보는 문제에서는 이 소수점 만으로도 틀렸다고 하기도 합니다. 잠시후 자료형에서 자세히 다루겠습니다.
#예제7) 백준 27331 2桁の整数(Two-digit Integer)
산술 연산자는 다음 방법으로 간단하게 수행할 수도 있습니다.
a = 0
# 덧셈
a += 3
print(a)
# 3
# 뺄셈
a -= 1
print(a)
# 2
# 곱셈
a *= 3
print(a)
# 6
# 나눗셈
a //= 2
print(a)
# 3
# 나머지
a %= 2
print(a)
# 1
3. 기본 자료형
데이터는 형식을 지정해 관리합니다. 형식이 지정되지 않으면 프로그램이 데이터를 다룰 때 어떤 작업이 가능한지, 불가능한지 판단할 수 없게 되고 작업 도중에 치명적인 오류가 발생할 수도 있습니다. 따라서 파이썬 인터프리터는 자료형을 먼저 확인해 수행할 수 없는 작업 명령에 대해 에러를 발생시킵니다. 지금까지 우리가 다뤄본 자료형은 이렇습니다.
- 문자열(str)
- 정수형(int)
- 실수형(float)
다른 프로그래밍 언어에서는 문자와 문자열을 구분하지만 파이썬은 모두 같은 자료형으로 봅니다. 자료형을 지정해주기 위해서는 앞서 등장했던 것과 마찬가지로 str(), int(), float() 등의 함수를 사용합니다. 여기서 새로운 자료형이 하나 더 추가됩니다. 바로 논리 값을 다루는 논리형 자료 bool(boolean) 입니다.
1) 문자열(string)
문자열은 말 그대로 문자의 나열입니다. 경우에 따라 문장이 될 수도, 낱말이 될 수도, 글자가 될 수도 있습니다. 컴퓨터는 글자에 담긴 의미를 생각하지 않기 때문에 파이썬은 세 경우를 모두 같은 문자열로 처리합니다. 문자의 나열이기 때문에 일종의 "배열"로 관리됩니다. 배열이기 때문에 각 글자의 번호도 있습니다. 예를 들어 "Hello, World!"라는 문자열이 있다면 5번 문자는 ","가 됩니다. 배열의 특성과 관련된 문자열 처리는 선형 자료 구조를 다루며 함께 알아보겠습니다.
문자열에는 산술 연산자를 붙일 수 있습니다. "+" 연산자를 이용하면 문자열을 이어 붙일 수 있습니다.
a = "Hello"
a += ", World!"
print(a)
# Hello, World!
이런 것도 가능합니다.
a = "He"
a += "l"*2+"o, World!"
print(a)
# Hello, World!
"l"이라는 문자열에 2를 곱해 "ll"로 늘렸습니다. 다른 언어에서는 꿈도 못 꿀 문자열 처리 방법입니다. 대신 속도가 느리다는 점은 감안해야 합니다.
2) 정수형(integer), 실수형(float)
정수형 자료는 소수점이 없는 음수, 0, 양수입니다. 우리는 숫자를 보고 수식을 보면 당연히 계산을 수행하지만 컴퓨터는 숫자를 봐도 자료형이 무엇인지 확인하고 움직입니다. 만약 문자열인 숫자라면 그 숫자는 계산이 아니라 문자열 연산을 해야 합니다. 따라서 프로그램 안에서 어떤 계산을 하든 표준 입력이 변환 되었는지 꼭 확인해야 합니다
실수형 자료, 정확히는 부동소수점 형식은 소수점이 있는 숫자를 저장하기 위한 데이터 형식입니다. 컴퓨터의 이진법 연산 특성상 정확한 소수점 연산은 불가능하기 때문에 float 형식으로 데이터를 처리하게 되는 순간, 작은 오차를 떠안게 됩니다. 하지만 작은 수의 연산에서는 무시할 수준이므로 이 정도의 오차는 온라인 저지에서도 감안해주고 있습니다.
파이썬에서 정수형 자료는 최소 28byte, 실수형 자료는 24byte의 메모리를 할당 받습니다. 어째서 더 복잡한 실수형의 메모리가 더 작은지 의문일 수 있습니다. 실수형 자료의 최소 할당 메모리가 더 적은 까닭은 오차를 감수하기 때문입니다. 큰 값을 float 형식으로 저장하게 되면 어마어마한 오차가 발생합니다. 오차의 크기는 딱 24byte를 넘어서는 만큼이겠죠? float 형식은 24byte에서 더 이상 늘어나지 않지만 파이썬에서의 int 형식은 크기에 따라 유연하게 메모리 할당량을 늘려갈 수 있습니다. 메모리만 충분하다면 정수형 자료에서 사실상 표현 가능한 숫자의 크기 제한은 없다고 봐도 무방합니다. 파이썬을 제외한 거의 모든 언어에서 int 형식의 크기는 4byte에 -21억~21억 정도의 제한을 갖고 있습니다.
3) 나눗셈 연산과 자료형의 변환
1 나누기 3과 같은 연산의 결과를 정수 범위에서만 수행한다면 몫이 0, 나머지를 1로 끝낼 수 있지만 유리수 범위로 확장하게 되면 0.333333...과 같이 정확한 값을 표현할 수 없게 됩니다. (파이썬의 fraction 모듈을 사용하면 두 정수의 분수 꼴로 관리해 매우 높은 정확도를 유지할 수 있긴 합니다.) 그래서 세 가지 나눗셈 연산 중, "/" 연산은 연산 결과를 오차가 발생하는 float 형식으로 표현합니다. 4/2 와 같이 나누어떨어지는 경우라도 연산 결과를 반드시 float 형식으로 반환하기 때문에 출력 형식이 정해진 문제에서는 주의 깊게 살펴봐야 하는 부분입니다.
print(4/2)
# 2.0
print(0.09 + 0.01)
# 0.09999999999999999
어떤 문제에서는 이런 float 형식의 결과를 정해진 형식(소수점 아래 2자리까지 표기, 오차 범위 10-6 내에 표기 등)에 따라 출력해야 합니다. 문자열을 다루는 함수 중 하나인 format() 함수를 활용하는 방법이 있습니다. 이 함수는 활용 방법이 다양해 필요에 따라 찾아보고 소수점 아래 몇 자리까지 표기하는 포매팅을 연습해봅시다.
a = 5
b = 3.14159265358979
print("{:.2f}".format(a))
# 5.00
print("{:.4f}".format(b))
# 3.1416
따옴표로 묶은 문자열 내에 {} 괄호를 만들면 변수를 문자열에 끼워 넣는 것이 가능합니다. 여기서 괄호 내부에 여기 들어갈 변수의 형식을 지정해주는 것이죠. "f"는 float 형식으로 변환을 의미하고 ".2"는 소수점 아래 2자리를 의미합니다. ":"도 꼭 빼놓으면 안됩니다.
4) 논리형(boolean)
논리 자료형은 참(True)과 거짓(False)로 구분되는 2개의 값만 갖는 데이터입니다. 오늘 자세히 다루지는 않지만 매우 중요한 "논리 연산자"가 바로 이 논리형 값을 결과로 내어놓습니다. 간략히 논리 연산자를 몇 가지 소개하면 다음과 같습니다. a에 대한 b의 비교입니다.
초과 | 이상 | 동치 | 이하 | 미만 |
a < b | a <= b | a == b | a >= b | a > b |
논리 자료형은 기본적으로 정수형과 동일한 취급을 받습니다. 0인 경우 False, 그 외의 경우는 모두 True로 처리되어 역시 28byte의 메모리를 차지합니다. 그러므로 정수형을 논리형으로도 처리하는 것도 가능합니다. if 키워드를 활용한 조건문은 다음 시간에 배우겠지만, 아래 코드를 실행해봅시다.
a = int(input())
>>> -5
if a:
print("True")
else:
print("False")
# True
a에 입력으로 0이 아닌 어떤 값이 들어오든 True로 처리되기 때문에 print("True")가 실행됩니다. 하지만 정수형과 논리형이 같다는 것은 아니고 확실히 구분되는 자료형이기 때문에 0이 아닌 값이 논리형으로 변환되면 원래 값을 잃고 "True"로 바뀝니다.
4. 더 공부할 만한 내용(중요도)
- 이스케이프 문자(★☆☆☆☆)
- 출력 형식 지정(format)(★★★☆☆)
- 논리 연산자(and, or, xor, not 등)(★★★★★)
- 비트 연산자(★★☆☆☆)