TigerCow.Door

안녕하세요. 문범우입니다.

이번에 소개해드릴 알고리즘 문제는, 2017년 카카오톡 블라인드테스트 1차 코딩시험에서 나왔던 문제중 난이도가 제일 낮다는 소개된 '비밀지도' 문제입니다.


해당 문제는 프로그래머스를 통해, 아래 주소에서 만나보실 수 있습니다.

https://programmers.co.kr/learn/courses/30/lessons/17681?language=python3


난이도가 가장 낮다고 소개된 만큼, 문제자체도 간단하고 풀이도 어렵지 않습니다.

따라서 해당 문제는 추가적인 설명대신 코드만 첨부해드리도록 하겠습니다.

추가적으로 궁금한 사항이 있으시면 언제든지 댓글 및 카카오톡이나 이메일을 통해서 연락주시면 바로 답변드리도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def solution(n, arr1, arr2):
    answer = []
    decode_arr1 = []
    decode_arr2 = []
    tmp_str = ''
    tmp_answer = ''
    for i in arr1:
        tmp_str = str(bin(i))[2:]
        while(len(tmp_str) < n):
            tmp_str = '0'+tmp_str
        tmp_str = tmp_str.replace('0',' ')
        tmp_str = tmp_str.replace('1','#')
        decode_arr1.append(tmp_str)
    for i in arr2:
        tmp_str = str(bin(i))[2:]
        while(len(tmp_str) < n):
            tmp_str = '0'+tmp_str
        tmp_str = tmp_str.replace('0',' ')
        tmp_str = tmp_str.replace('1','#')
        decode_arr2.append(tmp_str)
    
    for i in range(n):
        for j in range(n):
            if (decode_arr1[i][j] == '#'or (decode_arr2[i][j] == '#'):
                tmp_answer += '#'
            else:
                tmp_answer += ' '
        answer.append(tmp_answer)
        tmp_answer = ''
    return answer
cs


블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc

안녕하세요. 문범우입니다.

요새 많은 기업들이 공채시즌이 다가와서 그런지, 평소보다 알고리즘 문제풀이에 대한 학원이나 온라인강의에 대한 광고가 많아진 것 같네요.


요새보면 대부분의 기업에서 SW인원들은 다른 시험보다 코딩테스트를 중요시하고 있고 많은 사람들이 제일 까다로워 하는 부분인 것 같습니다.


요새 개인적으로 공부하는 기계학습이나, 리액트네이티브때문에 블로그활동을 자주못하고 있는데, 오랜만에 프로그래머스에 들어갔다가 2017년 카카오톡 블라인드테스트 1차 코딩문제를 공개해두었길래 이번주에 하나씩 풀어보려합니다.


처음에는 쉬운문제부터 풀어보려했는데.. 나중에 확인해보니 이번에 소개해드릴 '추석트래픽' 문제가 가장 어려웠다고 하네요.


프로그래머스에서 제공하는 작년 카카오톡 코딩테스트 문제는 아래에서 만나보실수 있으며,

https://programmers.co.kr/learn/challenges


이에 대한 전체적인 해설은 아래에서 만나보실수 있습니다.

http://tech.kakao.com/2017/09/27/kakao-blind-recruitment-round-1/



오늘 소개해드릴 '추석트래픽' 문제의 정답률이 약18%라고 하지만, 개인적인 생각으로는 2017 카카오톡 블라인드테스트 1차 코딩테스트에서 총 5시간이 주어졌기때문에 어려웠다기보단 시간이 부족했다는 이야기가 많았을 듯 합니다.


문제에 대한 전체적인 안내나, 난이도정도등에 대해서는 위에 소개해드린 해설에서 확인해보시길 바랍니다.



1. 추석 트래픽


추석 트래픽 문제에 대한 설명은 따로 진행하지 않겠습니다.

제가 말로 주구장창 설명하는 것보다 직접 문제와 예제를 보시는게 이해가 빠를 것 같아서요 :'(


특별히, 예제3번에서 하나의 그림을 보여주고 있습니다.

x축을 시간으로 두고 각각의 트래픽을 bar형태로 표시해두었죠.

그리고 1초라는 시간범위(구간)를 정해서, 가장 많은 트래픽이 포함되는 구간에서의 트래픽 개수를 찾아내고 있습니다.


해당 그림을 보면서 어디서 많이 낯익다 싶었습니다.

바로, An activity-selection problem 문제입니다.

작년 알고리즘수업을 들으면서 봤던 문제인데, 잘 모르시는 분들은 한번 쯤 찾아보셔도 좋을 듯 합니다.


먼저 저는 입력되는 lines 를 하나씩 가져와서 datetime 객체로 바꾸고 이를 end_datetime으로 두었으며 lines에서 주는 실행시간을 가져와서 실행시간의 초단위 값 processing_s 와, 실행시간의 micro second단위 값 processing_ms 를 만들었습니다.

그리고 이 세개의 값를 이용해서, 트래픽의 시작시간을 구해 datetime객체로 하여 start_datetime으로 두었습니다.


이들을 이용해 같은 트래픽끼리 하나의 리스트로 묶어서, start_end_datetime 리스트에 저장하였고, 추후 answer를 탐색하기 위해 sorted_time 리스트를 만들어 start_datetime과 end_datetime의 모든 요소를 같이 저장하였습니다.

그리고 모든 lines에 대한 처리가 끝나면 sorted_time 리스트는 sort함수를 통해 오름차순으로 정렬합니다.


즉, 예제 1번과 같이 입력이 다음과 같다면,

입력: [
2016-09-15 01:00:04.001 2.0s,
2016-09-15 01:00:07.000 2s
]


start_end_datetime = [[ '2016-09-15 01:00:02.002000', '2016-09-15 01:00:04.001000' ], [ '2016-09-15 01:00:05.001', '2016-09-15 01:00:07.000']]


sorted_time = [ '2016-09-15 01:00:02.002000', '2016-09-15 01:00:04.001000', '2016-09-15 01:00:05.001', '2016-09-15 01:00:07.000']


과 같이 만들어지게 됩니다.


이제 문제에서 원하는 답을 찾을 차례입니다.

여기서 저도 한번 헤매고, 1000 micro second마다 탐색하는 방법으로 시도해봤더니 역시나 시간초과에 걸렸었습니다....


하지만 조금 더 생각해보면, 구하고자 하는 초당 최대 처리량이 변하는 순간은 단지 어떤 트래픽의 시작 또는 종료 시점뿐 입니다.

즉, 위에서 만들어두었던 sorted_time 리스트에 있는 시간에서만 초당 최대 처리량의 변화가 발생합니다.

따라서 우리는 sorted_time 리스트를 범위로 for문을 돌리면 되고, sorted_time 리스트에서 꺼낸 하나의 요소를 compare_time으로 두었고, 여기에 1초를 더한 시간을 compare_time_one으로 두었습니다.

그리고 start_end_datetime에서 하나씩 꺼내어 compare_time과 compare_time_one이라는 범위에 해당 트래픽이 속하여있는지를 탐색하고 각각의 탐색에 따른 최대값을 찾아 정답으로 반환하면 됩니다.


설명이 잘 된지는 모르겠으나, 실제로 코드를 보시면서 이해해보시면 잘 이해할 수 있을 것이라고 생각됩니다.


전체적인 python 코드는 다음과 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import datetime
 
def solution(lines):
    start_end_time = []
    sorted_time = []
    tmp_answer = 0
    answer = tmp_answer
    for line in lines:
        split_line = line.split()
        split_day = split_line[0].split('-')
        split_time = split_line[1].split(':')
        split_s = split_time[2].split('.')
 
        Y = int(split_day[0]); M = int(split_day[1]); D = int(split_day[2])
        h = int(split_time[0]); m = int(split_time[1])
        s = int(split_s[0]); ms = int(split_s[1])*1000
        
        end_datetime = datetime.datetime(Y,M,D,h,m,s,ms)
        
        split_processing = split_line[2][:-1].split('.')
        processing_s = int(split_processing[0])
        if len(split_processing) == 1:
            start_datetime = end_datetime - datetime.timedelta(seconds=processing_s)
        else:
            processing_ms = int(split_processing[1]) * 1000
            start_datetime = end_datetime - datetime.timedelta(seconds=processing_s) - datetime.timedelta(microseconds=processing_ms)
        start_datetime = start_datetime + datetime.timedelta(microseconds=1000)
        start_end_time.append([start_datetime,end_datetime])
        sorted_time.append(start_datetime)
        sorted_time.append(end_datetime)
    sorted_time.sort()
    
    for compare_time in sorted_time:
        compare_time_one = compare_time + datetime.timedelta(seconds=1)
        if compare_time >= start_end_time[-1][1]:
            break;
        for each in start_end_time:
            if (compare_time <= each[0])and(each[0< compare_time_one):
                tmp_answer += 1
            elif (compare_time <= each[1])and(each[1< compare_time_one):
                tmp_answer += 1
            elif (each[0<= compare_time)and(compare_time_one <= each[1]):
                tmp_answer += 1
        if answer < tmp_answer:
            answer = tmp_answer
        tmp_answer = 0
    if answer == 0:
        answer += 1
    return answer
cs


만약 코드에 대해 궁금한 사항이나, 보다 효율적인 방법에 대해서 말씀해주실 점이 있다면 언제든지 댓글 또는 카카오톡, 이메일을 이용해서 말씀해주세요 :)

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc


안녕하세요. 문범우입니다.


최근 멋쟁이 사자처럼 6기 운영진으로 활동하며, 지난 8월말에 해커톤을 진행하였습니다.

저는 방학간 파이썬, 장고 스터디를 진행하며 함께 공부한 친구들과 장고를 활용한 공유일기장 플랫폼 웹사이트를 개발하였습니다.


aws ec2 프리티어을 이용하여 배포까지 완료하였으나, 실제로 서비스 론칭등의 계획은 없습니다.

장고를 직접 활용해보고 6기 인원들에게는 배포까지 해보는 경험으로써의 토이 프로젝트였습니다.


활용된 스택은 다음과 같습니다.


python: 3.6.5


django: 2.0


postgresql: 10.4



해당 프로젝트에서 저는, 프로젝트 전체 기획 및 진행을 담당하며 세부적으로는 데이터베이스 설계 및 구축, 교환일기장 기능 개발을 담당하였습니다.



- 서비스 소개


카카오톡 소셜 로그인



장고를 활용한 공유일기장 플랫폼에서는 카카오톡 소셜로그인을 통해 누구나 쉽게 서비스를 이용할 수 있도록 개발하였습니다.



메인화면 달력 및 작성한 일기 확인하기


사용자는 메인화면에서 달력을 확인할 수 있고, 그 전에 작성한 일기들은 해당 요일에 제목이 표시됩니다. 제목을 누르면 해당 일기로 이동할 수 있습니다.



일기 작성하기


사용자는 일기 쓰기 탭을 눌러서 일기를 작성할 수 있습니다.

제목, 작성할 교환일기장, 감정, 날씨, 사진, 내용을 입력합니다.

하나라도 작성되지 않을시에는 저장되지 않고 빈칸을 알려줍니다.



교환일기장


교환일기장에서는 개인이 작성한 일기 뿐아니라 다른 사람의 일기를 확인할 수 있습니다. 관심사가 같거나, 친구들끼리 함께 일기를 작성하고 싶을때는 교환일기장을 만들거나, 참여하여 함께 일기를 작성할 수 있습니다.



이 외에도 일기를 보관할 수 있는 '뜯어가기' 기능과 댓글기능 등을 구현하였습니다.



배포된 프로젝트의 모든 코드는 아래 깃헙에서 확인하실 수 있습니다.

https://github.com/doorBW/LAN-Diary


이 외에도 추가적인 질문등은 언제든지 댓글 및 이메일, 카카오톡을 이용해주세요 :)

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc


안녕하세요.

이번 포스팅에서는 django에서 admin 페이지를 사용해보도록 하겠습니다.



1. Admin 계정 만들기


먼저 우리는 지난 포스팅까지해서 django와 postgresql의 django_test라는 데이터베이스를 만들어서 연결하였습니다.

그리고 django에서 post라는 모델을 생성하여 migrate 해줌으로써 실제 django_test라는 데이터베이스 내부에 blog_post라는 테이블이 생성된 것을 확인하였습니다.


django에서는 model에 대한 data를 간편하게 확인하고 조작할 수 있는 admin 페이지를 제공해주는데, 이때 admin페이지에는 아무나 접근 가능한 것이 아니고, 서버개발 과정에서 admin 계정을 직접 만들어주어야 합니다.

admin 계정을 만들기 위해서 manage.py 파일이 있는 위치에서 아래 명령어를 입력해줍니다.


python manage.py createsuperuser



명령어를 입력하면 위와 같이 admin 계정에 대한 정보를 입력할 수 있습니다.

여기서 입력한 Username과 password를 통해 admin 페이지에 접근가능합니다. 



2. Admin 페이지 사용하기


이렇게 admin 계정을 만들었다면 이제 서버를 돌려서 주소의 가장 끝에 /admin 을 붙여 admin 페이지에 접근해보겠습니다.



이렇게 접근하면, 아래와 같은 화면이 나타나게 됩니다.



이제 위에서 만들었던 admin 계정의 username과 password를 입력해서 로그인을 합니다.


그런데 로그인을 하고나서 확인을 해보아도 우리가 이전에 만들었던 post와 관련된 것은 찾아볼 수 없습니다.

우리가 마이그레이션을 통해 만들었던 post 모델을 admin 페이지에서 보기 위해서는 이를 admin 페이지에 등록을 해주어야 합니다.


우리가 만든 blog라는 app의 폴더로 들어가보면 admin.py 라는 파일이 있습니다. 해당 파일을 켜서 아래와 같이 작성해줍니다.


1
2
3
4
5
from django.contrib import admin
from .models import Post
# Register your models here.
 
admin.site.register(Post)
cs


그리고 해당 파일을 저장한 뒤에 다시 admin 페이지를 새로고침 해보면,



위와 같이 blog 라는 app에 Posts 라는 것이 새로 생긴것을 확인할 수 있습니다.

실제로 저것을 통해 Posts 라는 테이블에 저장된 값들을 확인할 수 있으며 우리가 직접 데이터를 추가할 수도 있습니다.


이를 직접 확인해보기 위해서, admin 페이지에서 데이터를 입력해보고 postgresql에서 확인해보도록 하겠습니다.


admin 사이트에서 Posts 우측에 있는 Add 버튼을 눌러서 데이터를 추가해보겠습니다.



위와 같이 제목과 내용을 입력하고 Save 버튼을 눌러 데이터를 저장합니다.


그럼 실제로 admin 페이지에서 데이터가 성공적으로 입력되었다는 문구가 뜨고, Posts object도 하나 생긴것을 볼 수 있습니다.


이제 postgresql에서 확인해보도록 하겠습니다.



간단한 SQL문장으로 해당 테이블을 확인해보니 정상적으로 데이터가 입력된 것을 볼 수 있습니다.



이렇게 해서 admin 페이지 사용에 대한 간략한 설명을 진행해보았습니다.

추후에 지속될 포스팅 내용으로는, 개발된 django 어플리케이션을 aws를 이용하여 배포하는 방법을 진행해보도록 할 예정입니다.

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc


안녕하세요. 문범우입니다.

이번 포스팅에서는 django에서 첫화면을 만들어보도록 하겠습니다.

먼저 우리가 만들어볼 전체적인 그림은 MVC패턴을 이용하여 간단한 블로그를 만들어 보는 것 입니다.

글을 작성하고 이것이 데이터베이스에 저장되고, 글을 삭제하거나 그에 대한 댓글을 다는 등의 기능을 구현해보도록 할 것 입니다.

먼저 이번 포스팅에서는 위에서 말씀드린 것과 같이 간단하게 첫화면을 만들어보도록 하겠습니다.


1. App 만들기


먼저 우리는 지난 포스팅을 통해서 start_django라는 프로젝트를 만들었습니다.

장고에서는 프로젝트 내부에 여러개의 app이 존재하면서 여러개의 기능을 구현할 수 있습니다.


먼저 우리는 blog라는 app을 만들어 보도록 하겠습니다.


터미널(윈도우의 경우 cmd창)을 열어서 지난 포스트에서 만든 프로젝트까지 이동합니다.

그리고 아래의 명령어를 입력합니다.


python manage.py startapp blog



그럼 위와 같이 start_django라는 우리의 프로젝트 폴더안에 blog라는 폴더가 새로 생긴 것을 확인할 수 있습니다.

현재 명령어 창이 위치한 start_django 폴더는 프로젝트의 기본 폴더라고 생각하시면 되고, 이 내부에 blog라는 app이 존재하며, 또 존재하는 start_django 폴더는 프로젝트 전체에 대한 설정 파일들이 담겨 있다고 생각하시면 됩니다.



2. 첫번째 화면 만들기


그럼 첫번째화면을 만들기 위해서 먼저 url지정을 진행해보도록 하겠습니다.

에디터를 통해 start_django > start_django > urls.py 파일을 열어봅니다.



위와 같이 매우 긴 주석처리와 약간의 코드가 존재합니다.

django 1.x 버전과 2.0 버전의 차이점 중의 하나가 여기서 나타나는데, 1.x 버전에서는 url 지정에서 url 함수를 사용하며 정규식을 사용하는데 2.0 이후부터는 path함수를 통해 정규식이 필요 없어지게 되었습니다.


우리가 추가하고자 하는 url은 맨처음 들어올 메인화면에 대한 것이므로 다음과 같이 코드를 수정합니다. (주석처리된 곳은 삭제하여도 무방합니다.)


1
2
3
4
5
6
7
from django.contrib import admin
from django.urls import path, include
 
urlpatterns = [
    path('', include('blog.urls')),
    path('admin/', admin.site.urls),
]
cs


2번째 줄을 보시면 include라는 함수를 추가로 import 하였습니다.

그리고 5번줄에서 path함수를 활용하여 첫 화면, 아무것도 입력되지 않은 url에서는 blog의 urls를 참고하도록 하였습니다.


이제 blog라는 폴더안에 urls.py 이라는 파일을 새로 만들어서 아래와 같이 코드를 작성합니다.


1
2
3
4
5
6
from django.urls import path
from . import views
 
urlpatterns = [
    path('', views.index),
]
cs


해당 urls.py 파일은 위에서 작성한 파일과 다른 파일입니다.

blog라는 폴더 내부에 새롭게 만든 파일입니다.

먼저 해당 blog에서는 존재하지 않은 admin관련 import를 삭제하였습니다.

admin과 관련되어서는 추후 model을 다루게 될때 살펴보도록 하겠습니다.

그리고 화면을 보여주도록 할 views 파일을 import 하였고, url패턴에서 아무것도 입력되지 않은 주소에 대해서 views의 index를 참고하도록 하였습니다.


그럼 마지막으로 blog 폴더에 있는 views.py 파일을 열어 아래와 같이 코드를 작성합니다.


1
2
3
4
5
6
from django.shortcuts import render
from django.http import HttpResponse
 
# Create your views here.
def index(request):
    return HttpResponse("Main Screen!!!")
cs


2번째 줄에, HttpResponse 라는 함수를 추가로 import하였습니다.

그리고 index라는 함수를 만들었고 단순히 HttpResponse함수를 통한 반환을 하도록 하였습니다.


이렇게 하고 명령어창을 기본 start_django 폴더에 위치하게 한다음, 아래의 명령어를 입력하면 서버가 돌아가게 됩니다.


python manage.py runserver



migrations 에 대한 경고가 뜨는데 일단은 무시하겠습니다.

그리고 이제 localhost:8000 으로 접속하게 되면 아래와 같이 우리가 작성한 첫화면이 나오게 됩니다.



블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc


안녕하세요. 문범우입니다.

이번 포스팅에서는 파이썬에서 사용되는 컴프리헨션(Comprehension)이라는 개념에 대해서 알아보도록 하겠습니다.



1. 컴프리헨션(Comprehension)이란?


일단, 파이썬에서 사용되는 Comprehension이 무엇인지 알아보기 전에, 어떤 의미를 가지고 있는 단어인지 살펴보았습니다.

사전적으로는 이해, 이해력, 포용, 포용력, 포함, 압축 등의 뜻을 가지고 있습니다.

단순히 이런 의미로는 대체 어떻게 파이썬에서 사용되는지 감이 쉽게 안오실텐데, 하나씩 천천히 살펴보시면 충분히 이해하실 수 있을 것입니다.


앞으로 알아보는 Comprehension을 보다 제대로 이해하기 위해서는 기본적으로 파이썬의 조건문, 반복문 등의 개념을 알고 있으셔야 하며 해당 개념은 리스트, 집합(set), 딕셔너리(dictionary) 자료형에 대해 사용될 수 있기 때문에, 해당 자료형에 대해서도 알아야 제대로 이해할 수 있습니다.


2. List, Set, Dict Comprehension


먼저 알아볼 내용은 List, Set, Dict 자료형으로 사용되는 Comprehension입니다.

사용되는 방법이나, 문법등은 동일하나 어떤 자료형에 적용시키는지에 따라 다르기 때문에, 대표적으로는 리스트자료형으로 Comprehension을 알아보도록 하겠습니다.


List Comprehension은 쉽게 생각하면, 반복되거나 특정 조건을 만족하는 리스트를 보다 쉽게 만들어 내기 위한 방법입니다.



2-1. 반복문을 사용한 Comprehension


실제로 어떻게 사용되는지 먼저 간단한 예제를 통해 확인해보겠습니다.



위의 코드를 보시면 총 3번의 Comprehension이 사용되었습니다.

그 동안 우리는 특정 리스트를 생성하기 위해서 직접 값을 넣어주거나, 기타 다른 함수들을 이용했지만 Comprehension이라는 개념을 사용하면 위와 같이 쉽게 리스트를 생성할 수 있습니다.


첫번째 코드, [i for i in range(10)] 은 사실상 단순히 range()를 이용해도 되지만, Comprehension이 어떻게 사용되는지를 보여드리기 위해 보여드렸습니다.



2-2. 조건문을 사용한 Comprehension


위에서 살펴본 것은 반복문을 사용한 Comprehension 이며 반복문 이외에도 조건문을 추가로 이용하여 리스트를 생성할 수 있습니다.



위와 같이, 반복문과 조건문을 함께 사용하여 리스트를 생성한 모습입니다.


먼저 반복문을 적어주고, 반복문의 인자가 뒤에 적어준 조건문에 해당하는지를 확인하여 그 인자를 리스트의 요소로 가지게 되는 것입니다.


또한, 위에서 사용한 반복문이나 조건문을 여러개 이용하는 것도 가능합니다.

이때, 여러번 사용되는 반복문은 각각이 개별적으로 돌아가는 것이 아니고 조건문은 서로 and연산으로 묶인다고 생각하시면 됩니다.

아래 예시들을 통해 확인해보도록 하겠습니다.



2-3. 두 개의 반복문 사용하기


먼저, 두개의 반복문이 사용된 예제입니다.



먼저 반복문이 어떻게 돌아가는지 보다 잘 확인하기 위해 a,b,c,d,e 를 가진 리스트 a와 1,2,3,4,5를 가진 리스트 b를 만들어 주었습니다.

그리고 [i+j for i in a for j in b] 라는 Comprehension 구문으로 새로운 리스트를 생성해보았습니다.

해당 결과를 보면 먼저 앞의 for문에서 하나의 요소에 대해 뒤의 for문을 적용하는 방식임을 확인할 수 있습니다.

보다 쉽게 보자면, 아래와 같은 것임을 알 수 있습니다.



위의 코드를 보시면, 우리가 전에 알던 for문의 문법을 이용하여 a, b 리스트에 있는 요소를 하나씩 꺼내어 새로운 리스트에 삽입하는 방식으로 결과를 확인해보았습니다.



2-4. 두 개의 조건문 사용하기


이번에는 두개의 조건문이 사용된 예제를 확인해보도록 하겠습니다.



위의 결과를 보시면 0~49의 범위에서 2로 나눴을 때 0이며, 3으로 나눴을 때 0인 요소로 새로운 리스트가 생성된 것을 확인할 수 있습니다.

즉, 우리가 적어준 두개의 조건문이 서로 and로 묶인 것을 알 수 있습니다.



2-5. 조건문에서 else 사용하기


물론 우리가 사용하는 조건문에 대해서 if 이외에 else도 함께 사용할 수 있습니다. 하지만 else if (elif)는 사용할 수 없습니다.



위의 코드의 첫번째 줄에서 볼 수 있듯이 if와 else를 함께 사용할 수 있습니다.

0~9의 숫자에 따라서 2로 나누어 떨어지면 'even'을, 그게 아니면 'odd'를 출력하게 한 구문 입니다.

하지만 그 아래줄에서 0인 숫자에 대해서는 zero를 출력하도록, elif를 사용해보았지만 오류가 발생합니다.


그렇지만, else를 사용할 수 있기때문에 elif를 직접적으로 사용할 수는 없지만, 개념적으로 elif와 동일한 구문을 사용할 수는 있습니다.



2-6. 조건문에서 elif 사용하기



위와 같이 else 뒤에서 if를 한번 더 사용함으로써, elif와 같은 기능을 갖는 구문을 만들 수 있습니다.

첫 번째 줄에서는 0이 2로 나누어떨어진다고 보기 때문에 맨 앞에 있는 if 문에 걸려 even을 출력하게 되었습니다. 이를 수정하여 두번째 코드와 같이 작성하면 우리가 기대한 값이 출력됨을 볼 수 있습니다.

물론 이러한 경우에도 else를 여러번 중복하여 사용할 수 있습니다.



위의 코드에서는 0~9의 숫자에 대해서, 1일때는 'one', 2일때는 'two', 3일때는 'three', 4일때는 'four'를 출력하도록 여러개의 else와 if를 사용하였습니다. 그리고 그외에는 'hum'을 출력하도록 설정하였습니다.



3. Generator Expression


이번에는 Generator Expression에 대해서 알아봅니다.

지금까지 Comprehension에 대해서 알아보다가 갑자기 다른 주제라서 당황하셨나요?

하지만 Generator Expression도 Comprehension를 사용한 기능 중에 하나입니다.


처음에, Comprehension도 리스트말고도 {}를 사용하는 집합 자료형과 키와 값을 이용한 딕셔너리 자료형에서도 이용할 수 있다고 말씀드렸습니다.

그리고 이와 비슷하게, ()를 사용하면 Generator Expression이 되는 것 입니다.

표현하는 방법 자체는 위에서 알아본 Comprehension과 동일하니 어떻게 사용되는 것인지만 예제를 통해서 알아보도록 하겠습니다.



먼저 a와 b에 우리가 위에서 알아본 Comprehension 의 구문과 소괄호, ()를 이용하여 정의하였습니다. 그리고 각각을 print해보니 generator 객체가 나왔습니다.

이처럼 Comprehension 구문을 사용하여 소괄호로 묶어주면 자동적으로 파이썬에서 generator expression으로 인식하여 generator 객체를 생성하게 됩니다.

이를 사용하기 위해서는 해당 generator객체를 next로 감싸서 출력해주면 됩니다.


우리가 Comprehension를 통해 기대하는 값들이 순서대로 하나씩 출력되는 것을 볼 수 있습니다. 그리고, 모든 값에 대해 한바퀴를 돌게 되면 아래와 같이 Stop Iteration 이라는 오류를 출력하게 됩니다.




4. Comprehension 정리


우리는 위에서 Comprehension에 대해서 알아보았습니다.

그 구문과 사용법을 익히면, list, set, dict과 같은 자료형에서 다양하게 사용될 수 있으며, 소괄호를 이용하여 generator 객체를 만들어 이용할 수도 있습니다.

이러한 구문이 나왔을 때 당황하지 않고 리딩할 수 있어야 하며, 필요할때는 적극적으로 이용할 수 있어야 하기 때문에 꼭 한번씩 인터프리터등을 이용해 연습해보시기를 추천드립니다.

마지막으로 Comprehension 구문에 대해서 총정리를 해보면, 아래와 같습니다.




블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc


안녕하세요. 문범우입니다.

이번 포스팅에서는 파이썬에서 사용되는 언더스코어(_)에 대해서 정리해보고자 합니다.

언더바라고도 읽는, ' _ ' 와 같은 표시를 언더스코어라고 하는데 이것이 파이썬에서는 보다 특별한 의미를 가지고 사용되는 경우가 있습니다. 어떤 의미로 어떻게 사용되는지 하나씩 알아보도록 하겠습니다.


1. 값을 무시하고 싶은 경우


제일 먼저 알아볼 경우는 값을 무시하고 싶은 경우입니다. 물론 쓰레기 변수를 만들어서 사용해도 되겠지만 언더스코어를 사용하는 경우 더 깔끔하고, 코드를 확인할 때 사용하지 않는 값임을 나타내기 때문에 보다 직관적일 수 있다고 생각합니다.

사실상, 값을 무시하는 경우가 언제 필요할까? 라는 생각이 드시는 분도 있겠지만 간단하게는 아래와 같은 코드에서 값을 무시할 경우가 생깁니다.



예제가 깔끔한지는 의문이 들지만..

대략 무슨 느낌인지 아시겠나요?

우리가 for 문을 사용할때 지정되는 인덱스가 굳이 필요 없을때가 있습니다. 이럴때 언더스코어를 사용하며 또는, 우리가 함수 등에서 한번에 두개의 값을 반환받을 때 둘 중 하나의 값만 이용하고 싶다면, 아래와 같이 사용할 수 있습니다.



즉, 우리가 원래 변수로 사용하던 곳에 대신하여 언더스코어( _ )를 사용하면 그 값은 무시하게 되는 것 입니다.



2. 숫자 자리수 구분을 하는 경우


숫자의 자리수 구분으로 사용하는 경우는 간단합니다. 우리는 보통 1000 대신 1,000와 같이 반점을 이용해 숫자를 세자리씩 구분하는데 이러한 역할이라고 생각하면 됩니다.




3. private으로 선언하고 싶은 경우


이번에는 private으로 선언하고 싶은 경우입니다. 물론 변수/메소드/클래스 등 모든 것을 포함하는데, 사실상 이는 private 이라고 보기에는 약간 애매할 수 있습니다.

예들 들어, 아래와 같이 함수와 변수를 선언했다고 생각해보겠습니다.


물론 이후에, _hello() 함수를 실행시키거나 _private_var를 출력해도 결과가 올바르게 나옵니다. 그럼 무엇때문에 private 선언이라고 하느냐?

예를 들어, 위의 코드를 hello.py 라는 이름으로 저장하고, 다른 파일에서 이를 불러온다고 생각해봅시다.

그럼 우리는 from hello import * 와 같은 식으로 모듈을 불러오게 됩니다.

하지만, 이때 언더스코어( _ )로 시작된 함수나 클래스, 변수는 사용할 수 없게됩니다. 사용할 수 없다기보다 불러와지지 않는다는 말이 맞을듯 합니다.

즉, 외부에서 모듈을 불러올때 언더스코어로 시작된 변수나 함수, 클래스는 사용하지 못하는 것입니다.

하지만.. 이 또한 사용할 수 있는 방법이 있기는 있습니다.

import 를 할때 * 를 사용해서 하는 것이 아니고, 직접 함수나 변수명을 언급하여 import를 하면 사용할 수 있게됩니다.


완벽하게 private이라고 볼 수는 없겠지만, 숨기고 싶은 함수나 변수, 클래스를 만들때는 직관적으로라도 이해하기 위해 언더스코어를 사용하여 네이밍하는 것이 좋을 것 같습니다.



4. 중복된 이름을 피하고 싶은경우


이는 맹글링을 사용한다고 표현하기도 합니다.

맹글링이란 프로그래밍 언어 자체적으로 가지고 있는 규칙에 의해서 함수나 변수의 이름을 변경하는 것을 말합니다. 즉, 파이썬에서 가지고 있는 규칙에 의해서 우리가 네이밍한 변수나 함수의 이름을 변경하는 것 입니다.

위와 같이 코드가 구현되었을 때, A 클래스와 B 클래스의 두개의 함수명이 서로 같습니다. 물론 잘 구분해서 사용한다면 문제가 없겠지만, 코드가 복잡해지고 변수이름이 다양해졌을때에는 충분히 헷갈릴 수 있게됩니다.

이럴때 각 클래스에 대해서 중복된 함수명을 우리가 만들더래도, 맹글링을 통해 알아서 구분되는 함수명으로 변경시키기 위해 더블언더스코어( __ )를 사용합니다.

즉, 다음의 코드와 같습니다.

위와 같이, 함수를 네이밍할때 그 앞에 더블언더스코어( __ )를 사용하였습니다.

그리고 각 함수를 호출하기 위해서는 하단에서 보이는 것처럼 _<클래스명><함수명>과 같이 호출해야 합니다.

즉, 같은 함수명을 가졌더라도 함수를 호출할 때 직접적으로 클래스명을 언급해주어야 하므로 코드를 이해할때 보다 직관적으로 이해할 수 있습니다.



+ 인터프리터에서 사용할 때


마지막으로 알아볼 것은 언더스코어를 인터프리터에서 사용할 때 입니다.

인터프리터에서는 마지막 변수의 값을 일시적으로 가지고 있는 용도로 사용됩니다.


위와 같이, 마지막으로 실행된 결과 값이 언더스코어에 저장되는 것을 볼 수 있습니다.

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc


안녕하세요.

이번 포스팅에서는 파이썬에서 정규 표현식을 지원하는 re모듈, 정규식을 이용한 문자열 검색과 정규식 컴파일 수행시 가능한 옵션에 대해서 알아보도록 하겠습니다.

지난 포스팅에서도 말씀 드렸듯, 정규표현식은 단순히 파이썬에서만 사용되는 것이 아닙니다. 기본적인 정규표현식을 익혀두시면 그 쓰임새는 무궁무진하며, 단순히 프로그래밍 언어에 따라 사용법만 약간의 차이가 있을 뿐 입니다.



1. re 모듈


파이썬에서는 정규 표현식을 사용하기 위해 re(regular expression) 모듈을 제공합니다. 파이썬이 설치될 때 함께 설치되는 라이브러리로 단순히 import 하여 사용할 수 있습니다.


import re


그리고 re모듈을 이용하여 우리가 작성하는 정규표현식을 컴파일합니다.

여기서 컴파일한다는 의미를 쉽게 생각해본다면, 우리가 작성한 정규표현식을 이해하라고 명령을 내린다고 생각해 볼 수 있습니다.

예를 들어서, 아래와 같이 코드를 작성했다고 생각합니다.


1
2
import re
= re.compile('[a-z]')
cs


1번 줄에서 re 모듈을 불러 왔고, 2번 줄에서 re모듈의 compile함수를 사용합니다. 그리고 해당 함수의 인자로 정규표현식을 넣어주었습니다.

이렇게 하면 정규식을 컴파일한 '패턴'이라는 객체가 나오게 됩니다.

즉, p는 [a-z]라는 정규식을 이해한 객체, 패턴이 되는 것입니다.

그리고 우리는 해당 객체 (위에서는 p)를 이용하여 문자열 검색을 수행할 수 있습니다.



2. 문자열 검색하기


정규식을 컴파일한 패턴 객체를 이용한 문자열 검색을 하는데 총 4가지의 메서드가 제공됩니다.


 Method

목적 및 기능

 match()

 문자열의 처음부터 정규식과 매치(일지)하는지 확인한다.

 search()

 문자열 전체를 검사하여 정규식과 일치하는지 확인한다.

 findall()

 정규식과 매치되는 모든 문자열(substring)을 리스트로 반환한다.

 finditer()

 정규식과 매치되는 모든 문자열(substring)을 iterator 객체로 반환한다.


이때, match()와 search()는 정규식과 매치될 때, match 객체를 반환하고 그렇지 않을 때는 None을 반환합니다.


하나씩 코드를 보며 확인해보도록 하겠습니다.



- match()


위에서 언급했던 것 처럼, match() 함수는 문자열에 대해 처음부터 검사하여 정규식과 매치될 때 match 객체를 반환하고 그렇지 않으면 None을 반환합니다.

이때, match 객체로 반환되었다면 group() 함수를 통해 매치된 문자열을 확인할 수 있습니다.

match는 search와 다른점을 구별하기 위해서, match 는 문자열의 처음부터 검사한다는 것을 기억하시길 바랍니다.



위의 코드에서와 같이, 먼저 p 에 정규식 '[a-z]+' 을 컴파일 하였습니다.

그리고 총 3개의 테스트를 진행해보았습니다.


첫번째 테스트에서는 itsfine 이라는 문자열 모두가 정규식에 매치되어 그대로 반환되었습니다.

두번째 테스트에서는 3이라는 숫자로 시작하는 문자열이기 때문에 애초 3부터 정규식과 매치되지 않아 None을 반환하였고, None에 대해 group() 함수는 오류를 발생시킵니다.

세번째 테스트에서는 중앙에 있는 숫자 4의 위치전까지의 what 이라는 문자열이 정규식에 매치되어 그 값을 반환할 수 있었습니다.



- search()


search() 함수는 문자열 전체를 검사하여 정규식과 매치되는지 확인합니다.

match() 함수와 거의 동일하지만, match 는 문자열 처음부터 검사하고, search는 문자열 전체를 검사하는 차이가 있습니다.

이 차이가 실질적으로 어떤 차이가 있을지 코드를 통해 확인해보도록 하겠습니다.



위에서 확인해본 match() 함수에서 사용한 코드에서 단순히 검색 메소드를 search() 로 바꾸어보았습니다.

아까는 오류가 났었던 두번째 테스트에서 fail을 잘 반환해주고 있습니다.

즉, match() 함수는 문자열 처음부터 검색을 시작하다가 정규식과 매치되지 않는 문자열이 발견되면, 검색을 멈추고 지금까지 매치된 것을 반환하게 됩니다.

이와 다르게 search() 함수는 정규식과 매치되지 않는 문자열이 발견되더라도 지금까지 매치된 문자열이 없으며 탐색을 계속합니다. 세번째 테스트에서 확인할 수 있듯이, 매치되지 않는 문자열이 발견되었는데, 그 전에 매치된 문자열이 있었다면 match() 와 동일한 행동을 취하게 됩니다.



- findall()


이번에 알아볼 함수는 문자열 전체에서 정규식과 매치되는 모든 문자열을 리스트로써 반환하는 findall() 함수 입니다.

위에서 확인했던 테스트들과 동일한 값으로 테스트를 진행해보았습니다.



차이점이 보이시나요?

일단 findall() 함수는 특정 객체가 반환되는 것이 아니고, 정규식과 일치되는 모든 문자열을 리스트로 묶어서 반환해주기 때문에 group() 과 같은 함수를 사용할 필요가 없습니다.

또한 세번째 테스트를 확인해보면 match() 와 search()에서는 반환되지 않았던 'o'가 반환되는 것을 볼 수 있습니다.



- finditer()


finditer() 함수는 findall() 함수와 기능적으로 동일하지만 반환하는 형태가 다릅니다.

코드를 통해 어떻게 다른지 확인해보겠습니다.


위의 코드를 보시면, finditer() 함수는 findall() 함수와 같이 정규식과 매치되는 모든 문자열을 반환하지만 리스트형이 아닌 iterator객체로 반환합니다.

이를 확인하기 위해서 for 문을 이용하였고, 세번째 테스트를 보시면 그 결과를 확인할 수 있습니다.

해당 iterator 객체에서 문자열을 꺼내기 위해서는 match() 와 search() 에서 이용했던 group() 함수를 이용하면 됩니다.



+ 모듈로 한번에 실행하기


지금까지 우리는 아래 두줄의 코드와 같이 p 라는 것을 re.compile() 에 대한 객체로 두고, 해당 객체를 통해 검사 메소드를 실행시켜 m 객체를 얻었습니다.


p = re.compile('[a-z]+')

m = p.match('hello')


하지만 단순히 모듈에서 실행할 수 있는 방법이 있습니다. 즉, 위의 두줄 코드를 한줄로 줄일 수 있는 방법입니다.


m = re.match('[a-z]+', 'hello')


이와 같이 작성해도 위에서와 같은 결과를 반환합니다.

물론, match 이외의 나머지 함수또한 동일한 방법으로 사용가능합니다.



3. 컴파일 옵션


우리는 위에서 정규식을 컴파일하고, 특정 함수들의 특징에 따라 문자열에 대해 검색하는 방법에 대해서 알아보았습니다.

추가적으로 알아볼 내용은, 정규식을 컴파일할 때 추가할 수 있는 옵션들에 대해서 입니다.

추가할 수 있는 옵션으로는 총 4개가 있습니다.

옵션을 사용하는 방법으로는, 컴파일 함수에 두번째 인자로 옵션값을 입력해주면 됩니다.

예를 들어,


p = re.compile('[a-z]+', DOTALL)


과 같은 식으로 DOTALL 옵션을 사용할 수 있습니다. 이후 검색메소드를 사용하는 등의 행위는 그전과 동일합니다.


하나씩 천천히 알아보도록 하겠습니다.



- DOTALL or S


우리가 지난 포스팅에서 메타문자에 대해서 알아볼 때, dot( . )이라는 메타문자는 줄바꿈(\n) 을 제외한 모든 문자에 대한 의미라고 설명드렸습니다.

이때, 줄바꿈 조차 포함을 하기 위해서는 DOTALL 또는 S 컴파일 옵션을 사용하면 됩니다.


ex)

p = re.compile('[a.b]', re.DOTALL)

또는

p = re.compile('[a.b]', re.S)



- IGNORECASE or I


해당 옵션은 문자의 대소문자를 무시하는 옵션입니다.

즉, 원래대로라면 알파벳에서 대소문자 모두를 포함한 문자열을 의미하는 정규식은, '[a-zA-Z]' 이었겠지만, 해당 옵션을 사용하면 단순히 '[a-z]' 를 사용해도 됩니다.


ex)

