3.12 연산자 우선 순위와 결합 방향    [doc]    [smts]

3.12.1 연산자 우선 순위

한 수식에 여러 개의 연산자를 사용하는 경우에 연산자들의 우선순위를 고려하지 않을 수 없다. 연산자 우선순위를 정리하면 다음과 같다.


  1. 괄호, 배열, 구조체, 공용체멤버를 나타내는 지시자 (), [], ->, . 은 우선순위가 가장 높다.

  2. 한 개의 피연산자를 갖는 단항연산자(!, ++, -- 등)이 그 다음으로 우선순위가 높다.

  3. 그 다음은 산술 연산자 (+, - *, /, %)이다.

  4. 쉬프트연산자와 비교연산자는 비트연산자보다 연산순위가 높다.

  5. 맨 하위에 대입연산자와 콤마(,)연산자가 있다.


[표 3.12.1] 연산자 우선 순위와 결합 방향

순위

연산자

결합 방향

1

( ) [ ] -> .

2

! ~ ++ -- - + (cast) *(pointer) &(addr) sizeof

3

* / %

4

+ -

5

<< >>

6

< <= > >=

7

== !=

8

&

9

^

10

|

11

&&

12

||

13

?:

14

= += -= *= /= %= ^= |= <<= >>=

15

,


연산자 우선 순위와 결합 방향을 고려하여 프로그래밍하는 것은 프로그래머의 몫이며 프로그래밍은 가독성(간결성)과 효율(길이) 측면에서 고민하게 되는 경우가 자주 발생한다. 한 수식에 부득이하게 연산자를 여러 개를 써야 한다면 괄호 등을 적극 활용하여 우선 순위를 명확히 하는 것이 좋다.


3.12.2 연산자 결합 방향

한 수식에서 동일한 우선순위를 가진 연산자들이 연속적으로 나타나 있으면 연산자의 결합 방향을 고려하여야 한다. 대부분의 연산자들은 왼쪽에서 오른쪽으로 결합을 한다. 하지만 반대로 오른쪽에서 왼쪽으로 결합하는 연산자가 있다.


  1. 단항연산자, 형변환연산자, sizeof등은 오른쪽에서 왼쪽으로 결합한다.

  2. 조건 연산자 (?) 는 오른쪽에서 왼쪽으로 결합한다.

  3. 대입 연산자는 오른쪽에서 왼쪽으로 결합한다.


[표 3.11.1] 연산자 결합 방향 예시

수식

결합 결과

a = x +y - z

a = (x + y) - z

a = x * y / z

a = (x * y) / z

a = x && y || z

a = (x && y) || z

a = y+= z

a = (y+= z)

a = *x++

a = *(x++)


예를 들어서 sA=sY+=sZ 와 같은 표현식은 내부적으로 정해진 결합 순서에 의해서 오른쪽에서 왼쪽으로 결합하겠지만 프로그래머가 아예


sA = (sY += sZ);

와 같이 괄호를 명시하든가 더 가독성을 높이려면


sY += sZ;
sA = sY;

와 같이 두 줄로 나누어 쓰는 것이 더 나아보일 수도 있다. 이는 전적으로 작성자의 성향에 달려있다고 할 수 있다.



Posted by 살레시오
,

 이전 포스트에서처럼 배열은 선언을 먼저 하고 나중에 초기화할 수도 있지만 선언과 동시에 초기화할 수도 있다. 배열을 선언하면서 초기화하는 문법은 다음과 같다.


자료형 배열명[크기] = {값0, 값1, 값2, ... };


배열을 선언하면서 동시에 초기화를 하면 배열 크기는 생략할 수 있다. 이 경우 배열의 크기는 초기값의 개수에 따라 결정된다.


 앞서 들었던 예를 이 방법으로 초기화시키면 다음과 같다.


int iaLength[] = {170, 169, 178, 159, 164};


