12 minute read


사전 자료형 (Dictionary)

  • 컬렉션 (Collection) : 데이터를 모아 놓은 자료형
    • 모아 놓은 데이터 간의 “순서” 가 존재하지 않는다.
    • 데이터 간의 순서를 기억 할 필요가 없다. (Good!)
      • 집합 (Set)
      • 사전 (Dictionary)
      • etc…
  • 사전 자료형
    • Python에서 가장 많이 쓰이는 Powerful 한 자료형.
    • Key-Value 형태로 사용.
      • 주어진 key 를 넣으면 대응되는 value 가 반환되는 형태이다.
  • 사전 자료형의 정의 및 사용
    • dict() 혹은 {} 로 최초 정의
    • 이후 배열과 같이 [] 안에 key 값을 넣어 value 값을 반환 받을 수 있다.
      • 사전 자료형의 [key] 값은 정수 뿐만 아니라 어느 자료형이든 가능하다.
      • 또한 key 값 간의 자료형이 같지 않아도 된다.

    #사전 자료형의 정의
    carefreelife = dict()
    carefreelife['care'] = 75
    carefreelife['free'] = 2
    carefreelife['life'] = 23
    carefreelife[True] = 'good' #key값 간의 자료형이 달라도 되며 어떠한 자료형도 사용 가능.
      
    programming = dict()
    programming[1] = 'learn'
    #key 값으로 dict() 자료형도 사용 가능. 다중 dict() 자료형 사용이 가능해 보인다.
    carefreelife[programming[1]] = 'do your best'
      
    print(carefreelife)
    print(carefreelife['free'])
    print(carefreelife[1]) # 1 == True로 인식하여 반환.
      
    #사전 자료형의 사용 1
    carefreelife['care'] = carefreelife['care'] - 70 #데이터 수정
    carefreelife['life'] = carefreelife['life'] + 70 #데이터 수정
    print(carefreelife)
      
    #다른 사전 자료형과의 연산
    programming[0] = carefreelife[True] + programming[1]
    if carefreelife[True] != programming[0]:
        print(carefreelife[True], 'is not same to', programming[0])
      
    #사전 자료형의 사용 2
    carefreelife = carefreelife['care']+carefreelife['free']+carefreelife['life']
    print(carefreelife)
    

dictRs_Procdess
실행 결과



사전 자료형의 자료 존재 여부 검사

  • 해당하는 key의 존재 여부 검사
    • 해당 key에 대한 value가 사전 자료형 dict() 내부에 존재한다면 True, 아니면 False 반환.
  • 예: names 리스트에 있는 이름을 key, value로 저장
counts = dict() # 사전 자료형 선언
names = ['care', 'free', 'life', 'free']  # 사전 자료형에 삽입할 데이터 리스트
for name in names:                        # 삽입할 데이터 리스트의 마지막 데이터까지 Loop
    if name not in counts:                # 사전 자료형에 해당 데이터가 없다면(처음 삽입되는 데이터 라면)
        counts[name] = 1                  # 사전 자료형에 (key = 데이터, value = 해당 데이터의 삽입 수)형태로 삽입
    else:
        counts[name] = counts[name] + 1   # 이전에 삽입되었었던 데이터라면 value를 + 1 해준다.
print(counts)                             # 출력

dictTest_Procdess
실행 결과



사전 자료형 관련 내장 메소드

clear() # 모든 key-value 쌍을 제거.
get(key) # key 에 대응하는 value 를 반환.
items() # (key, value)를 하나의 요소로 정의, 전체 요소를 반환.
key() # 사전 자료형의 모든 key들을 반환.
value() # 사전 자료형의 모든 value들을 반환.

# 주어진 key에 대한 value를 반환하고, 해당 key값을 제거.
# Stack 의 pop() 과 비슷한 로직.
pop(key)

# 주어진 새로운 사전 자료형 d의 key, value 값을 추가.
# 기존 자료형에 동일한 key 값이 존재할 경우에는 
# 해당 (key, value)값을 d의 (key, value) 값으로 수정. 
update(d)