p = re.compile('[a-z]', re.IGNORECASE)

또는

p = re.compile('[a-z]', re.I)



- MULTILINE or M


해당 옵션은 우리가 추후에 더 알아볼 메타문자 ^ 와 관련이 되어 있습니다.

옵션에 대한 설명을 위해 간단히 말씀드리면, 메타문자 ^ 는 문자열의 처음(시작)을 의미합니다. 즉 정규식 '^hello' 라고 한다면, hello 로시작하는 문자열을 의미하는 것입니다.

그런데 일반적으로 '^hello\s\w+' 라는 정규식을 이용하면

'''

hello python

Im beomwoo

hello everyone

'''

과 같은 여러개의 줄로 이루어진 문장에 대해서 첫번째 문장인 hello python 만을 반환하게 됩니다. findall() 함수를 사용해도 첫번째 문장만 반환하게 되는데 그 이유는 위의 문단이 결국 'hello python\nIm beomwoo\nhello everyone' 과 같은 문장이기 때문입니다.

이때 MULTILINE 옵션을 사용하면 각 문장에 대해서 정규식에 대해 검색을 진행합니다.

즉, MULTILINE을 사용한 컴파일에서 findall 메소드를 사용한다면 위의 문단이 반환하는 리스트는, ['hello python', 'hello everyone'] 이 될 것입니다.


