2019년 7월 13일 토요일

#45 알고리즘 연습 JadenCase 문자열 만들기 python


JadenCase 문자열 만들기


문제 설명
JadenCase란 모든 단어의 첫 문자가 대문자이고, 그 외의 알파벳은 소문자인 문자열입니다. 
문자열 s가 주어졌을 때, s를 JadenCase로 바꾼 문자열을 리턴하는 함수, solution을 완성해주세요.
제한 조건
  • s는 길이 1 이상인 문자열입니다.
  • s는 알파벳과 공백문자(" ")로 이루어져 있습니다.
  • 첫 문자가 영문이 아닐때에는 이어지는 영문은 소문자로 씁니다. ( 첫번째 입출력 예 참고 )
입출력 예
sreturn
3people unFollowed me3people Unfollowed Me
for the last weekFor The Last Week




문제풀이 IDEA

글자의 첫번째만 대문자로 작성해줘야하므로
1. 첫글자가 알파뱃인지 판단한다
2. 새로운 단어가 시작되는지 판단해준다.

이렇게 나눌 수 있겠다

따라서
1번의 경우에는 해당문자가 알파뱃범위에 있는지 판별해주고
2번의 경우 공백의 경우 조건문이 동작하지 않도록 장치를 설정해준다.



나의 코드


def solution(s):
answer = [] #문자열은 변경 불가능 하므로 list를 이용하여 답을 작성해준다
flag = 1 #공백다음으로 오는 단어의 첫글자를 판별한다
for i in s:
if "A" <= i <= "z" and flag == 1: #i가 알파벳이고 flag= 1 즉 공백 다음이면(시작값은 1이다)
answer.append(i.upper()) #해당 값을 upper해서 list에 추가하고
flag = 0 #flag를 0으로 만들어 첫글자가 아님을 알려준다
else:
answer.append(i.lower()) #첫글자가 아니므로 lower로 만들어주고 append해준다
flag = 0 #이 경우에도 flag를 0으로 해준 이유는 만약 공백이후에 알파벳이 안오는경우
#else문이 돌면서 append를 해주지만 flag를 0으로 안하면 계속 1이 설정되어있으므로
#Hello 3World 이런식으로 숫자다음 알파벳도 대문자화 된다
#따라서 flag를 0으로 하여 위왁 같은 case를 막았다

if i == " ": #공백이 발견되면 flag를 1로 만들어 다음글자가 단어의 시작일지 모른다는 신호를 준다
flag = 1

return "".join(answer) #생성된 list를 str로 만들어준다.


다른풀이의 주요 개념


str.title() 단어들의 첫글자를 대문자로 변경해주지만 공백 2개 or 숫자 시작같은경우 올바르지 못한 출력을 보인다.

그리고 split을 이용해 나눈 case가 많이 보이는데 split을 써서 나누게 되면 공백을 기준으로 나뉘게 되므로

공백이 2개 이상 있는 경우 처리가 힘들어진다.
따라서 간편한 방법이지만 공백이 1개인 문자열에서 밖에 못쓰다는 단점이 있다.





출처 : 프로그래머스

2019년 7월 12일 금요일

# 5 자료구조&알고리즘 Stack

Linear Structure



Stack, Queue, deque and data collections list

위의 구조들은 아이템이 추가될 때 다른아이템 이전에 오는지 이후에 오는지에 따라 구별된다.

선형구조는 끝을 가지고 있는데,
제거(remove)와 추가(add) 과정에서 끝(end)를 선택하는 방식에 따라 데이터 구조가 달라진다.

이러한 구조들은 많은 알고리즘과 함께 다양한 문제를 해결하는데 쓰인다


Stack ( or push-down stack )



아이템의 추가와 제거가 Top에서 이루어지는것 ( Top 의 반대는 Base라 한다)

따라서 LIFO ( last - in first-out ), 후입선출이라고 하기도 하는데
최신의 아이템이 Top, 오래된 아이템이 Base에 가까운 구조이다

이러한 구조는 책이 쌓여 있는 모양과도 같으며 다른 책을 확인하기 위해서는 Top position의 책을 제거해야한다

따라서 책을 쌓기 시작한 순서와 책이 제거되는 순서는 반대인데 이러한 아이디어가 Stack의 핵심이다.
(예를 들어보면 1번책 2번책 3번책 순으로 쌓았으면 제거는 3번책 2번책 1번책)

==> 삽입순서와 제거순서가 서로 역순의 관계이다.

     e.g) 컴퓨터에서 이러한 특징은 web page의 back 버튼으로 알수있는데
           page이동이 stack으로 저장되어 지금 보고 있는 page가 Top,
           처음 본 페이지가 Base에 위치하게 된다.


Stack Operations



    - Stack()      : 빈 스택을 생성                       parameter : X       return X

    - push(item) : 새로운 item을 top으로 넣는다.   parameter : item   return X

    - pop()        : top으로 부터 item을 제거한다.   parameter : X      return : item

    - peek()       : top의 item을 확인, 제거아님      parameter : X      return : item

    - isEmpty()   : stack이 비어있는지 확인한다.     parameter : X      return : boolean

    - size()        : stack의 item갯수를 return         parameter : X      return : integer


Stack 의 구현