사전 자료형 관련 내장 메소드 예제

# 사전 자료형 관련 내장 메소드의 사용
# get(key) : key에 대응하는 value를 반환.
free = counts.get('free')
print('get key(free) : ', free) # 2

# items() : (key, value)를 하나의 요소로 정의하여 전체 요소를 반환.
items = counts.items()
print('items() : ', items) # dict_items([('care', 1), ('free', 2), ('life', 1)])

# keys() : 사전 자료형의 모든 key들을 반환.
keys = counts.keys()
print('keys : ', keys) # dict_keys(['care', 'free', 'life'])

# values() : 사전 자료형의 모든 value들을 반환.
values = counts.values()
print('values : ', values) # dict_values([1, 2, 1])

# pop(key) : 주어진 key에 대한 value를 반환하고 해당 key 값을 제거.
# Stack에서의 pop()과 같다.
pop = counts.pop('free')
print('pop : ', pop, ' after pop : ', items) # 2, dict_items([('care', 1), ('life', 1)])

# 주어진 새로운 사전 자료의 key, value 값을 추가.
# 만약 기존 데이터와 동일한 key 값을 가진다면 수정.
new = dict() # 새로운 사전 자료형 new 선언
new['free'] = 2 # new에 {key = free, value = 2}데이터 삽입
new['care'] = 3
# 기존 사전 자료형 counts에 new 사전 자료형에 있는 데이터를 추가(free, 2) 및 수정(care, 3)
counts.update(new)
print('after update : ', counts.items()) # update()된 counts 출력



dictMethods_Procdess
실행 결과



Conter의 동작 원리

Counter 자료형

  • 사전 자료형에 기반하여 만들어진 자료형.
  • collections 모듈 내부에 존재하는 class(사용자 정의 자료형)이다.
  • 문자열을 인자로 받아 문자당 개수를 세어주는 자료형.
  • 내부적으로 사전 자료형을 포함하고 있다.
  • 동등 비교 연산자 (==) 으로 비교 가능.
    • # Counter class 를 활용한 모습 (어구전철)
      def isAnagramConter(a, b):
        from collections import Counter # Counter 선언
        # a, b 두개의 String을 파라미터로 받아 유효한 문자열로 추려낸다.
        a = ''.join(c for c in a if c.isalnum()).lower() # 영문자 or 숫자인 경우 소문자로 변환
        b = ''.join(c for c in b if c.isalnum()).lower() # 영문자 or 숫자인 경우 소문자로 변환
            
        # a의 key 모음과 b의 key 모음이 같고 value도 같은지 판별. (== 연산자의 역할)
        return Counter(a) == Counter(b)
      

      Counter_Procdess
      실행 결과



ASCII 코드 및 연산

아스키 (ASCII) 코드란 ?

ASCII_Procdess
ASCII

  • 미국 정보 교환 표준 부호 (American Standard Code for Information Interchange)
  • ASCII( /ˈæski/, 아스키)는 영문 알파벳을 사용하는 대표적인 문자 인코딩이다.
  • 아스키는 컴퓨터와 통신 장비를 비롯한 문자를 사용하는 많은 장치에서 사용되며, 대부분의 문자 인코딩이 아스키에 기초를 두고 있다.
  • 아스키는 7비트 인코딩 으로, 33개의 출력 불가능한 제어 문자들과 공백을 비롯한 95개의 출력 가능한 문자들, 총 128개로 이루어진다. (7비트 인코딩이므로 2^7 = 128개)
  • 출력 가능한 문자들은 52개의 영문 알파벳 대소문자와, 10개의 숫자, 32개의 특수 문자, 그리고 하나의 공백 문자로 이루어진다.
  • 제어 문자들은 역사적인 이유로 남아 있으며 대부분은 더 이상 사용되지 않는다.

    • [참고] 유니코드(영어: Unicode)는 전 세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 설계된 산업 표준이다 출처:위키백과



ASCII 문자와 숫자 간의 변환: ord() & chr()