ex)

p = re.compile('^hello\s\w+', re.MULTILINE)

또는

p = re.compile('^hello\s\w+', re.M)



- VERBOSE or X


해당 옵션은 특정 기능에 대한 것이라기 보다는 정규식을 보다 시각적으로 좋게 보기 위한 옵션입니다.

예를 들어서,

charref = re.compile(r'&[#](0[0-7]+|[0-9]+|x[0-9a-fA-F]+);')

이러한 정규식이 있다고 생각해봅시다. 아직 우리가 정규식에 대해 100% 공부를 하고 이해가 된것이 아니라는 것을 감안해도, 해당 정규식을 제대로 이해하기 어려운 부분이 있습니다. 하지만 VERBOSE 또는 X 정규식을 사용하면, 아래와 같이표현이 가능합니다.


charref = re.compile(r""" &[#] # Start of a numeric entity reference ( 0[0-7]+ # Octal form | [0-9]+ # Decimal form | x[0-9a-fA-F]+ # Hexadecimal form ) ; # Trailing semicolon """, re.VERBOSE)

아직 우리는 이렇게 되어있어도 이해가 어려울 수 있지만 나중에 정규식을 더 공부하고나면 위의 코드가 더 이해하기 편할 것입니다. 또한 # 을 통해 주석을 쓸 수 있기 때문에, 보다 쉽게 코드를 이해할 수 있습니다.



