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


자료형 배열명[크기] = {값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 살레시오
,