여느 OOP와 마찬가지로 stack의 ADT 구현은 class의 생성으로 할수 있고
stack의 operation은 method로 구현할수 있다. 

우리는 강력하고 단순한 python의 collections, list를 이용해서 구현해보도록 하자

class Stack:

def __init__(self):
self.object = [] #여기서 self.object는 Stack을 객체변수로 설정해 준것
def isEmpty(self):
return self.object == []
def push(self, item):
self.object.append(item)
def pop(self):
return self.object.pop()
def peek(self):
return self.object[-1]
def size(self):
return len(self.object)

a = Stack() #빈 Stack의 생성
print(a) # <__main__.Stack object at 0x000001F31F821860>

a.push(10) #Stack에 10이라는 item을 넣어줌
print(a.isEmpty()) # False

a.push(20)
print(a.size()) #10,20 push 했으므로 2


print(a.peek()) #top에 20이 위치해 있으므로 20

print(a.size()) #peek 메서드를 사용했으므로 size에는 변화없다 2

print(a.pop()) #top에 위치한 20이 제거된다

print(a.size()) #20이 제거되고 Stack에는 10만 남게 된다.


논리적인 특성을 유지하면서 다른 방법으로 stack을 구현할수 있는데
똑같이 작동한다 해도 성능에서 차이가 날 수 있으므로 효율적인 구현을 하는게 좋다.

e.g )

     1. list의 end부분을 top으로 사용하는 경우 ==> pop() 과 append()가 모두 O(1)의 복잡도

     2. list의 first 부분을 top으로 사용하는 경우== > pop(0) 과 insert(0)은 O(n)의 복잡도

     따라서 같은 논리적 작업을 한다해도 1번의 구현이 훨씬 더 효율적이게 된다.

2019년 7월 7일 일요일

#데이터분석 Pandas Reperence

pandas

Pandas

  • Pandas란 오픈소스로 높은 성능과 사용하기 쉬운 데이터 구조를 제공해준다
  • Python language로 이루어진 데이터 분석 tool이다.

Pandas 시작

import pandas as pd  #pandas 를 import하고 pd라고 줄여서 사용

Pandas 사용하기