ord() # 문자를 아스키 코드(숫자)로 변환.
chr() # 아스키 코드(숫자)를 해당하는 문자로 변환.

# 문자열 접합 (concatenation) 프로그램
c = ord('A') - 1 # A에 해당하는 아스키 코드의 숫자로 변환 후 -1
s = '' # 빈 문자열 생성
for i in range(26): # 영대문자의 개수
    c += 1  # A의 아스키코드 부터 시작.
    s += chr(c)  # c에 담겨 있는 아스키 코드를 문자로 변환하여 빈 문자열 s에 추가.
print(s) # 출력

ascii_concat_Procdess
실행 결과



Python 에서의 재귀 함수 (Recursion)

  • 재귀 함수 (Recursion) :
    • 함수의 정의 내에 자기 자신의 함수가 포함 되어있는 함수.
      • 함수의 동작 과정에서 자기 자신의 함수가 호출된다.
      • 반복적인 작업을 구현할 수 있다.
    • 함수가 호출 될수록 자신이 돌아가야 할 곳의 위치를 기억해야 한다.
      • 재귀 함수의 호출 수에 비례하여 메모리 사용량이 커진다.
  • 순환문 과의 비교
    • # 순환문으로 구현된 함수
      import time
      def countdown(n):
        while n > 0:
          print(n)
          time.sleep(1)
          n = n - 1
        print("fire!")
      


    •   # 재귀 함수로 구현된 함수
        import time
        def countdown(n):
          if n > 0:
            print(n)
            time.sleep(1)
            countdown(n-1)    # 자신의 정의 내에서 자기 자신의 호출.
          else:
              print("fire!")
      
    • 모든 순환문은 재귀 함수로 구현이 가능하다.



재귀 함수 활용의 예 : sigma 함수

  • sigma(n) : 1부터 n까지 모든 자연수의 합을 계산.
    • sigma_recursion_Procdess
      Sigma 함수의 정의

    • sigma(5) = 15
    • sigma(0) = 0
    • sigma(-3) = 0
  • 재귀로 구현된 표현
    • 재귀 함수를 사용하려면 필수적으로 존재해야 하는 조건이 있다.
      1. 반복 조건 (귀납 조건)
        • 조건문 등을 사용하여 반복 조건을 만들어 준 후 재귀 호출.
      2. 재귀 호출 (Recursive Call)
        • 자기 자신(함수)를 다른 파라미터를 삽입하여 호출.
      3. 종료 조건 (기초 조건)
        • 적당한 조건을 통해 재귀 함수의 종료가 발생하지 않으면 무한 LOOP 발생.
    • recursion_struct_Procdess
      재귀 함수의 필수 조건



재귀 함수를 사용하여 문제 해결하기

  1. 주어진 문제를 “점화식”으로 변환한다 (어려울 수 있다.)
  2. 점화식을 표현할 때 자신을 호출하는 조건이 작성되어야 한다.
  3. 해당 점화식을 그대로 함수로 변환하여 작성한다.

how_recursion_Procdess
점화식으로 표현된 재귀 함수



꼬리 재귀 함수 (Tail Recursion)

  • 꼬리 재귀 (Tail Recursion)
    • 재귀 호출 할 때 더 이상 기억해 줄 것이 없도록 한다.
      • 즉, 재귀 호출의 끝에 도달하여 도출된 결과를 가지고 roll back하며 계산할 것이 남아 있지 않는다.
    • 기존 재귀 함수에 비해 순환 횟수와 관련없는 작은 저장 공간만 필요로 한다.
    • 꼬리 재귀 함수 만드는 방법
      • 일반 재귀 함수를 구현.
      • 각 순환마다의 결과 값을 누적하는 추가적인 함수를 구현하여 해당 함수를 반환.
      • 추가적인 함수는 인자로 입력값과 누적 결과 값을 받음. (n, 0)
    • 매 순환마다의 결과 값을 누적할 추가적인 함수 정의.
    • 필요한 계산(덧셈)을 미리 한 후 해당 결과 값을 추가 인수로 들고 다니도록 한다.