이렇게 하면 초기값의 개수에 의해서 배열의 크기는 5로 자동으로 결정되게 된다. 만약 배열에 저장할 값들이 미리 정해져 있다면 이 방법이 훨씬 더 간단함을 알 수 있다.


 선언할 때 배열의 크기를 지정했는데 주어진 초기값들이 그 크기보다 작다면 나머지 배열의 요소는 0으로 채워진다. 예를 들어


short saA[100]={1};


의 경우 변수 saA의 첫 번째 요소 saA[0]는 1로, 나머지 saA[1]부터 saA[99]까지는 0으로 초기화 된다.


 하지만 아예 초기값을 주지 않은 경우라면, 예를 들어


char caA[20];


와 같은 경우라면 다음 절에 설명하듯이 이 배열이 전역/정적 배열인지 지역배열인지에 따라서 동작이 달라진다.


이제 배열을 이용하며 다섯 명의 평균키를 구하는 예제를 작성하면 다음과 같다.


#include <stdio.h>
int main()
{
   int iaHeights[] = {170, 169, 178, 159, 164};
   float fAverage = 0.f;
   for (int k=0; k<5; k++) {
       fAverage += iaHeights[k];
   }
   fAverage /= 5;
   printf("average : %f\n", fAverage);
}


여기에서는 산술 평균 (다 더한 후 개수로 나눈 값)을 구해서 화면에 표시해 준다.




Posted by 살레시오
,

5.1 배열의 선언과 초기화     [doc]     [smts]

배열(array)을 설명하기 위해서 다음 표에 기록되어 있는 5명 학생의 몸무게를 데이터로 저장하는 문제로 설명을 시작하도록 하겠다.


[표 5.1.1] 다섯 명의 키 데이터

번호

1

2

3

4

5

키(cm)

170

169

178

169

164


이 5명의 키 자료들을 입력하기 위해서 다음과 같이 int형 변수 5개를 각각 사용할 수 있을 것이다.


int iHeight1 = 170;
int iHeight2 = 169;
int iHeight3 = 178;
int iHeight4 = 159;
int iHeight5 = 164;

이렇게 데이터를 저장하여 관리하여도 문제가 없지만 여러 가지 이유로 비효율적이다.


이와 같이 동일한 성질의 데이터들을 하나의 이름으로 다루기 위해서 배열이라는 자료형이 있다. 배열은 같은 자료형의 데이터들을 연속적인 메모리 공간에 차례대로 저장해서 같은 변수명으로 관리할 수 있다.


 C 언어에서 배열을 선언하는 문법은 다음과 같다.


자료형 배열명[배열크기];

앞에서 예를 든 다섯 명의 키를 저장하는 배열을 선언하기 위해서 다음과 같이 하면 된다.


int iaHeights[5];

여기에서 iaHeights 는 배열의 이름(배열명)이며 배열의 요소(element)의 자료형은 int형이고 배열의 크기는 5이므로 이 배열에는 최대 다섯 개의 int형 값을 저장할 수 있는 것이다.


[표 5.1.2] iaHeights 배열의 메모리 구조

인덱스

0

1

2

3

4

요소

int형

int형

int형

int형

int형


배열 변수를 선언하였다면 이제 다음과 같이 초기화할 수 있다.


iaHeights[0] = 170; //첫 번째 데이터 초기화
iaHeights[1] = 169; //두 번째 데이터 초기화
iaHeights[2] = 178; //세 번째 데이터 초기화
iaHeights[3] = 159; //네 번째 데이터 초기화
iaHeights[4] = 164; //다섯 번째 데이터 초기화

여기서 '배열명[0]' 은 배열의 첫 번째 요소를 지칭한다. 대괄호 [...] 안의 숫자는 배열의 인덱스(index)라고 한다. 인덱스는 1부터가 아니라 0부터 시작하는 것에 유의해야 한다. 배열의 요소는 일반 변수와 동일하게 읽을 수 있으며 갱신할 수 있다.


 다섯 명의 키를 처리하기 위해서 다섯 개의 별개의 변수를 각각 사용하는 것보다 이렇게 배열을 이용하는 것이 훨씬 효율적인데 이는 데이터의 일관성을 유지할 뿐만 아니라 반복문을 사용하기에 용이해서 그렇다.


