Think Python의 8장 내용을 요약 및 수정한 내용입니다.
문자열은 여러 개의 문자열들의 나열이라는 의미에서 모음(collection) 자료형의 값이라 부르기도 한다고 앞서 설명하였다. 즉, 여러 개의 값(문자)을 하나로 묶여 있는 것이 문자열이다.
그런데 필요할 때는 항목으로 사용된 각각의 문자를 확인하여 활용할 수 있어야 한다. 인덱싱과 슬라이싱도 이런 용도로 사용되는 도구이지만, 문자열의 길이가 엄청 길어지면 인덱스와 슬라이싱 구간을 수동으로 지정하기 어려운 경우가 발생한다. 즉, 자동화 기능이 요구된다.
while
과 for
반복 명령문이 바로 자동화 기능을 지원한다.
특히, 문자열, 리스트, 튜플, 어레이, 사전 등의 자료형과 함께 사용할 때
두 반복문의 기능을 극대화할 수 있다.
참조: 반복 명령문을 지원하지 않는 프로그래밍 언어는 용도가 매우 제한적이며, 그런 프로그래밍 언어는 거의 사용되지 않는다.
while
반복문¶문자열과 while
반복문을 함께 사용하려면 인덱스를 활용해야 한다.
예를 들어, 문자열 'banana'
에 사용된 항목들을 하나씩 차례대로 출력하려면 다음과 같이 한다.
fruit = 'banana'
idx = 0
while idx < len(fruit):
letter = fruit[idx]
print(letter)
idx = idx + 1
print("최종 index =", idx)
위 코드 설명:
while
반복의 조건인 idx < len(fruit)
가 거짓이 될 때까지 idx
의 값이 0, 1, 2 등으로 변한다.idx
가 0, 1, ..., 5의 값을 가지면 해당 인덱스가 가리키는 위치의 문자를 출력한다. idx
에 할당된 값이 len(fruit)
인 6이 되는 순간에 while
반복문을 벗어난다.
따라서 위 프로그램의 실행이 종료될 때 idx
가 가리키는 값은 banana
문자열의 길이닌 6이 된다.주어진 문자열에 포함된 문자들을 역순으로 인쇄하는 프로그램은 다음과 같다.
fruit = 'apple'
idx = 1
while idx < len(fruit) + 1:
letter = fruit[-idx]
print(letter)
idx = idx + 1
그런데 print
함수가 출력할 때마다 줄바꿈이 사용된다.
줄바꿈이 발생하지 않도록 하려면 다음과 같이 print
함수의 옵션 인자 설정 하나를 바꿔야 한다.
fruit = 'apple'
idx = 1
while idx < len(fruit) + 1:
letter = fruit[-idx]
print(letter, end='')
idx = idx + 1
파이썬에서 특정 함수는 옵션 매개변수를 여러 개 가질 수 있다.
print
함수도 여러 개의 옵션 매개변수를 가지면, end
가 그중에 하나이다.
end
는 출력을 마친 후 실행하는 추가 옵션을 담당하며 기본 옵션값은 줄바꿈('\n'
) 이다.
위 프로그램에서는 end
옵션 매개변수에 할당된 인자를 아무 것도 하지 않는 것을 의미하는
빈 문자열로 지정한 것이다.
예를 들어, 항목들을 쉼표(콤마)로 구분하고 싶다면 다음가 같이 한다.
fruit = 'apple'
idx = 1
while idx < len(fruit) + 1:
letter = fruit[-idx]
print(letter, end=', ')
idx = idx + 1
print
함수의 기타 옵션 매개변수 중에 sep
도 중요하다.
sep
옵션 매개변수에 대한 설명은 코딩도장: 여러 값 출력하기를 참조하면 좋다.
위 프로그램은 슬라이싱을 사용하면 매우 간단하게 해결된다. 스텝이 음수이면 역순으로 슬라이싱을 적용한다.
fruit[::-1]
주의: 항목들 사이에 쉼표 등을 삽입하는 기능은 슬라이싱이 제공하지 않는다.
인터넷 검색에서 가장 중요한 요소는 원하는 단어 또는 문장을 포함한 웹사이트, 문서 등을 찾는 일이다.
여기서는 문자열에 특정 문자가 포함되어 있는지 여부를 판단하는 findChar
함수를
기존 문자열 메서드를 전혀 사용하지 않으면서 구현해 본다.
아래 정의된 findChar
함수는 입력된 문자열에 특정 문자가 포함되어 있으면
그 문자가 가장 먼저 나타나는 곳의 인덱스를 찾아주고, 그렇지 않으면 -1을 내준다.
def findChar(word, letter):
idx = 0
while idx < len(word):
if word[idx] == letter:
return idx
idx = idx + 1
return -1
findChar('banana', 'a')
findChar('banana', 'n')
findChar('banana', 'o')
for
반복 명령문¶파이썬에서 문자열과 같은 모음 자료형은 while
반복문 보다
for
반복문과 사용하는 게 보다 효율적이다.
이유는 for
반복문 자체가 자동으로 인덱스를 1씩 키워주는 기능을 갖고 있기 때문이다.
주의: C와 자바는 for
반복문이 이런 기능을 제공하지 않는다.
따라서 앞서 while
반복문에서 인덱스가 1씩 커지는 것을
지원하는 idx
변수가 필요없게 된다.
예를 들어, 아래 코드는 fruit
에 할당된 문자열의 각 항목을 차례대로 출력한다.
for letter in fruit:
print(letter)
letter
변수는 처음에 a
를 가리킨다.letter
변수가 더 이상 가리킬 값(문자)가 없을 때까지 위 과정을 반복한다.for
반복문의 본체를 실행한다.letter
변수가 가리키는 값을 오른편에 위치한 값으로 대체한다.for
반복 명령문을 활용하여 특정 단어들을 생성하는 예제를 다룬다.
구체적으로, JKLMNOPQ
에 포함된 문자를 하나씩 꺼내어 ack
라는 문자열의 맨 앞에 붙힌 단어들을 생성한다.
# 접두사
prefixes = 'JKLMNOPQ'
#접미사
suffix = 'ack'
for letter in prefixes:
print(letter + suffix)
apple
과 orange
두 단어에 공동으로 포함된 알파벳을 출력하는 방법은 다음과 같다.
word1 = 'apple'
word2 = 'orange'
for letter in word1:
if letter in word2:
print(letter)
두 개의 문자열에 공동으로 사용된 문자들을 모두 출력하는 함수 inBoth
를
구현하려면 word1
과 word2
가 임의의 문자열을 가리킬 수 있도록 하면 된다.
즉, 두 변수를 매개변수로 사용하는 함수를 아래와 같이 선언하면 된다.
def inBoth(word1, word2):
for letter in word1:
if letter in word2:
print(letter)
inBoth('apple', 'orange')
다음 프로그램은 문자열에서 문자 'a'
가 나타나는 횟수를 집계(카운팅, counting)할 수 있다.
word = 'orange'
count = 0
for letter in word:
if letter == 'a':
count = count + 1
print(word + '에서' + " 알파벳 a가", count, '번 사용되었어요.')
주의: print
함수의 인자들을 의도적으로 복잡하게 사용하였다.
아래의 is_reverse
함수는 인자로 입력된 두 문자열이 서로 뒤집어진 관계인지를 판단하는 기능을 갖도록 정의되었다.
def is_reverse(word1, word2):
if len(word1) != len(word2):
return False
i = 0
j = len(word2)
while j > 0:
if word1[i] != word2[j]:
return False
i = i+1
j = j-1
return True
코드 설명:
if
명령문은 단어들의 길이가 같은지 검사하며, 즉시 False
를 리턴하고 실행을 종료한다. i
와 j
선언.while
명령문i
는 word1
를 순방향으로 탐색j
는 word2
를 역방향으로 탐색False
를 리턴하고 실행 종료.while
명령문이 끝난 경우True
리턴하고 실행 종료.그런데 위 함수는 잘못 정의되어 있다.
is_reverse
함수의 정의가 문법상으로는 하자가 없지만 실행을 하다보면 오류가 발생한다.
이와 같은 오류를 실행시간 오류(runtime error) 라 부른다.
is_reverse('pots', 'stop')
이런 종류의 오류를 디버깅할 때 오류를 발생시킨 명령문 바로 앞줄에서 인덱스의 값을 인쇄해보는 것이다.
def is_reverse(word1, word2):
if len(word1) != len(word2):
return False
i = 0
j = len(word2)
while j > 0:
print('i =', i, 'j = ', j) # print 명령문 추가
if word1[i] != word2[j]:
return False
i = i+1
j = j-1
return True
다시 실행하면 좀 더 많은 정보를 얻는다.
is_reverse('pots', 'stop')
while
명령문을 시작하면서 i
와 j
에 할당된 인덱스의 값을 출력한다.
그런데 j
의 값은 4인데, 문자열 'pots'의 범위를 벗어나기 때문에 오류가 발생한다.
마지막 문자의 지수는 문자열의 길이에서 1을 빼야 하므로, j
의 초기값은 len(word2)-1
이어야 한다.
def is_reverse(word1, word2):
if len(word1) != len(word2):
return False
i = 0
j = len(word2) - 1 # 1차 수정
while j > 0:
print('i =', i, 'j = ', j) # print 명령문 추가
if word1[i] != word2[j]:
return False
i = i+1
j = j-1
return True
is_reverse('pots', 'stop')
이번에는 답은 맞았지만 while
순환이 세 번만 실행된 것으로 확인된다.
실제로 아래의 경우에는 오답을 리턴한다.
is_reverse('pota', 'stop')
while
순환이 세 번만 실행되기에 pota
의 마지막 문자인 a
와 stop
의 첫째 문자인 s
의
비교는 실행되지 않기 때문이다.
즉, j
의 값이 0인 경우를 다루지 않는다. 따라서 코드를 아래와 같이 추가 수정해야 한다.
def is_reverse(word1, word2):
if len(word1) != len(word2):
return False
i = 0
j = len(word2) - 1 # 1차 수정
while j >= 0: # 2차 수정
print('i =', i, 'j = ', j) # print 명령문 추가
if word1[i] != word2[j]:
return False
i = i+1
j = j-1
return True
is_reverse('pota', 'stop')
이제 is_reverse
함수가 제대로 작동함을 확인하였다.
따라서 디버깅을 위해 추가한 print
명령문을 이제는 삭제하는 게 좋다.
def is_reverse(word1, word2):
if len(word1) != len(word2):
return False
i = 0
j = len(word2) - 1
while j >= 0:
if word1[i] != word2[j]:
return False
i = i+1
j = j-1
return True
is_reverse('pots', 'stop')
is_reverse('pota', 'stop')
아래 코드는 앞서 살펴본 findChar
검색함수이다.
def findChar(word, letter):
idx = 0
while idx < len(word):
if word[idx] == letter:
return idx
idx = idx + 1
return -1
아래 조건을 만족시키도록 findChar
함수를 수정하라.
word
와 letter
두 개의 매개변수와 더불어
position
이라는 매개변수를 하나 더 사용하도록 한다. position
매개변수는 정수를 입력값으로 기대한다.position
을 통해 전달된 정수는 탐색을 시작할 위치를 나타낸다.
즉, 앞서 정의된 findChar
함수는 position
이 0인 특수한 경우가 되도록 한다.
문자열의 find
메서드에 대응하는 find
함수를 정의하라.
즉, 아래 조건을 만족하는 find
함수를 정의해야 한다.
word1
, word2
, position
세 개의 인자를 받는다.word1
, word2
는 문자열을 입력값으로 기대한다.word1
이 word2
를 부분문자열로 포함할 경우 부분문자열의 시작위치를 리턴하고,
포함하지 않을 경우 -1을 리턴한다.position
매개변수는 정수를 입력값으로 기대한다.position
을 통해 전달된 정수는 탐색을 시작할 위치를 나타낸다.
(힌트) 문자열의 find
메서드 활용 가능.
word = 'orange'
count = 0
for letter in word:
if letter == 'a':
count = count + 1
print(count)
counting
라는 이름의 함수를 선언하라.
즉, counting('orange', 'a')
형식으로 호출되면 orange
문자열에서 a
문자가 몇 번 나타나는지를 확인해주어야 한다.find
함수의 경우처럼 counting
함수를 인자를 하나 더 받아서 특정 인덱스 이후 지정한 문자가 몇 번 출현하는지를 집계하도록 수정하라.
radar
, noon
등과 같이 앞으로 읽으나 뒤로 읽으나 스펠링이 동일한 단어를
회문(palindrome)이라 한다.
문자열이 회문이면 True
를, 아니면 False
를 리턴하는 함수 is_palindrome
을 구현하라.
즉, 아래 코드에서 pass
부분을 적당한 코드로 구현하라. def is_palindrome(word)
pass
is_palindrome
함수를 스텝을 이용하여 구현하라.
다음 함수들은 모두 문자열이 소문자를 포함하고 있는지 여부를 조사하도록 구현되었지만 일부는 제대로 작동하지 않는다. 각각의 함수마다, 함수가 실제로 무엇을 하는지 설명하라. (인자는 문자열이 들어오는 경우를 다룬다.)
def any_lowercase1(s):
for c in s:
if c.islower():
return True
else:
return False
def any_lowercase2(s):
for c in s:
if 'c'.islower():
return 'True'
else:
return 'False'
def any_lowercase3(s):
for c in s:
flag = c.islower()
return flag
def any_lowercase4(s):
flag = False
for c in s:
flag = flag or c.islower()
return flag
def any_lowercase5(s):
for c in s:
if not c.islower():
return False
return True
'A'
를 3만큼 이동하면 'D'
가, 'Z'
를 1만 큼 이동하면 'A'
가 된다.
rotate_word
를 구현하라.
예를 들어, 'cheer'
를 7만큼 회전시키면 'jolly'
이고 'melon'
을 -10만큼
회전시키면 'cubed'
가 되어야 한다.
#### 힌트:
문자를 숫자 코드로 형변환하는 ord
내장함수와
숫자코드를 문자로 형변환 시키는 chr
내장함수를 이용할 수 있다.
아래 사이트에 가면 모범답안이 있다. 하지만 먼저 스스로 해결하도록 노력해 보아야 한다.