# 꼬리 재귀 함수의 구현
def sigma1(n):
    return loop(n, 0) # 카운터는 n 부터, 누적 결과값은 0 부터 시작.

# 중간 계산 결과를 전달하기 위한 보조함수 loop
def loop(n, sum): # loop(카운터, 누적 결과 값)
    if n > 0:
        # loop 호출 시 카운터 1감소, sum에 각 호출마다의 결과 값 갱신(합) 
        return loop(n - 1, n + sum) 
    else:
        return sum



꼬리 재귀 함수의 분석

  • 시간적 측면
    • 답을 구하는데 걸리는 계산 시간: 재귀 호출 하는 횟수와 비례한다.
    • 인수가 n이면 재귀 호출은 총 n+1번 이므로, 계산 시간은 인수 n에 비례
  • 공간적 측면
    • 필요 공간: 재귀 호출 횟수에 관계없이 일정하다.



일반 재귀 VS 꼬리 재귀

  • 일반 재귀 함수:
    • 할 수 있다면 점화식으로 표현
    • 결과 도출까지의 순환 과정을 되짚어 가며 최종 결과를 도출
      • 비효율적인 메모리 사용
  • 꼬리 재귀 함수:
    • 각 순환마다의 결과 값을 추가 인수에 계속 갱신하며 최종 결과 값을 도출.
    • 마지막 순환 실행 후 추가 인수에 갱신되어온 최종 결과 값을 반환.
    • 일반 재귀 함수와 달리 매 순환 마다의 결과 값이 저장된 위치 기억용 메모리를 사용하지 않음.
      • 공간 비효율의 향상
      • 일반 재귀 함수는 대부분 꼬리 재귀 함수로 변환이 가능하다.

점화식 ---변환---> 일반 재귀 함수 ---변환---> 꼬리 재귀 함수



꼬리 재귀 VS 반복문

꼬리 재귀 함수에서 반복문으로 변환하는 것도 쉽다.

# 꼬리 재귀 함수
def sigma1(n):
    def loop(n, sum):
        if n > 0: #(2)
            return loop(n - 1, n + sum) #(3)
        else:
            return sum #(4)
    return loop(n, 0) #(1)

# 반복문
def sigma2(n):
    sum = 0 #(1)
    while n > 0: #(2)
        sum = n + sum #(3)
        n = n - 1 #(3)
    return sum #(4)

프로그래밍의 철학은 재귀로부터 시작하여 꼬리 재귀를 거쳐 반복문 순서로 만드는 것이다.



문자열 치환 프로그램

  • 사전 자료형을 사용하지 않고 영문자 a부터 z까지를 1~26으로, A부터 Z까지를 27~52로 변환.
  • 변환된 정수 값을 반환해주는 함수 convertCharacter1(c) 작성
문자열 치환 프로그램 소스 코드 보기 (클릭)
# 10_1_1. 문자열 치환
# 사전 자료형을 사용하지 않고 영문자 a부터 z까지를 1~26으로, A부터 Z까지를 27~52로 변환.
# 변환된 정수 값을 반환해주는 함수 convertCharacter1(c) 작성
def convertCharacter1(c):
    try:
        if (65 <= ord(c) <= 90) or (97 <= ord(c) <= 122):
            # 대문자
            if ord(c) <= 90:
                # 대문자의 아스키 코드는 65(A) ~
                return ord(c) - 38
            # 소문자
            else:
                # 소문자 a의 아스키 코드는 97(a) ~
                return ord(c) - 96
    except:
        print('잘못된 입력입니다.')

# 메인 함수
x = ''
while x != '=':
    x = str(input('소문자 a 부터 대문자 Z중 하나를 입력하시오: '))
    print(convertCharacter1(x))

10_1_1_Procdess
문자열 치환 프로그램 실행 결과