이제 이 다섯 명의 키를 화면에 출력하는 프로그램을 작성해 보자.


0501-01.c
#include <stdio.h>
int main()
{
  int iaHeights[5]; //배열 선언

  iaHeights[0]=170;
  iaHeights[1]=169;
  iaHeights[2]=178;
  iaHeights[3]=159;
  iaHeights[4]=164;

  for (int k=0; k<5; k++) {
      printf("%d:%d\n",k,iaHeights[k];
  }
}

 위와 같이 배열은 선언을 먼저 하고 나중에 초기화할 수도 있지만 선언과 동시에 초기화할 수도 있다. 배열을 선언하면서 초기화하는 문법은 다음과 같다.


자료형 배열명[크기] = {값0, 값1, 값2, ... };

배열을 선언하면서 동시에 초기화를 하면 배열 크기는 생략할 수 있다. 이 경우 배열의 크기는 초기값의 개수에 따라 결정된다.


앞서 들었던 예를 이 방법으로 초기화시키면 다음과 같다.


int iaLength[] = {170, 169, 178, 159, 164};

이렇게 하면 초기값의 개수에 의해서 배열의 크기는 5로 자동으로 결정되게 된다. 만약 배열에 저장할 값들이 미리 정해져 있다면 이 방법이 훨씬 더 간단함을 알 수 있다.


선언할 때 배열의 크기를 지정했는데 주어진 초기값들이 그 크기보다 작다면 나머지 배열의 요소는 0으로 채워진다. 예를 들어


short saA[100]={1};

의 경우 변수 saA의 첫 번째 요소 saA[0]는 1로, 나머지 saA[1]부터 saA[99]까지는 0으로 초기화 된다.


하지만 아예 초기값을 주지 않은 경우라면, 예를 들어


char caA[20];

와 같은 경우라면 다음 절에 설명하듯이 이 배열이 전역/정적 배열인지 지역배열인지에 따라서 동작이 달라진다.


이제 배열을 이용하며 다섯 명의 평균키를 구하는 예제를 작성하면 다음과 같다.


0501-02.c
#include <stdio.h>
int main()
{
  int iaHeights[] = {170, 169, 178, 159, 164};
  float fAverage = 0.f;
  for (int k=0; k<5; k++) {
      fAverage += iaHeights[k];
  }
  fAverage /= 5;
  printf("average : %f\n", fAverage);
}

여기에서는 산술 평균 (다 더한 후 개수로 나눈 값)을 구해서 화면에 표시해 준다.



Posted by 살레시오
,

 나열형(sequence)은 데이터들의 집합이며 기본적인 나열형 객체는 list, tuple, range 등이다. str형과 bytes형, bytearray 등도 나열형에 속한다. (나열형은 기본적으로 iterable 이다.)


[표 1] 대표적인 가변(mutable) 불변(immutable) 나열형

mutable 시퀀스

immutable 시퀀스

list, bytes, bytearray

str, tuple, range


 다음 표는 가변/불변 나열형 객체에서 지원하는 연산을 우선 순위 차례로 나열한 것이다. x는 임의의 객체이고 s, s1, s2는 나열형 객체들을 표기한다. n, i, j, k는 정수이다.


[표 2] 나열형의 기본 연산의 종류

연산

결과

x in s

x not in s

s의 한 요소가 x와 같다면 True

s의 한 요소가 x와 같다면 False

s1 + s2

s*n 혹은 n*s

두 시퀀스를 결합한다.

시퀀스를 n번 반복한다.

(immutable 시퀀스의 경우 항상 새로운 시퀀스를 생성한다.)

s[i]

i번째 요소 (0부터 시작)

s[i:j]

i번째 부터 j-1번째 요소까지의 슬라이스

s[i:j:k]

i번째 부터 j-1번째 요소까지의(k는 스텝) 슬라이스

len(s)

요소의 개수

min(s)

가장 작은 요소

max(s)

가장 큰 요소

s.index(x[, i[, j]])

x와 같은 첫 번째 요소의 인덱스

s.count(x)

x와 같은 요소들의 개수


같은 형의 나열형 객체끼리는 비교도 가능하다. 특히 리스트와 튜플은 길이와 같은 인덱스를 가지는 모든 요소들끼리 같다면 두 리스트/튜플은 같은 것으로 판별된다.


>>> ‘gg’ in ‘eggs’
True


 나열형 객체의 복사는 ‘얕은 복사’라는 것도 유의해야 한다. 즉, 중첩된 구조는 복사되지 않는다.


>>> lst=[ [] ]*3
>>> lst
[[], [], []]
>>> lst[0].append(1)
>>> lst
[[1], [1], [1]]


이 예제는 곱(*) 복사가 ‘얕은 복사’이기 때문에 원래의 빈 리스트의 참조를 복사해서 붙임을 알 수 있다. 따라서 하나가 변하면 다른 것들도 변한다. 같은 참조를 가지기 때문이다.


서로 다른 리스트를 만들려면 다음과 같이 하면 된다.


lst = [ [] for _ in range(3)]
>>> lst
[[], [], []]
>>> lst[0].append(1)
>>> lst[1].append(2)
>>> lst[2].append(3)
>>> lst
[[1], [2], [3]]


만약 ‘깊은 복사’를 수행하려면 copy 모듈의 deepcopy 함수를 이용하면 된다.


>>> x=[11,22]
>>> y=[x, 33]
>>> y
[[11, 22], 33]
>>> from copy import deepcopy
>>> z = deepcopy(y)
>>> z
[[11, 22], 33]
>>> x[0]=-44
>>> y
[[-44, 22], 33] #x가 변하면 y도 변한다.
>>> z
[[11, 22], 33] # x가 변해도 z는 변함이 없다.


 가변 나열형 객체의 경우 다음과 같은 조작이 추가로 가능하다.


[표 3] 가변 나열형 객체의 조작

연산

결과

s[i] = x

s의 i번째 요소를 x로 교체

s[i:j] = t

i번째 요소부터 j-1번째 요소를 t(iterable)로 교체

del s[i:j]

i번째 요소부터 j-1번째 요소를 삭제 ( s[i:j] = [] 와 동일)

s[i:j:k] = t

i번째 요소부터 j-1번째 요소(k는 스텝)를 t(iterable)로 교체 ➊

del s[i:j:k]

i번째 요소부터 j-1번째 요소(k는 스텝)를 삭제

s.append(x)

s의 마지막 요소로 x를 삽입

s.extend(t)

t의 내용물로 s를 확장 ( s[len(s):len(s)]=t 와 동일)

s.insert(i, x)

i 번째에 x를 삽입

s.pop()

s.pop(i)

마지막 삭제하고 그것을 반환한다.

i 번째 요소를 삭제하고 그것을 반환한다.

s.remove(x)

s의 요소 중 x와 같은 첫 번째 것을 제거 ➋

s.reverse()

요소들을 역순으로 배열한다. ➌

s.clear()

모든 요소 삭제 (del s[:] 과 동일) ver3.3부터 도입

s.copy()

얕은 복사본 생성 (s[:] 와 동일) ver3.3부터 도입


➊ t 와 슬라이싱 된 요소들의 크기가 같아야 한다.

➋ s 안에 x가 없다면 ValueError 예외가 발생한다.

➌ 요소의 순서를 역순으로 바꾼다. ( 역순으로 바뀐 객체가 반환되는 것이 아니다.)



Posted by 살레시오
,

 딕셔너리에 대해서 사용할 수 있는 메써드는 다음과 같은 것들이 있다. 여기서 d는 딕셔너리 객체를 나타낸다.


[표 1] 딕셔너리의 내장 메소드

소속 함수

기능

d.keys()

d.values()

d.items()

키들을 모아서 dict_keys 객체로 반환한다.

값들을 모아서 dict_values 객체로 반환한다.

(키,값) 튜플을 모아서 dict_items 객체로 반환한다.

d.clear()

모든 키:값 쌍을 제거하고 빈 딕셔너리로 만든다.

d.get(key)

key에 해당하는 값을 가져온다. d[key]와의 차이점은 해당 키가 없을 경우 에러를 발생시킨다는 것이다. (d[key]는 None을 반환함)


여기에서 dict_keys, dict_values, dict_items 객체는 모두 iterable 이다. 따라서 for 문에서 사용할 수 있다. 예를 들면 다음과 같다.


>>> capital = {'kr':'seoul', 'jp':'tokyo', 'cn':'beijing'}
>>> for val in capital.values():
  ...:     print(val)
  ...:     
seoul
beijing
tokyo


만약 이 객체로부터 리스트를 생성할 필요가 있다면 파이썬 내장 함수 list() 를 이용하면 된다.


>>> list( capital.keys() ) # 키들로부터 리스트를 생성한다.


만약 딕셔너리 안에  해당 키가 있는지 조사하려면 in 연산자를 이용한다.


>>> ‘kr’ in capital # ‘kr’이라는 키가 captal 딕셔닐에 있으면 True
True
>>> ‘de’ in capital
False


만약 값을 검색하려면 values() 메써드를 이용해야 한다.


>>> ‘seoul’ in capital.values()
True


 특정 키:값 쌍을 삭제하려면 파이썬 내부 명령어인 del 을 이용하면 된다.


>>> del color[0] # 키가 0인 키-값 쌍을 삭제한다.


리스트, 튜플, 딕셔너리와 같은 자료형은 파이썬 프로그램에서 기본적으로 사용되는 자료형이다. 때문에 확실하게 이해하지 않으면 좋은 프로그램을 작성할 수 없으며 다른 사람의 프로그램도 제대로 이해하기 힘들다.



Posted by 살레시오
,

 딕셔너리(dictionary)는 키(key)와 그것에 딸린 값(value)의 쌍들의 집합으로 이루어진 자료형이다. 어떤 사람과 관련된 데이터의 예를 들면 ‘name’은 ‘박장현’ , ‘age’가 ‘44’, 등이 있을 것이다. 여기서 ‘name’, ‘age’ 등이 키가 되고 ‘박장현’은 ‘name’이라는 키에 해당되는 값, ‘44’는 ‘age’라는 키에 해당되는 값이다. 이를 파이썬 문법으로 표현하면 다음과 같다.


>>> man1 = {‘name’:’박장현’, ‘age’:44 }


이렇게 입력하면 man1 이라는 딕셔너리가 생성된다. 딕셔너리는 {...}기호로 생성된다. 이와 같이 딕셔너리는 ‘키:값’ 쌍들의 집합이다. 키와 값은 콜론(:)으로 각 쌍은 콤마(,)로 구분한다.


{ key1:val1, key2:val2, …… }


키로 쓸 수 있는 객체는 숫자와 문자열 등 immutable한 객체라면 무엇이든지 사용할 수 있다. 값에 해당하는 것은 어떤 파이썬 객체라도 올 수 있다.


만약 앞의 예에서 man1의 이름에 해당하는 값객체를 얻고 싶다면 다음과 같이 접근할 수 있다.


>>> man1[‘name’]


이것은 man1 딕셔너리의 ‘name’ 키에 해당하는 값객체를 반환한다.


>>> capital = {‘kr’:’seoul’, ‘jp’:’tokyo’, ‘cn’:’beijing’}


이 예에서도 키가 문자열이고 값도 문자열이다. 딕셔너리는 데이터의 저장 ‘순서’라는 개념이 없다. 따라서 내부적인 인덱스는 없으며 오직 ‘키’로만 연결된 ‘값’에 접근할 수 있을 뿐이다.


>>> color = {0:’red’, 1:’yellow’, 2:’white’, 3:’black’}


위의 예도 딕셔너리이지만 이것은 키로 정수를 갖는다. 따라서


>>> color[1] # ‘yellow’ 값을 읽는다.
>>> x = color[0] # x에 문자열 ‘red’가 저장된다.
>>> color[3] = ‘grey’ # 키 3 에 해당하는 값을 변경한다.
>>> color[4] = ‘blue’ #새로운 키-값 쌍을 추가한다.


전술한 바와 같이 딕셔너리는 인덱스가 없다. 이 예제에서 color[1] 과 같은 용례는 인덱싱이 아니라 1이라는 키를 지정해 준 것이므로 혼동하면 안된다. 딕셔너리는 인덱싱도 할 수 없고 슬라이싱도 당연히 허용되지 않는다. 따라서 color[0:2] 같은 사용은 에러를 발생시킨다.


 또 한 가지 주의할 것은 중복된 키는 허용되지 않는다는 것이다.


>>> a={0:'a', 1:'b', 0:'c'}
>>> a
{0: 'c', 1: 'b'}


위 예에서 딕셔너리 a에 0이라는 키가 중복으로 지정되었는데 결과를 보면 하나는 무시되었다. 딕셔너리는 내부 데이터를 키값으로 구별하기 때문에 중복된 키는 허용하지 않는 것이다.


 딕셔너리의 키로 리스트는 사용 불가지만 튜플은 가능하다. 리스트는 mutable 이고 튜플은 immutable이기 때문이다.


>>> a={(1,):'hi', (1,2):'world'}
>>> a
{(1, 2): 'world', (1,): 'hi'}


값에는 어떠한 파이썬 객체도 올 수 있으며 딕셔너리 안에 값으로 딕셔너리가 다시 올 수도 있는 등 중첩도 얼마든지 가능하다.




Posted by 살레시오
,

튜플(tuple, 터플이라고도 읽는다)은 객체들의 묶음이라는 점에서 리스트와 유사하다. 정의할 때 대괄호 대신 괄호 (..)를 사용한다.


>>> t1 = () # empty tuple 생성
>>> t2 = (11,) # tuple의 요소가 하나일 경우 반드시 끝에 콤마(,)를 붙여야 한다.
>>> t3 = (11, 22)
>>> t4 = (‘abc’, 11, [22, 33])
>>> t5 = ( (11,22), (‘hi’, ‘world’))
>>> t6 = ((True, False))


위에서 빈 튜플을 생성하는 첫 번째 경우만 제외하고 모두 괄호를 생략할 수 있다.


>>> t2 = 11,
>>> t3 = 11, 22
>>> t4 = ‘abc’, 11, [22, 33]
>>> t5 = (11,22), (‘hi’, ‘world’)
>>> t6 = (True, False), # 이 경우 마지막에 콤마가 반드시 붙어야 한다.


이것을 보면 이전에도 몇 번 나왔던 여러 개의 변수를 동시에 생성시키는 경우도 튜플이 사용됨을 알 수 있다.


>>> a, b, c = 11, 22+33j, True # 좌변과 우변 모두 (괄호가 생략된) 튜플이다.


>>> t = 11,22,33 # t는 튜플임
>>> e,f,g = t # e=t[0];f=t[1];g=t[2] 와 같다.
>>> e
11
>>> f
22
>>> g
33


 리스트와의 가장 차이점은 튜플은 그 크기나 개별 요소를 전혀 변경시킬 수 없다는 점이다. 즉, 한 번 생성되고 나면 변경시킬 수 없다. (뒤에서 설명할 immutable sequence 이다.)  튜플도 인덱싱과 슬라이싱이 가능하고 immutable sequence 들의 공통적인 연산(덧셈과 곱셈)이 가능하다.



Posted by 살레시오
,

 리스트도 문자열과 동일한 인덱싱과 슬라이싱이 가능하다. 또한 변형 가능(mutable)하기 때문에 mutable sequence에서 공통으로 사용 가능한 연산(덧셈과 곱셈)을 적용할 수 있다. 이 부분은 문자열의 인덱싱/슬라이싱 포스트와 문자열의 덧셈과 곱셈를 참조하기 바란다.


 리스트의 내장 메소들(method)들은 다음 표와 같다.


[표 1] 리스트의 메소드들

리스트 함수

동작

append(x)

x를 리스트의 마지막 요소로 추가한다.

extend(list)

list의 요소로 원 리스트를 확장한다.

insert(i, x)

x를 i번째 위치로 끼워 넣는다.

remove(x)

x와 같은 첫 번째 요소를 삭제한다.

pop()

pop(i)

마지막 요소를 삭제하고 반환한다.

i번째 요소를 삭제하고 반환한다.

clear()

모든 요소를 삭제한다.

index(x)

x와 같은 첫 번째 요소의 인덱스를 반환한다.

count(x)

x와 같은 요소들의 개수를 구한다.

sort()

정렬

reverse()

역순으로 배열

copy()

얕은 복사본을 반환한다.


이 함수들의 사용 예를 들면 다음과 같다.


>>> a = [66.25, 333, 333, 1, 1234.5]
>>> print(a.count(333), a.count(66.25), a.count('x'))
2 1 0
>>> a.insert(2, -1)
>>> a.append(333)
>>> a
[66.25, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.25, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.25]
>>> a.sort()
>>> a
[-1, 1, 66.25, 333, 333, 1234.5]
>>> a.pop()
1234.5
>>> a
[-1, 1, 66.25, 333, 333]


append()함수와 extend()함수는 그 동작에 차이가 있다.


>>> x=list(range(5))
>>> x.append([5.6]) # x의 마지막 요소로 [5,6] 이 들어간다.
[0,1,2,3,4,[5,6]]

>>> x=list(range(5))
>>> x.extend([5.6]) # x의 오른쪽에 [5,6]의 요소를 병합시킨다.
[0,1,2,3,4, 5,6]


그리고 append() 함수와 pop()함수를 이용하면 리스트를 스택(stack)으로 운영할 수 있다.


>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]