이렇게 해서 정규식에 대한 두번째 포스팅을 진행하였습니다.

이번 포스팅에서는 정규식에서 사용되는 4가지 메소드와 4가지 컴파일 옵션에 대해서 알아보았습니다.

추가적으로 궁금한 사항은 댓글 및 이메일을 이용해주세요 :)

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc

안녕하세요.

이번 포스팅에서는 파이썬을 이용하여 링크드 리스트를 구현해보도록 하겠습니다.



1. 링크드 리스트(Linked list)란?


먼저 링크드 리스트가 무엇인지 간단히 살펴보도록 하겠습니다.



링크드 리스트는 위와 같이 세개의 형태를 가지고 있습니다.

그림에서 두번째, 양방향 연결 리스트는 *Prev 가 자신보다 앞의 요소를 가르키는 것입니다.

단순히 단방향 연결리스트를 구현하면 어떤 요소의 앞의 요소를 탐색하기 위해서 결국 다시 처음부터 검색을 진행해야 하는 일이 발생할 수 있기 때문에 수행능력이 보다 안좋을 수 있습니다.

이러한 것을 보완하기 위해 양방향 연결 리스트 및 환형 연결 리스트라는 개념이 있는데, 이들은 단방향 연결 리스트보다 구현하기는 상대적으로 어려울 수 있지만 앞에서 말씀 드린 상황과 같을 때 보다 효율적으로 수행될 수 있습니다.



