2. Chapter 3 Working with batches of data ¶
여태까지의 기초적인 입출력 문제를 넘어서서 본격적인 작업을 다루기 시작하는 장이다.
학생들의 성적 관리 프로그램으로 작업을 하면서, 뭔가를 저장하는 방법 등등을 논의할 것이다.
학생들의 성적 관리 프로그램으로 작업을 하면서, 뭔가를 저장하는 방법 등등을 논의할 것이다.
2.1. 3.1 Computing student grades ¶
- 먼저 우리가 가지고 놀 코드를 보자.
~cpp #include <iostream> #include <iomanip> #include <string> using std::cin; using std::setprecision; using std::streamsize; using std::cout; using std::string; using std::endl; int main() { // ask for and read the students's name cout << "Please enter your first name: "; string name; cin >> name; const string greeting = "Hello, " + name + "!"; // ask for and read the midterm and final grades cout << "Please enter your midterm and final exam grades: "; double midterm, final; cin >> midterm >> final; // ask for the homework grades cout << "Enter all your homework grades, " "follewd by end-of-file: "; // the number and sum of grades read so far int count = 0; double sum = 0; // a variable into which to read double x; // invariant: // we hava read count grades so far, and // sum is the sum of the first count grades while(cin >> x) { ++count; sum += x; } // write the result streamsize prec = cout.precision(); cout << "Your final grade is " << setprecision(3) << 0.2 * midterm + 0.4 * final + 0.4 * sum / count << setprecision(prec) << endl; return 0; }
- cin
~cpp cin >> a >> b; // 이 문장은 다음과 같다. cin >> a; cin >> b; // >> 연산자는 left operand를 리턴한다.
- 변수 선언에 관해서..
~cpp string insu("insu"); // 요건 string형 변수 insu에 "insu"라는 문자열이 들어간다. string insu; // 요건 디폴트 생성자(그냥 넘어가자. 책에는 Default Initialization이라고 써있다.)에 의해 그냥 비어있게 된다. int num = 0; // num을 0으로 초기화해준다. int num; // num에는 무슨 값이 들어갈까? 책에는 undefined라고 써있다. 메모리에 있던 쓰레기값이 들어가게 된다. // -8437535 이거 비슷한 이상한 숫자가 들어가게 되는걸 보게 될 것이다.
- while(cin >> x)
~cpp // 다음과 같은 코드는 cin >> x를 만족할 동안 돌게 된다. 이게 무슨 말인지는 일단 넘어가자.(3.1.1) while (cin >> x) { ++count; // count에 1을 더한다. sum += x; // sum에 x를 더한다. }
- setprecision
~cpp // 숫자의 정밀도를 조절해준다. setprecision(3); // 유효숫자는 3자리가 되고, 일반적으로 소숫점 앞의(정수부분의) 2자리, 소수보분의 1자리로 채워지게 된다.
2.1.1. 3.1.1 Testing for end of input ¶
위에서 살펴본 while(cin>>x)구문을 살펴보도록 하자. 위에 써놓은 바에 의하면 cin >> x가 성공할동안 계속 루프를 돈다고 했다. 그러면 언제 실패할까?
- 입력 작업 빠져 나갈때
- x의 타입에 맞지 않는 값이 들어올때
- >>
~cpp if (cin >> x) ... // 이 문장은 다음과 같다. cin >> x; if(cin) ... // istream 내부의 복잡한 작업이 있긴 하지만 12장까진 몰라도 된다. 그냥 이것마 알아도 충분히 쓸수 있다.
- bool과 숫자 : 조건문 내에서 0이외의 숫자는 모두 true로 변환, 0은 false로 변환된다.
- bool과 cin : 역시 cin도 조건문 내에서 쓸수 있게 변환된다. 지금은 자세히 알 필요 없다.
- stream으로부터 읽어들이는데 실패할 경우
- 입력파일의 끝에 도달했을때
- type이 맞지 않을때
- 입력 하드웨어 상에 문제가 생길때
- 입력파일의 끝에 도달했을때
- 실패했을 경우에는 stream 초기화를 시켜줘야 한다.(4장에서 보자)
2.2. 3.2 Using medians instead of averages ¶
- 평균값 대신에 중간값을 사용하는 프로그램으로 변경한다.
- 평균값보다 중간값이 좋을 때도 있다.
- 평균값은 그냥 다 더해서 나눠주면 되니까 하나하나의 값을 저장할 필요가 없었다.
- 하지만.. 중간값은 그렇지가 못하다. 다 저장해놔야 한다. 그리고 sort를 해야할 것이다.
- 이제부터 그 방법들을 살펴보도록 하자.
2.2.1. 3.2.1. Storing a collection of data in a vector ¶
- vector란? - 주어진 타입의 값들의 모음을 가지고 있는 컨테이너이다. 확장요청이 있을때 커진다.
- 이러한 것들을 Telplate Classes 라고 한다. 11장에서 자세히 보도록 하자.
- vector 사용하기
~cpp // 다음과 같은 코드를 int count = 0; double sum = 0; double x; while(cin >> x) { ++count; sum += x; } // 다음과 같은 코드로 바꿀수 있다. vector<double> homework; // double값들을 저장할 vector double x; while(cin >> x) // while루프는 값들을 읽어들이면서 homework에 저장한다. homework.push_back(x);
- push_back : vector의 멤버 함수. vector의 끝에다 집어넣는 역할을 한다. 그러면서 벡터의 크기를 하나 증가시킨다.
2.2.2. 3.2.2 Generating the output ¶
- size() 멤버 함수 : vector가 소지하고 있는 값들의 갯수를 리턴해준다.
~cpp typedef vector<double>::size_type vec_sz; vec_sz size = homework.size();
- typedef : vector<double>::size_type이라고 일일히 쳐주기엔 너무 길기 ㅤㄸㅒㅤ문에 vec_sz로 줄여쓴 것이다.
- 또한 vector의 크기가 0이면 아무것도 안들어있다는 것이므로 중간값의 의미가 없다. 0일때 처리
~cpp if(size == 0) { cout << endl << "you must enter your grades. " "Please try again." << endl; return 1; // main함수가 0을 리턴하면 성공적으로 끝난것이고, 그 외의 숫자는 실패적으로 끝난것이다. }
- 중간값을 찾기 위해 먼저 해야할 작업 sort : algorithm 헤더에 정의되어 있다.
~cpp // sort the grades sort(homework.begin(),homework.end());
- begin() 메소드와, end() 메소드
- begin() : 컨테이너의 맨 처음 값을 가리킨다.
- end() : 컨테이너의 맨 마지막 원소에서 한칸 지난 값을 가리킨다.
- 5장에선 이 둘을 자세하게 살펴볼것이며, 8장에서는 한칸 지난다는 것의 의미를 살펴볼 것이다.
- begin() : 컨테이너의 맨 처음 값을 가리킨다.
- find a median
~cpp median = size % 2 == 0 ? (homework[mid] + homework[mid-1]) / 2 : homework[mid]; // 개수가 홀수이면 딱 가운데꺼, 짝수개면 가운데 두개의 평균을 median 변수에 넣어준다.
- % : 나머지 연산
- ? : : ? 앞의 문장이 참이면 ?와 :사이의 문장을 수행, 거짓이면 : 뒤의 문장을 수행
- etc : vector의 맨 처음 인덱스는 0이다. 마지막은 size-1
- 최종 소스
~cpp #include <iostream> #include <iomanip> #include <string> #include <vector> #include <algorithm> #include <ios> // 원래 책에는 위 소스 처럼 각각 이름공간을 주었지만 이제부터 무난한 std로 쓰겠습니다. using namespace std; int main() { // ask for and read the students's name cout << "Please enter your first name: "; string name; cin >> name; const string greeting = "Hello, " + name + "!"; // ask for and read the midterm and final grades cout << "Please enter your midterm and final exam grades: "; double midterm, final; cin >> midterm >> final; // ask for the homework grades cout << "Enter all your homework grades, " "follewd by end-of-file: "; vector<double> homework; double x; // invariant: homework contains all the homework grades read so far while(cin >> x) homework.push_back(x); // check that the student entered some homework // 바로 밑에 소스가 이상합니다. // 원래 책에는 "typedef vector<double>::size_type vec_sz;" 이렇게 되어있지만 // 컴파일시 에러가 나서 같은 의미인 unsigned int형을 써서 vec_sz을 표현했습니다. typedef unsigned int vec_sz; vec_sz size = homework.size(); if(size == 0) { cout << endl << "you must enter your grades. " "Please try again." << endl; return 1; } // sort the grades sort(homework.begin(),homework.end()); // compute the median homework grade vec_sz mid = size / 2; double median; median = size % 2 == 0 ? (homework[mid] + homework[mid-1]) / 2 : homework[mid]; // compute and write the final grade streamsize prec = cout.precision(); cout << "Your final grade is " << setprecision(3) << 0.2 * midterm + 0.4 * final + 0.4 * median << setprecision(prec) << endl; return 0; }
2.2.3. 3.2.3 Some additional observations ¶
- homework가 비어있을때 왜 프로그램을 끝내야 할까?
- 끝내지 않는다면, size/2=0 이다. homework0은 정의되어 있지 않다. 그런 것이다.
- size_type은 unsigned int 이다.
- vector와 sort의 수행성능에 관해
- vector에다 값을 새로 추가하는 데에는 Θ(n)의 시간이 걸린다.
- sort 작업은 Θ(nlog(n))의 시간이 걸린다.
- vector에다 값을 새로 추가하는 데에는 Θ(n)의 시간이 걸린다.
- C++의 스탠다드 라이브러리들은 걱정하지 말고 써도 된다. C++은 performance-critical한 애플리케이션 제작을 위해 만들어졌기 때문이다.