이와 같이 리스트의 메쏘드를 이용하면 stack/queue 와 같은 고급 자료형을 쉽게 구현할 수 있다.



Posted by 살레시오
,

 리스트는 여러 개의 객체를 하나로 묶는 데이터 형이다. (엄밀히 말하면 mutable sequence 형임) 기본적으로 대괄호 [..]를 이용하여 묶고자 하는 데이터들을 콤마(,)로 구별하여 나열한다. 보통은 같은 형의 데이터를 묶어서 많이 사용하지만 리스트의 요소는 서로 다른 데이터 형일 수 있다.


>>> la = [11, 22, 33] # 리스트를 생성하여 la 에 저장한다.
>>> lb = [‘life’, ‘is’, ‘short’] # 문자열의 리스트
>>> lc = [True, ‘hi’, 33.4, 2-3j]
>>> ld = [] # empty list를 생성하여 lc에 저장


이 예제와 같이 리스트의 요소는 어떠한 자료형이라도 가능하다. 리스트 안에 리스트가 오는 것도 얼마든지 가능하다.


>>> le = [ [11,22], [33, 44, 55] ]


이것을 중첩된 리스트라고 한다.


리스트를 만드는 또 다른 방법은 list comprehension 을 이용하는 것이다. 예를 들어서 제곱 수의 리스트틀 만든다고 가정하자. 다음과 같이 for 문을 이용할 수 도 있을 것이다.