카이사르(Caesar) 암호화 프로그램 제작

  1. 영문 소문자로 이루어진 문자열 m과 26이하의 자연수 k를 입력 받는다. (영소문자의 개수 : 26개)
  2. 평문에 있는 문자를 차례대로 하나씩 k번째 뒤의 문자로 변환하여 암호문 작성.

    • 영문 소문자로 이루어진 문자열과 k 값을 입력 받아 암호문으로 변환하는 프로그램 작성.
    • 문자열의 각 문자에 대응하는 ASCII코드 + k 의 ASSCII 코드로 문자열의 전체 문자 변환
    • 암호화 전 기존 문자열로 변환하는 복호화 함수 작성.
입력: 평문과 26이하의 자연수
출력: 암호문

Words

평문 (Plain Text) : 암호화 되지 않은 Original Text
암호문 (Cipher Text) : 암호화된 Text
암호화 (Cipher) : 평문을 암호문으로 만드는 것
복호화 (Decipher) : 암호문을 평문으로 만드는 것

(2) 소스 코드 보기 (클릭)
# 10_1_2. 시저 암호화 및 복호화 함수 작성
# 영문 소문자로 이루어진 문자열과 k 값을 입력 받아 암호문으로 변환하는 프로그램 작성.
# 문자열의 각 문자에 대응하는 ASCII코드 + k 의 ASSCII 코드로 문자열의 전체 문자를 변환.
# 암호화 전 기존 문자열로 변환하는 복호화 함수 작성.

# 시저 암호화 함수
def cipher(string, k):
    # 암호화 된 문자열이 저장될 빈 문자열 rs 생성
    rs = ''
    try:
        # string 이 문자열 인지, k 의 범위가 맞는지 검사
        if (str(type(string)) == "<class 'str'>") and (1 <= k <= 26):
            # 문자열 내 전체 문자 검사
            for char in string:
                # 기존 문자의 아스키 코드 + k 된 아스키 코드를 구한다.
                # 해당 아스키 코드에 대응하는 문자로 변환하여 빈 문자열 rs에 저장.
                rs += chr(ord(char) + k)
            return rs
    except:
        print('잘못된 입력입니다.')

# 복호화 함수
def decipher(string, k):
    # 복호화 된 문자열이 저장될 빈 문자열 rs 생성
    rs = ''
    try:
        # string 이 문자열 인지, k 의 범위가 맞는지 검사
        if (str(type(string)) == "<class 'str'>") and (1 <= k <= 26):
            # 문자열 내 전체 문자 검사
            for char in string:
                # 암호화 된 문자의 아스키 코드 - k 된 아스키 코드를 구한다.
                # 해당 아스키 코드에 대응하는 문자로 변환하여 빈 문자열 rs에 저장.
                rs += chr(ord(char) - k)
            return rs
    except:
        print('잘못된 입력입니다.')

# 메인 함수
m = ''
while m != '=':
    m = str(input('문자열 입력: '))
    k = int(input('1~26 사이의 정수 k 입력: '))
    
    # 암호화
    cip = cipher(m, k)
    
    print('시저 암호화 된 문자열:', '[' + cip + ']')
    print('복호화 된 문자열:', '[' + decipher(cip, k) + ']')

10_1_2,3_Procdess
카이사르(Caesar) 암호화 프로그램 실행 결과



순열 (Permutation) 출력 함수

  • 주어진 입력 문자열에 대해서 모든 가능한 순열을 출력해주는 함수
    • 단, 입력 문자열에서 중복되는 문자는 없다고 가정.
순열 (Permutation) 출력 함수 소스 코드 (클릭)
# 알고리즘
# 문자열 길이 중 각 자리에 어떤 것이 출력될지 결정하여 출력
# 끝까지(n-1까지) 결정이 되었으면 마지막 자리에는 n만큼 넣은 후 출력. (종료 조건)

# a == 입력 문자열
# n == 입력 문자열의 길이
# i == 현재 자리
def perm(a, i, n):
    if i == n-1:
        for i in range(n):
            print(a[i], end='')
        print(end='\n')
    else:
        for j in range(i, n):
            a[i], a[j] = a[j], a[i]
            perm(a, i+1, n)
            a[i], a[j] = a[j], a[i]