데이터 입출력

  • data.to_csv('name.csv') data를 csv파일로 작성
  • my_data = pandas.read_csv('name.csv) csv 파일을 읽어옴
  • 위의 csv는 각각 excel,hdf 로 바뀌어 사용 할수 있다

Series(Pandas의 자료구조)

특징

  • index를 지정해 줄 수 있다.
  • 지정하지 않을시 default값인 1,2,3,4가 지정된다
  • mutable하다(index, value의 변경이 가능하다)

생성

  • Series의 생성은 list자료형과 함께 index를 list의 형태로 넘겨주거나
  • dictionary type을 Series로 변경해주면 key값이 index로 value가 value로 간다.
my_series = pd.Series([1,2,'good',True], index = ['하나','둘','셋','넷'])  
                      
print(my_series)

print("*"*30)

my_series2 = pd.Series({'첫째': 1, '둘째': 2, '셋째': 3})

print(my_series2)
하나       1
둘        2
셋     good
넷     True
dtype: object
******************************
첫째    1
둘째    2
셋째    3
dtype: int64

Series의 property

  • series.values Series의 value
  • series.index Series의 index (start는 포함, stop 이전)
  • series.index.name index들의 name을 붙여줄 수 있다.
print(my_series.index) # iterable한 index object를 가져온다
print("")
print(my_series.values)
print("")
my_series.index.name  ="index_col"
print(my_series)
Index(['하나', '둘', '셋', '넷'], dtype='object', name='index_col')

[1 2 'good' True]

index_col
하나       1
둘        2
셋     good
넷     True
dtype: object

Data Frame(Pandas의 자료구조)

특징

  • 행의 index는 index로 열의 index는 columns으로
  • mutable( 값의 변경이 가능하다)
    생성
  • python의 dictionary, numpy 의 array로 생성가능
  • dictionary로 생성시 key값이 열의 이름으로 들어간다
  • 초기에 columns과 index(row)를 설정하여 생성할 수 있다.
    • DataFrame(data, 설정, 설정)의 모양,
    • 없는 index(columns)일때 NaN(Not a Number)으로 value를 채운다.
    • 이미 있는 index일경우 순서를 설정에 맞춰준다
mydata = {'name' : ['jang','kim','lee','park'],'age':[25,23,27,21]}
mydf = pd.DataFrame(mydata)
mydf2 = pd.DataFrame(mydata,index=['하나','둘','셋','넷'],columns=['age','name','bool'])

print(mydf)
print("*"*20)
print(mydf2)
   name  age
0  jang   25
1   kim   23
2   lee   27
3  park   21
********************
    age  name bool
하나   25  jang  NaN
둘    23   kim  NaN
셋    27   lee  NaN
넷    21  park  NaN

Data Frame method & property

  • dataframe.values Data Frame의 value들
  • dataframe.index 행의 index들
  • dataframe.index.name 행의 이름을 설정해줄 수 있다.
  • dataframe.columns 열의 index들
  • dataframe.columns.name 열의 이름을 설정해줄 수 있다.
  • dataframe.describe() 각각의 col에 해당하는 계산가능한 value로 count,평균,최소,최대 등을 표시(NaN은 계산불가)
mydf.index.name = '번호'
mydf.columns.name = "분류"
print(mydf)

mydf.describe() #name은 연산할수 있는 value가 없으므로 표시 X
분류  name  age
번호           
0   jang   25
1    kim   23
2    lee   27
3   park   21
분류 age
count 4.000000
mean 24.000000
std 2.581989
min 21.000000
25% 22.500000
50% 24.000000
75% 25.500000
max 27.000000

DataFrame 조작 Reperence

Columns 편

  • dataframe['colname'] dataframe의 col
  • dataframe[['col','col2'...]] 복수개의 col
  • dataframe['colname'] = value
    • colname 존재 : index의 갯수만큼입력하면 index마다 다른 value대입(1개면 해당 value로 통일된 col생성)

    • colname 부재 : 새로운 열이 추가됨

    • value값에 Series가 오면 DataFrame의 index에 Series의 index를 맞춰 배열한다

  • del dataframe['col] col의 삭제
  • dataframe['col'] 간의 사칙연산, 논리연산자(>,==,&,|) 사용가능하다

Row 편

  • dataframe['start_index' : 'end_index'] list의 indexing과 비슷한 모습
  • dataframe.loc['rowname',<'target col name'>]
    • Series의 모양으로 반환하는데 이때 Series의 index가 사실상은 columns들의 name이 되는것이다,
    • rowname부분에 slice가 들어갈수 있다
    • target부분을 안적어주면 default로 전체를 반환한다
    • target부분에 list모양이면 특정 col만, slice면 해당 범위 col을 가져온다
  • dataframe.loc['rowname',<'target_col'>]= value 새로운 행 삽입
    • loc위치에 at을 사용할수 있고 더 빠르다
  • dataframe.iloc[] loc과 다르게 name이 아닌 index번호를 사용하여 가져옴
    • iloc위치에 iat을 사용할수 있고 더 빠르다
  • dataframe.head(n) 위에서 n개 만큼의 행을 읽어옴, n을 안줄시 default==5
  • dataframe.tail()

공통 편

  • dataframe.drop(name or condition , axis = 0 or 1) 해당하는 행or열 삭제

    • axis가 0이면 row(행)(default)
    • axis가 1이면 col을 drop한다
  • dataframe.sum(axis=0 or 1) axis에 해당하는 계산가능한 value의 합

    • sum이외에도 min,max,count,argmin(max),mean 등의 함수도 적용된다

    위의 axis에서 알수있듯 ↓방향이 axis 0에 해당
    ➝ 방향이 axis 1에 해당한다
    따라서 drop의 경우 ↓로 진행하다 조건에 맞는 row를 발견하면 삭제하고
    sum은 ↓의 방향으로 더해나가는 것이다

  • dataframe.reindex(index = ... , col = ...) row와 col의 index를 다시 배열

  • dataframe.sort_index(axis=0 or 1, <ascending=bool> ) index를 sort한다

    • ascending option을 주지않으면 기본적으로 ascend = True 오름차순으로 된다
  • dataframe.sort_values(by=col,<ascending=bool>) col의 value들을 정렬한다

  • dataframe['row or col'].unique() 행 or 열의 유니크한 값

  • dataframe['row or col'].value_counts() 행 or 열의 value갯수 conunt

  • dataframe['row or col'].isin(value) 행 or 열에서 value가 있는지 bool로 return

  • dataframe.T dataframe을 전치한다
    (행과 열을 바꾼다,(0.0),(1.1),(2.2)…를 이은 선분을 기준으로 좌우대칭으로 교환)

  • dataframe2 = dataframe.copy() dataframe을 복사한다

NaN 편

  • 삭제

    • dataframe.dropna(how='any') 하나이상이 NaN인경우
    • dataframe.dropna(how='all') 모두 NaN
  • 변경

    • dataframe.fillna(value = somevalue) somevalue값으로 NaN값을 변경
  • 확인

    • dataframe.isnull() dataframe전체의 value에 대해 NaN인지 확인하여 bool로 리턴

Pandas의 functions

  • pandas.date_range('date(yyyymmdd',period=n) yyyymmdd부터 n일간의 range를 만들어준다
  • ```pandas.to_datetime(‘yyyymmdd’)```` yyyymmdd에 해당하는 Timestamp를 생성

Pandas 응용

Apply

  • dataframe.apply(function) data에 함수를 적용한다
    • 이때 함수는 lambda function형태로 작성가능하다(lambda x: x.max()+x.min())

연결

  • pandas.concat([dataframe[0:1],dataframe[2:3]] dataframe의 2행을 제거하고 concat으로 연결
  • pandas.merge(dataframe1, dataframe2, on = 'criteria') dataframe1과 2를 on에서 설정한 기준에 따라 merge한다

Grouping

  • dataframe.groupby('value') 특정 value를 기준으로 data를 그룹화한다(행합침)
    • value부분에 list가 들어가 여러 기준이 적용될 수 있는데 앞에서부터 기준을 적용하여 grouping

2019년 7월 3일 수요일

#5 자료구조&알고리즘 - 알고리즘 분석(Big-O Notation)


알고리즘의 분석


✔ 알고리즘을 작성하는데는 어떠한 naming을 하냐도 중요한 요소로 작용한다


알고리즘 분석에 있어 고려해야할 것


    1. 문제를 푸는데 요구되는 메모리 공간

    2. 실행시간


그런데 이러한것을 측정할때 컴퓨터의 성능 및 프로그램, 언어에 의해서 속도가 달라질수 있다

따라서 우리는 공통된 측정방법이 필요하고 그게 바로
Big-O Notation이다.



Big-O Notation



할당문의 갯수를 계산해주면 되는데

이때 data의 규모가 커질수록 step의 수가 증가하고 함수 또한 빠르게 증가하므로
정확한 값보다는 비교를 위한 근사값을 사용한다.



e.g)  n^2+1    , n^2 은 n의 갯수가 증가할수록 뒤에 붙은 1의 영향은 미미해진다
     
       또한 n^2과 2n^2도 n의 갯수가 증가함에 따라 계수는 무시할 수 있을 영향력을 가진다.

       따라서 7n^2 + 10n + 5의 경우 O(n^2)만 유의미한 값으로 근사치를 잡아준다


그런데 이렇게 계산을 함에 있어서 data set의 종류에 따라 best case 가 나타날수도 있고
worst case 가 나타날수도 있는데 우리는 일반적으로 average case를 생각하도록 한다

(n logn 과 n^2 중 누가 더 연산속도가 느린지 확인 할때는 임의로 특정 수를 넣어서
비교를 해주면 된다 예를 들어 n이 10일때 n logn은 10이 되고 n^2는 100이다.
따라서 n^2가 더 느린 것이다.)



Big-O를 이용한 알고리즘 분석 예시


Anagram 이라는 게임을 예로 들어보자.
(Anagram은  대기 - 기대  이와 같이 순서를 바꾸어 단어를 만드는 게임)

1. Checking Off    :    O(n^2)  


   A 단어와 B단어(list로 바꿔준다)에 대해서 A 단어의 0번째 index부터
   B단어의 index들과 차근차근 비교하며  같은 character 를 발견하면 B의 해당 index의
   문자를 None 으로 바꾼다. 만약 찾지 못하면 특정 flag를 false로 변경해주고 return한다
 
   A의 한 index당 B의 index전체를 비교하므로 n*n n^2만큼 비교가 들어가게 된다.



2. Sort and Compare    :  O(n^2) or O(n logn)


   A단어와 B단어를 list화 해준다음 sort를 이용해 정렬을 해주고 두 list의 index를 차례차례 학인해주며
   같은 index에 다른 글자가 오면 다른 글자 조합인게 되므로 False를 반환해준다.

   얼핏 보면 sort후 반복문을 통해 index비교를 해주기 때문에 O(n)만 볼수 있는데
   sort자체가 O(n^2) or O(n logn)를 가지기에 여기에 집중해야한다



3. Brute Force       :     O(n!)

   
   A단어를 구성하고 있는 문자열로 생성할수 있는 모든 단어를 생성한 뒤에 B가 그 안에 있는지 확인해주는 방법

   A단어의 글자수가 n이면 첫째자리에 올수 있는 경우의수 n, 둘째 자리 n-1.... 처럼 이루어 지기때문에 n! 이 오게되고
   n! 이라는 복잡도는 10!만 해도 어마어마해지기때문에 심지어 2^n 보다 안 좋은 효율이다.



4. Count and compare    : O(n)


   A단어와 B단어의 문자수를 저장할 수 있는 list를 생성하여 해당 문자가 발견될때 마다
   list의 count를 늘려간다.
   이 때  A와 B 다른 list를 구성하게 되므로 반복문은 중첩이 아니라 따로 구성이 되고

   A, B의 문자수 list의 생성이 끝나면 반복문을 통해 index를 비교해가며 다르면
   Anagram의 결과가 틀렸다고 해주면 된다.




단 위의 4번과 같은 경우에는 연산속도를 좋지만
메모리 공간을 사용함에 있어서는 추가 list를 만드는 행위 등으로 더 많은 소요가 있다.

따라서 이를 잘 절충하여 코드를 짤 필요가 있겠다.



위의 결과처럼 code의 구성 자체에서 성능의 차이를 불러 올수 있지만 python의 자료구조가 가지고 있는 operator의 차이로 인해 연산속도의 차이가 나는 경우도 있다.

특히 구성되는 과정에서 일반적으로 많이 사용하는 operation의 효율성을 위해 덜 사용되는 operation의 효율이 희생되기도 했다.

따라서 적절한 tool을 사용하는 것은 중요하다


python의 주요 데이터 구조의 시간복잡도


1. List


e.g )   양의정수 list를 만드는 4가지 방법
       

         1.  concat (아주 느림)

           
             list1 = []
             for i in range(100):
                 list1 = list1 +[i]

         2.  append (일반적인 for문으로 만드는 방법)


             list2 = []
             for i in range(!00):
                  list2.append(i)
       

         3.  list comprehension (append에 비해 2배가량 빠름)


             list3 = [ i for i in range(100)]

         4.  list range  (가장 빠름)


             list4 = list(range(100)) 


이처럼 똑같이 list를 만드는데 있어서도 연산속도의 차이가 많이 나기때문에 적절한 tool을 이용하는게 중요하다.


2. Dictionary 


   Dictionary도 List와 마찬가지로 index 와 set에 있어서 O(1)만큼의 효율을 보이나

   하지만 중요한 것은 contain 여부를 확인하는 것도 Dictionary에서는 O(1)이라는 것이다.



시간 복잡도와 관련하여 추가 사항은  Time Complexity Wiki  를 참고하도록 하자.

#44 알고리즘연습 위장 python


위장


문제 설명
스파이들은 매일 다른 옷을 조합하여 입어 자신을 위장합니다.
예를 들어 스파이가 가진 옷이 아래와 같고 오늘 스파이가 동그란 안경, 긴 코트, 파란색 티셔츠를 입었다면 다음날은 청바지를 추가로 입거나 동그란 안경 대신 검정 선글라스를 착용하거나 해야 합니다.
종류이름
얼굴동그란 안경, 검정 선글라스
상의파란색 티셔츠
하의청바지
겉옷긴 코트
스파이가 가진 의상들이 담긴 2차원 배열 clothes가 주어질 때 서로 다른 옷의 조합의 수를 return 하도록 solution 함수를 작성해주세요.
제한사항
  • clothes의 각 행은 [의상의 이름, 의상의 종류]로 이루어져 있습니다.
  • 스파이가 가진 의상의 수는 1개 이상 30개 이하입니다.
  • 같은 이름을 가진 의상은 존재하지 않습니다.
  • clothes의 모든 원소는 문자열로 이루어져 있습니다.
  • 모든 문자열의 길이는 1 이상 20 이하인 자연수이고 알파벳 소문자 또는 '_' 로만 이루어져 있습니다.
  • 스파이는 하루에 최소 한 개의 의상은 입습니다.
입출력 예
clothesreturn
[[yellow_hatheadgear], [blue_sunglasseseyewear], [green_turbanheadgear]]5
[[crow_maskface], [blue_sunglassesface], [smoky_makeupface]]3
입출력 예 설명
예제 #1
headgear에 해당하는 의상이 yellowhat, greenturban이고 eyewear에 해당하는 의상이 blue_sunglasses이므로 아래와 같이 5개의 조합이 가능합니다.
1. yellow_hat
2. blue_sunglasses
3. green_turban
4. yellow_hat + blue_sunglasses
5. green_turban + blue_sunglasses
예제 #2
face에 해당하는 의상이 crowmask, bluesunglasses, smoky_makeup이므로 아래와 같이 3개의 조합이 가능합니다.
1. crow_mask
2. blue_sunglasses
3. smoky_makeup





문제풀이 IDEA


입력되는 form 이 [의상이름 , 의상종류] 이므로 이를 분리해서 dictionary로 저장한다
그런데 이떄 의상의 종류를 key값으로 이름을 value값으로 하여 종류에 따라 구분지어 볼수 있게 한다

그리고 의상종류가 이미 들어와 있으면 해당 key에 대한 value를 갱신해줘야하는데 이때 의상의 이름들을 저장해놓기 위해서 list를 value로 사용했다

마지막으로 최소 1개 이상의 종류를 입으므로 찾아보면
모자 2개 - 바지3개 일때

모자의 경우의수 0 1 2 (안 쓸때, 1번모자, 2번모자)
바지의 경우의 수 0 1 2 3 (안 입을때??, 1번바지, 2번바지, 3번바지)

모자의 경우의수 x 바지의 경우의 수 = > 12개 인데 이때는 모자와 바지를 둘다 안입는 경우도 포함

따라서 -1을 추가적으로 해줘야한다




나의 코드


def solution(clothes):
spy = {} #spy가 입을 옷들을 담아둘 dictionary를 선언한다
answer = 1 #결과값의 경우의 수가 곱연산이 이루어져야하므로 1을 초깃값으로 설정한다

for i in clothes: #입력받은 list를 돌며
if i[1] in spy: #list의 두번째값 즉 옷의 종류가 dictionary안에 있는지 확인하여
spy[i[1]].append(i[0]) #있으면 spy의 key값으로 value list를 불러와 append해주고
else:
spy[i[1]] = [i[0]] #없으면 value값에 옷의 이름을 추가해준다

for k in spy: #dictionary의 key값을 가져와
answer *= len(spy[k])+1 #key에 해당하는 list의 길이 +1 (안입는 경우도 존재하기때문)해서 곱연산해준다

return answer - 1 #둘다 안 입는 경우는 제외한다


다른코드



def solution(clothes):
from collections import Counter
from functools import reduce

cnt = Counter([kind for name, kind in clothes]) #collections 의 Counter 함수를 이용해서 kind의 갯수를 세어
#dictionary의 형태로 kind가 각각 몇개인지 반환해준다
answer = reduce(lambda x, y: x*(y+1), cnt.values(), 1) - 1 #reduce함수를 이용해서 연산을 해줄수 있는데
#맨뒤의 1은 초깃값으로 설정된 1이고
#cnt dictionary에 저장되어있는 value들을 가져와
#갯수 + 1 (안입는 경우는 생각)하여 x에 곱해나가준다
#그리고 마지막으로 전체값에 -1을 해준다
return answer


reduce는 python 3이상의 version부터 내장함수에서 제외되어 빠졌다


collections 의 counter 함수는 iterable한 애들을 받아 몇개씩 있는지 세어
key(종류)와 value(갯수)를 가진 dictionary로 생성해준다


출처 : 프로그래머스

2019년 6월 26일 수요일

#3 Git 분산버전관리시스템 - Basic - git의 명령어(심화)


원격 저장소와의 작업


pull



    원격에서 가져오는 경우도 branch와 같은 원리로 pull이 진행된다



    1 . local의 변경사항이 없으면 그냥 fast-forward 되고

    2 . local의 변경사항이 있고 원격저장소의 변경사항도 있으며 둘의 충돌이 없으면
    그냥 병합커밋이 만들어지고

    3 . 충돌이 있으면 수동으로 해결한다음 commit해줘야한다



fetch   



    pull과 달리 병합과정을 거치지 않고 원격 저장소의 내용만 확인하고 싶은 경우 fetch를 사용한다

    fetch를 실행하면 FETCH_HEAD라는 이름의 branch로 가져오기때문에
    git branch checkout FETCH_HEAD 로 확인해볼 수 있다.


    즉 fetch + merge 는 pull이다



push 


    local에서 원격으로 push 할때는 fast-forward로 처리하도록 해줘야한다

    (즉 로컬이 원격보다 commit이 앞서야한다)

    임의로 원격저장소의 커밋을 바꾸면 그 원격 저장소와 동기화 된 다른 저장소의 커밋에도 영향을 주니 주의하자



Tag


    커밋을 참조하기 쉽도록 이름을 붙이는것


    🔖일반 태그 ( Lightweight tag )
     
        이름만 붙일 수 있다



    🔖주석 태그 ( Annotated tag )

        이름을 붙일 수 있다

        태그에 대한 설명 포함 가능

        서명도 넣을 수 있다

        이 태그를 만든 사람의 이름, 이메일과 태그를 만든 날짜 정보 포함 가능



    일반적으로 release branch에서는 주석태그를 사용하여 상세한 정보를 포함하고

    topic branch에서는 일반태그로 이름만 표시

    태그 이름을 지정하여 checkout 하거나 reset할때 간단히 과거의 상태로 되돌릴수 있다.


    -사용방법-


        commit 후 최신 commit에 대해 사용해준다


    🔖일반 태그


$ git tag <tag name> 
                        commit 에 tagname을 붙여준다 (여러번 달아줄 수 있다)
$ git tag               tag 목록을 볼 수 있다.
$ git log --decorate    tag 정보까지 포함한 이력을 확인 할 수 있다. (그런데 git log만으로도 보인다)


    🔖주석태그


$ git tag -a <tag name> 
                        tag name으로 만들어진 tag에 내용을 추가할 수 있다
$ git tag -am "git tag 내용" <tag name>       한번에 처리 가능하다


    태그 삭제


$ git tag -d <tag name> 


Commit


    --amend 옵션을 지정하여 commit을 하면 같은 브랜치 상에 이전 커밋내용을 수정할수 있다

$ git commit --amend

    commit 에 amend옵션을 주게되면 최근 commit 내용이 뜨는데

    commit massage를 수정해 줄 수 있다.



    이때 add를 하고 commit --amend를 해주게 되면 index에 올라간 내용과 이전의 commit을 합쳐 새로운 commit을 만들어주는 것이 되자만

    index에 올라간 변경사항 없이 그냥 commit --amend를 실행해주면 이전 commit massage에 대해 변경이 가능하다



revert


    commit 삭제하기

    rebase -i 나 reset을 이용해 삭제 할수 있지만 이미 공개된 경우에는 하기 힘들다
    (안전하게 삭제하자)


    따라서 해당 commit의 변경사항을 되돌리는 commit을 만들어서 해결한다

    A -> B -> C -> B'

$ git revert HEAD
                        HEAD는 지금 branch의 최신 commit을 가리키기 때문에 해당 commit을 지우는 commit을 생성한다
   ⭐ 이때 HEAD~ 의 경우에는 ~ 1개당 1단계 전의 commit을 의미한다 즉 HEAD~~은 전전 commit을 의미한다


reset


    필요없어진 commit 삭제


📋 reset의 종류( default는 mixed이다 )


        HEAD의 위치         인덱스            작업트리            사용

soft     변경함             변경 안함         변경 안 함      커밋만 되돌릴때

mixed  변경함               변경함           변경 안 함      변경한 인덱스를 원래대로

hard    변경함               변경함             변경함         최근 커밋을 완전히 버리고 이전으로

$ git reset --hard HEAD~~
                         전전의 commit 으로 완전히 돌아가라는 의미이므로 작업트리뿐만아니라 index도 다 변경되었다
$ git reset --hard ORIG_HEAD
                          만약 실수로 reset을 하여 지워진 경우 reset 실행전의 상태로 돌아가게 하는 명령어
                          ORIG_HEAD가 reset 전의 commit을 참조한다


cherry-pick


    다른 브랜치의 특정 커밋을 복사해 현재 브랜치로 가져올 수 있다.

$ git cherry-pick <특정 commit 번호>
                          지금 HEAD가 위치한 branch에 특정 commit이 추가된다. commit 번호는 git log로 확인할 수 있다


rebase -i


    commit을 편집(수정, 삭제, 통합 )작업을 할 수 있다.

    따라서 push전 commit내용을 정리할때나, 커밋에 누락된 파일을 추가할때 사용

$ git rebase -i HEAD~~  
                                HEAD에서 HEAD~ 까지 커밋이 표시
    상단의 pick을 squash로 바꾸면 해당 commit으로 통합된다
    그런데 이때 주의 해야할것이 최신의 commit으로 통합하는 것이고 squash를 적게 되면 그 이전의 commit이랑만 통합하므로
    여러개의 commit을 통합할시 여러개의 pick을 모두 여러개의 squash로 바꿔줘야 한다
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    edit을 입력해주면 해당 commit을 수정할 수 있다.
    -> 그럼 해당 commit을 수정해준 다음 add , git commit --amend를 해준다
    이 커밋 작업이 종료했다는것을 알리기 위해 git rebase --continue를 실행
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    만약 중간에 충돌이 발생하면 add , git rebase --continue를 바로 실행,
    중지하고 싶으면 git rebase --abort 
$ git reset --hard ORIG_HEAD 
                              reset 과 마찬가지로 rebase전의 commit상태로 돌아갈 수 있다.

2019년 6월 25일 화요일

#2 Git 분산버전관리시스템 - Basic - Branch


Branch 


          H    
    ↗️         ↘️ 
A  ➝   B    ➝  C  ➝ D
    ↘️                   ↗️
          X    ➝   Y 


동일한 소스코드로 협업을 할때 여러사람이 서로 다른 작업을 하게 되면
다른 버전의 code가 만들어지게 되는데 이런 작업을 손쉽게 돕는 기능이 Branch이다

말그대로 나뭇가지처럼 master branch에서 부터 뻗어 나와 우리가 사용하고 싶은
용도로 branch를 만들어 코드를 작성할 수있는 것이다.
(branch는 다른 branch의 영향을 받지 않는다)

이렇게 작업할경우 branch로 작업기록을 중간중간 남기게 되므로 문제가 발생했을 때
원인을 찾고 대책을 세우기 쉬워진다.

이렇게 branch를 나누어 작업을 한 이후에는 merge를 통해 branch를 모은다



참고할 만한 Branch 모델


Git에서는 하는 작업에 따라 자유롭게 Branch를 만들 수 있는데
협업하는 팀원들과 함께 branch의 작성규칙, 통합규칙을 정하는게 좋다


master branch 란?

repository를 처음 만들면 기본적으로 제공되는 branch로 새로운 branch를 만들어
checkout( 브랜치 이동 ) 을 하지 않는 이상 master에서 작업하게 됩니다.


- Main branch


        ✔ master 브랜치는 배포가능한 상태만을 관리, commit시 tag를 사용하여 배포 번호 기록
        ✔ develop 브랜치는 통합 브랜치의 역할을 하며 이 브랜치를 기반으로 개발 진행, 안정적인 상태(app의 모든 기능 정상동작) 유지


- Feature branch or Topic branch


        ✔ 새로운 기능 개발 및 버그 수정이 필요할시 develop branch로부터 분기

        ✔ 개발이 완료되면 develop branch로 병합


- Release branch


        ✔ 모든 기능이 정상적으로 동작하는지 확인
,
        ✔ branch 앞에 'release-'를 붙인다.

        ✔ 최종 버그 수정등의 개발을 수행

        ✔ 모든 준비가 끝나고 배포가능한 상태가 되면 master branch로 병합. commit에 tag번호

        ✔ release에서 발견한 버그사항은 develop branch에도 적용해야하므로 병합작업

- Hotfix branch


        ✔ 배포 버전에 긴급 수정사항이 있을경우 master branch에서 분기하는 branch

        ✔ branch 이름 앞에 'hotfix-'를 붙인다

        ✔ develop branch는 개발 진행중이기때문에 hotfix에 대처하기 힘드므로 master에서 분기해서 처리한다

        ✔ 처리 후에는 develop branch에도 병합해준다




1 . 브랜치 만들기


$ git branch < branch name > 
                                branch 를 만들어 주는 명령어

$ git branch
                       branch 목록을 확인하는 명령어

2 . 브랜치 전환하기


$ git checkout <목표 branch>
                              현재 위치한 branch 에서 목표 branch로 이동< HEAD >가 변경된다.
$ git branch -b <목표 branch>
                               목표 branch를 생성함과 동시에 이동한다

브랜치를 checkout하면 해당 브랜치의 최신 commit사항이 작업트리에 반영된다

 ⇒ 즉 directory에 해당 branch에서 작업한 최신 commit이 반영된다

git log를 통해서 branch의 생성과 commit을 확인해 줄수 있다.


2 - 1 .  Stash

branch의 변경사항에 대해 commit하지 않으면 checkout을 통해
index와 작업 트리에 남아있는 내용들을 다른 브랜치에서 commit할 수도 있다.

그런데 이때 다른 branch에서 변경사항에 대해 충돌되는 부분이 이미 commit되어 있다면
commit을 하지 않았다 할지라도 checkout이 되지 않는다.
이럴때 사용할 수 있는것이 merge 혹은 stash이다


e.g)

A branch  : 1.txt 생성 , 첫번째 줄에 111 입력
$ git branch B 를 통해 B branch 생성
A branch  : 1.txt 의 첫번째 줄을 111222 로 수정  -- commit
$ git checkout B : 1.txt의 첫번째 줄은 111만 있음
                    1.txt의 첫번째 줄을 111333으로 수정 --- commit X
$ git checkout A : Error 발생/ B branch에서 commit되지 않은 111333 부분이랑 A에서 commit된 111222가 서로 충돌을 일으킴
$ git stash 
          위의 명령어를 통해 지금 위치한 branch의 1.txt 파일을 stash 한다
          stash란?
          merge는 아예 확정적으로 commit을 하는거라면 stash는 git내부의 가상의 어떠한 공간에 충돌이 일어나는 file을 넣음
          즉 임시 보관의 개념으로 사용할 수 있다.
$ git checkout A : 이제는 충돌하는 부분 없이 A로의 이동이 가능
$ git stash show                   stash 된 file들을 확인할 수 있다.
$ git stash pop                    stash 된 file을 다시 뽑아내어 git add , commit을 해줄수 있다.

이처럼 commit을 하지 않은 채 branch의 이동간에 conflict가 나는 경우 
우리는 stash를 이용해서 해결할 수 있다.


3 . 브랜치 병합하기


$ git merge <commit 대상>
                            현재 사용중인 branch에 commit내용을 병합해준다.
e.g 현재 (master) branch를 사용중이라고 하면
     git merge mybranch 
     위의 명령어 실행시 mybranch의 변경사항을 master branch로 병합해준다.

3 - 1 . 브랜치의 충돌


A ➝ B ➝ D(master)[HEAD]
         ↘️              
             X ➝ Y(bugfix)

위의 D commit 과 Y commit 이 같은 내용을 서로 다르게 변경한 경우 conflict가 발생할 수 있는데
Git이 자동으로 충돌내용에 대해서 알려주기 때문에 직접 수정해서 다시 commit을 해줘야한다

A ➝ B ➝ D ➝ F(master)[HEAD]
         ↘️          ↗️
             X ➝ Y (bugfix)

그럼 이렇게 F라는 merge commit이 새로 생성되게 된다
위와 같은 방법은 non fast-forward 병합이다.


3 - 2 . 브랜치 병합의 두 방법 ( merge & rebase )


merge - 변경내용의 이력이 모두 그대로 남아있기 때문에 복잡하게 보일 수 있음

rebase -  이력은 단순하지만 원래의 commit이 변경됨.
             정확한 이력을 남겨야 할 필요가 있을때는 사용하면 안됨


fast- forward(빨리감기) 


A ➝ B(master)[HEAD]
         ↘️
             X ➝ Y ➝ Z(bugfix)


A ➝ B   
         ↘️   
             X ➝ Y ➝ Z(master)(bugfix)[HEAD]


master branch가 변경된 이력이 없기 때문에
bugfix는 master branch의 이력을 모두 포함하고 있다

따라서 master브랜치가 빨리 감기된 모양을 가진다.



merge commit(병합 커밋)


A ➝ B ➝ D ➝ E(master)[HEAD]
         ↘️              
             X ➝ Y ➝ Z(bugfix)


A ➝ B ➝ D ➝ E ➝ F(master)[HEAD]
         ↘️                 ↗️
             X ➝ Y ➝ Z(bugfix)

bugfix가 분기한 이후 master의 변경사항이 있는 경우

양쪽의 변경을 모두 가져온 merge commit을 실행

፠ non fast-forward

    fast forward를 할 수 있는 경우에도 master의 branch 확장을 통해
    non fast-forward 병합을 할수 있는데(그래서 F라는 commit 이 추가로 생성되었다)

    A ➝ B➝ F(master)[HEAD]
             ↘️                 ↗️
                 X ➝ Y ➝ Z(bugfix)

    위와 같이 bugfix가 그대로 남기 때문에 브랜치 관리면에서 유용할 수 있다.



rebase(base를 변경한다)


$ git rebase <목표 branch>
                             지금 위치한(HEAD가 있는) 브랜치에 목표 branch의 변경사항을 update해준다  
  만약 file간의 충돌이 발생한 경우에는 직접 수정해준 이후에 commit작업이 아닌 
  git add <수정 file>
  git rebase --continue        명령어를 실행 시켜줘야한다     (rebase 취소는 git rebase --abort)   


A ➝ B ➝ D(master)
         ↘️              
             X ➝ Y(bugfix)[HEAD]


A ➝ B ➝ D(master)  X' ➝ Y'(bugfix)[HEAD]



bugfix branch를 master 브랜치에 rebase 하면
bugfix branch의 이력이 master branch 뒤로 이동한다

( 즉 bugfix branch의 base를 B에서 D로 바꾸어 변경사항도 적용해주는 것이다)

따라서 master branch의 이력를 변경하기 위해 이제는 fast-forward 를 통해 병합해주자


4 . 브랜치 삭제


$ git branch -d <branch name>
                                branch를 삭제하는 명령어










가장 많이 본 글