>>> squares = []
>>> for x in range(10):
...    squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]


하지만 이 방법은 변수 x가 생성되고 for 문이 종료된 후에도 변수 x 가 메모리에 남아 있게 된다. 좀 더 깔끔한 방법은 다음과 같이 map()과 익명 함수를 이용하는 것이다.


>>> squares = list(map(lambda x: x**2, range(10)))


이것과 완전히 동일한 방법이 바로 다음과같은 list comprehension 이고 훨씬 더 간결하고 가독성이 높다.


>>> squares = [x**2 for x in range(10)]


일반적으로 다음과 같이 구성된다.


[ 표현식 for문 (for 문 | if 문)* ]


예를 들어서 두 리스트 요소 중 다른 것들로만 조합하는 리스트는 다음과 같다.


>>> [(x,y) for x in [1,2,3] for y in [3,1,4] if x!=y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]


이것은 다음 프로그램과 동일한 동작을 수행한다.


>>> combs = []
>>> for x in [1,2,3]:
...    for y in [3,1,4]:
...        if x != y:
...            combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]


두 가지 경우에서 for 문과 if 문의 순서가 같다는 것을 알 수 있다.

표현식이 튜플일 경우 반드시 괄호로 묶어야 한다.


>>> [(x,x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]


만약 다음과 같이 괄호를 생략하면 에러가 발생한다.


>>> [ x,x**2  for x in range(6)] # 에러 발생


여기서 소개한 내용이 리스트를 생성하는 기본적인 방법들이다.



Posted by 살레시오
,

진리값

 파이썬에서 참, 거짓은 True 와 False 라는 키워드를 사용한다. (첫 글자가 대문자라는 것에 유의해야 한다.) 진리값은 자체로 변수의 값으로 사용될 수 있으며 논리 연산의 결과를 표현하는데 사용된다. 한 가지 알아둘 것은 파이썬은 False 뿐만 아니라 None, 0 (0.0, 0j, 0.0j, 0+0j 도 마찬가지), 빈 문자열(“” 혹은 ‘’),  빈 집합 ( (), {}, [] )도 False 로 간주한다는 것이다.


비교 연산자

 파이썬에는 다음과 같은 비교연산자들이 있다.


[표 1] 파이썬의 비교 연산자들

연산자

의미

x < y

x보다 y가 크면 True

x <= y

x보다 y가 크거나 같다면 True

x > y

x보다 y가 작으면 True

x >= y

x보다 y가 작거나 같다면 True

x == y

x와 y가 같다면 True

x != y

x와 y가 다르다면 True

x is y

동일한 객체인지를 판별 (x와 y가 같은 참조인지 판별)

x is not y

is 의 부정형 (x와 y가 다른 참조인지 판별)


연산자 ‘==’와 ‘!=’은 객체의 내용(content)를 직접 비교하여 참/거짓을 판별한다. 객체의 형(type)이 다르면 두 객체는 항상 다르다. 단, 숫자형인 경우 int, float, complex 간에도 실제로 같은 숫자라면 같다. 즉, 1 (int형), 1.0 (float형), 1+0j (complex형), 1.0+0j (complex형), 1.0+0.0j (complex형) 는 모두 같은 숫자로 판별된다.


>>> sa = 'hello everybody'   
>>> sb = 'hello everybody'
>>> sa == sb
True


반면 연산자 is 는 두 객체가 동일한 참조(주소)를 가지고 있는 지를 판별하는 것이다. 만약 id(x)와 id(y)가  같다면 x is y 는 True값을 가진다.


>>> sa='hello everybody'   
>>> sb='hello everybody'
>>> sa is sb
False

>>> x=[1,2]
>>> y=[1,2]
>>> x==y
True
>>> x is y
False
>>> z=x
>>> z is x
True


논리 연산자

 논리 연산자는 조건식들끼리 묶는 역할을 한다.


[표 2] 파이썬의 논리 연산자들

연산자

기능

x and y

x와 y 둘 다 True 일 때 True 그 외는 False

x or y

x와 y 둘 다 False 일 때 False 그 외는 True

not x

x가 True 일 때 False


이와 같이 파이썬의 논리 연산자는 다른 언어들과는 달리 자연어(and, or, not)을 사용한다.



Posted by 살레시오
,