string = input('문자열 입력: ')
length = len(string)
listed = list(string)
perm(listed, 0, length)

10_2_2_permutation_rs_Procdess
순열 출력 프로그램 실행 결과



어구전철 관계 여부 판별 프로그램

  • 두 개의 문자열을 입력 받아 어구전철 관계 여부를 판별하는 프로그램.
    • V1 : list의 내장 메소드인 sort() 함수 사용하여 구현.
어구전철 V1(sort) 소스 코드 (클릭)
    # 10_2_1. 어구전철(Anagram) 여부 확인 함수(1)
    # - 두 개의 문자열을 입력 받아 어구전철인지 여부를 출력하는 프로그램 작성
    #   - V1) list의 내장 메소드 "sort" 사용
    def is_anagram_v1(str1, str2):
        x = str1
        y = str2
        if len(x) == len(y):
            x = ''.join(c for c in x if c.isalnum()).lower()
            y = ''.join(c for c in y if c.isalnum()).lower()
        
            x = list(x)
            x.sort()
            y = list(y)
            y.sort()
            for i in range(len(x)):
                if x[i] == y[i]:
                    i += 1
                else:
                    return False
                    break
            return True
    
    
    a = input('첫 번째 문자열 입력: ')
    b = input('두 번째 문자열 입력: ')
    if is_anagram_v1(a, b):
        print('어구전철 입니다.')
    else:
        print('어구전철이 아닙니다.')

10_2_1_V1_rs_Procdess
V1 실행 결과



  • 두 개의 문자열을 입력 받아 어구전철 관계 여부를 판별하는 프로그램.
    • V2 : Counter 모듈 사용하여 구현.
어구전철 V2(Counter) 소스 코드 (클릭)
    # 10_2_1. 어구전철(Anagram) 여부 확인 함수(2)
    # - 두 개의 문자열을 입력 받아 어구전철인지 여부를 출력하는 프로그램 작성
    #   - V2) Counter 모듈 사용
    def is_anagram_v2(str1, str2):
        from collections import Counter
    
        a = Counter(str1).copy()
        b = Counter(str2).copy()
    
        print(a.items())
        print(b.items())
    
        if a.items() == b.items():
            return True
        else:
            return False
    
    a = str(input('문자열 A 입력: '))
    b = str(input('문자열 B 입력: '))
    if is_anagram_v2(a, b):
        print('어구 전철 입니다.')
    else:
        print('어구 전철이 아닙니다.')

10_2_1_V2_rs_Procdess
V2 실행 결과



  • V3 : 재귀 함수 사용(Recursion) - 재귀 함수에 대해 더 자세히 알고 싶다면?

    순환(Recursion) - 클릭!
  • 10_2_1_EX_Procdess
    입출력 예시 화면



최대한의 설명을 코드 블럭 내의 주석으로 달아 놓았습니다.

혹시 이해가 안가거나 추가적인 설명이 필요한 부분, 오류 등의 피드백은 언제든지 환영합니다!

긴 글 읽어주셔서 감사합니다. 포스팅을 마칩니다.



처음으로~

Task Lists

  • 사전 자료형 (Dictionary)
  • 사전 자료형의 자료 존재 여부 검사
  • 사전 자료형 관련 내장 메소드
  • Conter의 동작 원리
  • ASCII 코드 및 연산
  • ASCII 문자와 숫자 간의 변환: ord() & chr()
  • Python 에서의 재귀 함수 (Recursion)
  • 재귀 함수 활용의 예 : sigma 함수
  • 재귀 함수를 사용하여 문제 해결하기
  • 꼬리 재귀 함수 (Tail Recursion)
  • 일반 재귀 VS 꼬리 재귀
  • 꼬리 재귀 VS 반복문
  • 카이사르(Caesar) 암호화 프로그램 제작
  • 순열 (Permutation) 출력 프로그램 제작
  • 어구전철 관계 여부 판별 프로그램 V1, V2, V3 제작

Comments