본문 바로가기

카테고리 없음

[ 11/09 Today I Learned ] 알고리즘 기본 / 파이썬 기본

좋은 프로그램. 좋은 개발자란

 

개발자는 프로그램을 만드는 직업이다.

좋은 개발자가 되기 위해서는, 좋은 프로그램을 짤 줄 알아야 한다.

 

좋은 프로그램이란?

  • 적은 공간을 활용해서, 빠른 속도로 수행되는 프로그램이다.

이런 프로그램을 위해서는

특정 자료구조 ( = 공간 )와, 접근방법 = 알고리즘 ( =속도 )를

사용해야 한다.

즉, 프로그래밍을 잘하기 위해서는

여러 자료구조와, 방법들을 배우고 익혀야 한다.

막연하게 개발만 하면 좋은 코드를 만들지 못하니,

알고리즘 공부를 통해 질을 챙겨보자!

 

[ 알고리즘 #1 ] 배열 내 최고값 찾기


말 그대로, 주어진 배열에서 최고값이 뭔지를 찾는 알고리즘이다.

 

강의에서 제시하는 Python 으로 개발한 버전이 두가지 있었다.

 

사실 나는 2번째 방법으로만 풀고 있었는데,

 

2중 for 문을 통해, 최고값을 담을 변수를 선언하지 않고

구현한 첫번째 방법이 신선하고 새로웠다. ( But, 후술할 시간복잡도 측면에서는 효율이 좋지 않다 )

input = [3, 5, 6, 1, 2, 4]

def find_max_num(array):
    for num in array:
        for compare_num in array:
            if num < compare_num:
                break
        else:
            return num

result = find_max_num(input)
print(result)

< 파이썬에서 for-else 구문을 사용할 수 있다는 것 또한 신기했다 >

 

[ for - else ? ]

for문을 break 없이 다 돌았을 때, 어떤 명령을 수행할 지 else 구문에 적어놓는 방식이다.

 

두번째 방법에서 깨달았던 것은

 

함수 내에 지정변수를 사용할 때, 초기값을 내 임의로 정하는 것이 아니라

 

주어진 배열의 첫번째 값으로 초기화 하는 점이었다.

 

하드코딩을 예방하는, 에러의 가능성을 없애는 코드 짜는 능력에 감탄했다.

 

input = [3, 5, 6, 1, 2, 4]

def find_max_num(array):
    # 지정 변수를 이용해서 구현
    max_num = array[0]  # 첫번째 원소를 넣으면, if-else 문으로 거를 필요도 없어지네!
    for num in array:
        if max_num < num:
            max_num = num

    return max_num

result = find_max_num(input)
print(result)

 

막연히 max_num 부분을 0 이라는 상수로 초기화했었는데,

 

이렇게 코딩하면 추후에 어떤 array가 들어오더라도, 값을 비교할 수 있겠다는 생각이 들었다!

 

[ 알고리즘 #2. 주어진 문장에서 가장 자주 나온 알파벳 찾기 ]

 

주어진 문장에서 가장 많이 나온 알파벳의 종류와 횟수를 찾는 알고리즘을 만들어보았다.

def find_max_occured_alphabet(string):

    alphabet_occurence_array = [0]*26

    alphabet_string_real = ''

    for x in range(len(string)):
        if string[x].isalpha():
            alphabet_string_real += string[x]

    alphabet_string_real = alphabet_string_real.lower()
    string_to_ascii = list(alphabet_string_real.encode('ascii'))

    # print(string_to_ascii)

    for x in range(len(string_to_ascii)):
        alphabet_occurence_array[int(string_to_ascii[x])-97] += 1

    for i, num in enumerate(alphabet_occurence_array):
        for compare_num in alphabet_occurence_array:
            if num < compare_num:
                break
        else:
            return {'alphabet':chr(i+97), 'occurence': num}

def print_result(result):
    print('가장 많이 나온 알파벳 : ' + result['alphabet'] 
		+ '\\n횟수 : ' + result['occurence'].__str__())

result1 = find_max_occured_alphabet("Hello my name is sparta!")
print_result(result1)

result2 = find_max_occured_alphabet("Sparta coding club")
print_result(result2)

result3 = find_max_occured_alphabet("best of best sparta")
print_result(result3)
가장 많이 나온 알파벳 : a
횟수 : 3
가장 많이 나온 알파벳 : a
횟수 : 2
가장 많이 나온 알파벳 : s
횟수 : 3

이렇게 잘 나온다.

( 지금 생각해보면, print_result 함수를 정의할 때, 후술할 파이썬 기본에서 배운 f-string을 사용했으면 더 깔끔했겠단 생각이 들어 아쉽다! )

 

하지만 위 코드에는 몇가지 문제점이 있었다.

 

 

[1] 쓸데없는 변수의 선언

alphabet_string_real = ''

    for x in range(len(string)):
        if string[x].isalpha():
            alphabet_string_real += string[x]

input 으로 받은 string 의 공백을 제거해주기 위한 코드인데.

굳이 이걸 따로 변수로 만들어서 저장할 필요가 없었다.

[2] 쓸데없는 변수 ( 리스트 ) 선언 & 내장함수 사용

 string_to_ascii = list(alphabet_string_real.encode('ascii'))

string_to_ascii 라는 리스트를 담는 변수를 만들어,

알파벳을 아스키코드 값으로 변환하는 작업을 거쳤다.

둘 다 공간복잡도를 늘린다는 점에서 개선점이 있고

더 큰 개선점은, 코드가 읽기 어렵다는 것이다. 직관적이지 않아, 협업할 때 좋은 코드가 아니다. ( 개선점 3 )

이를 개선한 코드가 아래의 코드이다.

def find_max_occured_alphabet(string):

    alphabet_occurence_array = [0]*26
    string = string.lower()

    for char in string:
        if not char.isalpha():
            continue
        arr_index = ord(char) - ord('a')    # ASCII 코드를 사용하여, 배열의 index를 계산한다.
        alphabet_occurence_array[arr_index] += 1

    max_num = alphabet_occurence_array[0]
    max_idx = 0
    compare_idx = -1    # 배열에서 비교할 값의 index를 담기 위한 변수, for 문에서 처음 돌 때 +1 이 되므로, 초기값을 -1로 지정해주었다.
    for num in alphabet_occurence_array:
        compare_idx += 1
        if max_num < num:
            max_num = num
            max_idx = compare_idx

    return {'alphabet': chr(max_idx+ord('a')), 'occurence' :max_num }

def print_result(result):
    print('가장 많이 나온 알파벳 : ' + result['alphabet']
          + '\\n횟수 : ' + result['occurence'].__str__())

result1 = find_max_occured_alphabet("Hello my name is sparta!")
print_result(result1)

result2 = find_max_occured_alphabet("Sparta coding club")
print_result(result2)

result3 = find_max_occured_alphabet("best of best sparta")
print_result(result3)

이 코드 중에서

for char in string:
        if not char.isalpha():
            continue
        arr_index = ord(char) - ord('a')    # ASCII 코드를 사용하여, 배열의 index를 계산한다.
        alphabet_occurence_array[arr_index] += 1

이 부분이, 앞에 모든 과정을 생략하게 해준다.

 

 

=========

 

[ 파이썬 기본 문법 꿀팁 정리 ]

 

<f-string>

 

scores = [
    {'name':'영수','score':70},
    {'name':'영희','score':65},
    {'name':'기찬','score':75},
    {'name':'희수','score':23},
    {'name':'서경','score':99},
    {'name':'미주','score':100},
    {'name':'병태','score':32}
]

for s in scores:
    name = s['name']
    score = s['score']
    # print(name + '의 점수는 ' + str(score) + '점입니다.')

    print(f'{name}의 점수는 {score}점입니다.')

 

이거 듣고나서, 아 이거다!! 싶었다.

 

파이썬 내장함수인 print를 사용하여 출력할 때, 

 

그 안에 int나 다른 값들을 str() 을 씌워주는게 참 안이쁘고 귀찮았는데,

 

마치 jQuery에서 배운 것처럼, f-string 이란 기법을 사용하는 게 신선했다.

 

얘는 오래 볼 거 같은데? 꼭 기억해놔야겠다 ㅎㅎ

 

 

< 한 줄의 마법 >

 

if num % 2 == 0:
    result = '짝수'
else:
    result = '홀수'

이런 구문을 

result = '짝수' if num % 2 == 0 else '홀수'

이렇게 바꿔줄 수도 있다!

 

또한

 

for a in a_list:
    b_list.append(a*2)

이런 구문을

 

b_list = [a*2 for a in a_list]

이렇게 바꿔줄 수도 있다!

 

 

보다 더 직관적이고, 섹시하게 코드를 짤 수 있겠단 생각이 들었다.

 

 

https://www.w3schools.com/python/python_lists_comprehension.asp

 

Python - List Comprehension

W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, Python, SQL, Java, and many, many more.

www.w3schools.com

 

더 찾아보니 이런 개념도 나왔다, 이렇게 코딩하면 나도 좀 프로페셔널해 보일지도?

 

[ 리스트를 다루는 방법들 ]

 

< map >

- map은 주어진 리스트의 자료들을 함수와 매칭한 결과 그 자체이다.

 

- map 에 list 를 씌워줌으로써, 주어진 함수를 처리한 결과물들을 리스트에 담을 수 있다.

 

- 주어진 리스트에 연산을 통해 조작한 결과물들을 넣을 수 있다. ( 리스트 전체를 건들임 )

 

- map (  적용할 함수 , 리스트 ) // 적용할 함수를 주로 lambda 식을 이용해서 쓴다

 

 

people = [
    {'name': 'bob', 'age': 20},
    {'name': 'carry', 'age': 38},
    {'name': 'john', 'age': 7},
    {'name': 'smith', 'age': 17},
    {'name': 'ben', 'age': 27},
    {'name': 'bobby', 'age': 57},
    {'name': 'red', 'age': 32},
    {'name': 'queen', 'age': 25}
]

이런 dictionary로 이루어진 list가 있을 때,

 

result = map(lambda person: ('성인' if person['age'] > 20 else '청소년') , people)

print(list(result))

 

이렇게 써준다.

 

 

< lambda >

 

lambda person: ('성인' if person['age'] > 20

와 같이 간단한 함수를 한 쥴로 요약할 수 있다.

 

 

< filter >

 

- 주어진 리스트에, 함수를 적용한 결과를 매칭시켜놓은 것 자체이다.

 

- 조건을 통과한 리스트 요소들만으로 이루어진 리스트를 추출하는 역할이다.

 

- filter ( 적용할 함수, 리스트 )  // 적용할 함수를 lambda 를 활용해서 쓴다.

 

result = filter(lambda x: x['age'] > 20, people)

 

이 filter 에도, list 를 씌워줘서, list를 출력할 수 있다.

 

 

 

아 그리고 오늘의 TIL 중 핵심!!

ZEP에서 의자 앞에서 'x'키를 누르면 캐릭터가 앉는다.

넘 귀여웠다 ( 알려주신 분 : 하경화님 )

<ZEP에서 x키를 누르면 앉기가 된다!>