2. 단방향 연결 리스트 구현하기


이번 포스팅에서는 단방향 연결 리스트만 구현해보도록 하겠습니다.

기본적으로 Node 클래스와 LinkedList 클래스를 구현하였습니다. Node 클래스는 단순히 새로운 노드를 만들어 리스트에 삽입할때 사용됩니다.

LinkedList는 insert, delete, search, print, listNum 의 함수를 가집니다.

insert는 사용자가 지정한 값을 리스트의 맨 뒤에 삽입시키며 delete는 첫 번째 요소를 출력하며 이를 삭제합니다. search는 사용자가 지정한 값이 리스트의 몇번째에 있는지 탐색합니다. print는 현재 리스트를 출력해주고 listNum은 리스트의 요소가 몇개인지 출력합니다.


이를 구현한 전체 파이썬 코드는 아래와 같습니다.

LinkedList 자료구조 코드 보기


추가적으로 궁금한 사항이나 잘못된 점이 있으면 댓글을 남겨주세요 :)

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc

안녕하세요.

이번 포스팅에서는 파이썬으로 큐 자료구조 구현에 대한 내용을 소개해드리도록 하겠습니다.


1. 큐(Queue)란?


먼저 큐 자료구조에 대해서 간단하게나마 알아보도록 하겠습니다.



큐 자료구조는 위의 그림과 같이 요소(item)을 삽입하는 Enqueue 기능과 요소를 빼내는 Dequeue 기능이 있습니다.

그리고 처음에 삽입한 요소가 먼저 빠지게 되는 First In First Out(FIFO) 특징을 가지고 있습니다.



2. 큐(Queue) 구현하기


제가 파이썬 코드로 구현한 큐 자료구조는 위에서 말씀드린 Dequeue 기능과 Dequeue 기능을 포함한, 큐가 비어있는지 확인하는 isEmpty 기능을 구현하였으며 추가로 큐가 비어있을때 Dequeue를 수행하면 에러메세지와 함께 False 값을 리턴하게 하였습니다.


완성된 코드는 아래와 같습니다.


Queue 자료구조 코드 확인하기



추가적으로 궁금한 사항은 댓글을 이용해주세요.

블로그 이미지

Tigercow.Door

Data-Analysis / AI / back-end / Algorithm / DeepLearning / etc