Week 5

: Data Structures


 

Lecture Contents

 

 

  • Data structures are forms of organization in memory.

 

 

1. Stacks and Queues

 

  • Queues : one form of abstract data structure (enqueued, dequeued)
  • Stacks (push, pop)
FIFO First in first out
LIFO Last in first out
typedef struct
{
    person people[CAPACITY];
    int size;
}
stack;

- Limitation of the code above : unable to grow as items are added to it.

 

 

 

 

2. Resizing Arrays

 

There's an array :

 

In memory, there are other values being stored by other programs, functions, and variables.

 

If you want to store a fourth value '4' in our array. 

You can allocate a new area of memory of size 4 and move the old array to a new one.

This new area of memory would be populated with garbage values

 

 

However, it's bad designed - Every time we add a number, we have to copy the array item by item.

It would be nice if we were able to put the '4' somewhere else in memory. (this would no longer be an array though because it's not contiguous)

 

list.c

#include <stdio.h>

int main(void)
{
	//List of size 3
    int list[3];
    
    //Initialize list with numbers
    list[0] = 1;
    list[1] = 2;
    list[2] = 3;
    
    //Print list
    for (int i = 0; i < 3; i++)
    {
    	printf("%i\n", list[i]);
    }
}

 

We can leverage our understanding of pointers to create a better design in this code:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	//List of size 3
    int *list = malloc(3 * sizeof(int));
    if (list == NULL)
    {
    	return 1;
    }
    
    //Initialize list of size 3 with numbers
    list[0] = 1;
    list[1] = 2;
    list[2] = 3;
    
    //List of size 4
    int *tmp = malloc(4 * sizeof(int));
    if (tmp == NULL)
    {
    	free(list);
    	return 1;
    }
    
    //Copy list of size 3 into list of size 4
    for (int i = 0; i < 3; i++)
    {
    	tmp[i] = list[i];
    }
    
    //Add number to list of size 4
    tmp[3] = 4;
    
    //Free list of size 3
    free(list);
    
    //Remember list of size 4
    list = tmp;
    
    //Print list
    for (int i = 0; i < 4; i++)
    {
    	printf("%i\n", list[i]);
    }
    
    //Free list
    free(list);
    return 0;
}
  • Notice that the 'tmp' list was created.
  • free(list) : since the block of memory that list points to is no longer used.

 

 

 

3. Linked Lists

 

  • three useful primitives
struct a data type that you can define yourself
. dot notation allows you to access variables inside that structure
* is used to declare a pointer or dereference a variable

 

  • -> operator : goes to an address and looks inside of a structure

A linked list is one of the most powerful data structures within C. It allows you to include values that are located at varying areas of memoryThey allow you to dynamically grow and shrink the list as you desire.

 

Three values stored at three different areas of memory :

We could utilize more memory to keep track of where the next item is :

We would keep one more element in memory, a pointer, that keeps track of the first item in the list.

 

These boxes are called nodes.

A node contains both an item (int number) & a pointer called next.

typedef struct node
{
    int number;
    struct node *next;
}
node;

 

 

Let's implement this picture above in code:

 

#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
	int number;
    struct node *next;
}
node;

int main(int argc, string agrv[])
{
	//Memory for numbers
    node *list = NULL;
    
    //For each command-line argument
    for (int i = 1; i < agrc; i++)
    {
    	//Convert argument to int
        int number = atoi(argv[i]);
        
        //Allocate node for number
        node *n = malloc(sizeof(node));
        if (n == NULL)
        {
        	return 1;
        }
        n->number = number;
        n->next = NULL;
        
        //Prepend node to list
        n->next = list;
        list = n;
    }

	//Print numbers
    node *ptr = list;
    while (ptr != NULL)
    {
    	printf("%i\n", ptr->number);
        ptr = ptr->next;
    }
    
    //Free memory
    ptr = list;
    while (ptr != NULL)
    {
    	node *next = ptr->next;
        free(ptr);
        ptr = next;
    }
}

 

  • Inserting into the list : O(1)
  • The amount of time required to search this list : O(n)

 

Pros

-Since linked lists are not stored in a contiguous block of memory, they can grow as large as you wish.

 

Cons

-More memory is required to keep track of the list instead of an array. (Pointer)

-Binary search is not possible in a list constructed as above.

 

 

★ 헷갈렸던 NULL 개념 정리 

type something == NULL;

something이 아무것도 pointing 하고있지 않음을 의미함.

 

 

You can sort your list as items are added :

#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
	int number;
    struct node *next;
}
node;

int main(int argc, char *argv[])
{
	node *list = NULL;
    
    for(int i = 1; i < agrc; i++)
    {
    	int number = atoi(agrv[i]);
        node *n = malloc(sizeof(node));
        
        if(n == NULL)
        {
        	return 1;
        }
        n->number = number;
        n->next = NULL;
        
        if(list == NULL)
        {
        	list = n;
        }
        
        else if(n->number < list->number)
        {
        	n->next = list;
            list = n;
        }
        
        else
        {
        	for (node *ptr = list; ptr != NULL; ptr = ptr->next)
            {
            	if(ptr->next == NULL)
                {
                	ptr->next = n;
                    break;
                }
                
                if (n->number < ptr->next->number)
                {
                	n->next = ptr->next;
                    ptr->next = n;
                    break;
                }
            }
     }
     
     for (node *ptr = list; ptr != NULL; ptr = ptr->next)
     {
     	printf("%i\n", ptr->number);
     }
     
     node *ptr = list;
     while (ptr != NULL)
     {
     	node *next = ptr->next;
        free(ptr);
        ptr = next;
    }
}

 

 

 

 

 

4. Trees

 

 

  • Binary search trees : another data structure to store data more efficiently

  • The center value becomes the top of a tree.
  • Those that are less than the center value are placed to the left.
  • Those values that are more than the center value are to the right.

-Pointers can be used to point to the correct location of each area of memory.

 

In code :

 

-Search time : O(logn)

 

 

 

 

 

5. Dictionaries

 

  • Dictionaries : another data structure
  • They have a key and a value. (like actual book-form dictionaries that have a word and a definition)
  • The holy grail of algorithmic time complexity = O(1) or constant time
  • Dictionaries can offer this speed of access through hashing.

 

 

 

 

 

6. Hashing and hash Tables

 

  • Hashing : the idea of taking a value and being able to output a value that becomes a shortcut to it later.
  • A hash function : an algorithm that reduces a larger value to something small and predictable.
  • A hash table : a combination of both array and linked lists. An array of pointers to nodes.

 

A hash table could be imagined as follows:

- An array of alphabet

-When collisions happen, it can be reduced by improving your hash table and hash algorithm.

 

However, it takes up massive amount of memory.

 

A hash algorithm:

 

In code:

#include <ctype.h>

unsigned int hash(const char *word)
{
	return toupper(word[0]) - 'A';
}

 

 

 

 

 

7. Tries

 

  • Tries : another form of data structure
  • always searchable in constant time !
  • Downside : take up a large amount of memory

For example, a word 'Toad' would be stored as follows:

 

Toadette and Tom would be stored as:

 

 


5주차에서는 Queues, Linked lists, Trees, Dictionaries, 그리고 Tries 까지 다양한 data structure의 개념과 활용 방법에 대해서 알아보았다. 컴퓨터의 메모리를 정리할 때 어떤 data structure을 사용하느냐에 따라 속도와 메모리가 차지하는 크기가 달라지기 때문에 중요한 영향을 끼친다. 저번주에 이어서 보이지 않는 메모리 영역을 다루고, pointer의 개념이 아직 미숙해서 처음으로 강의를 다시 뒤로 돌려 반복해서 듣기도 한 내용들이다. 아직까지 완벽하게 이해했다고 자신할 수는 없지만, Section과 Pset5를 듣고 고민하는 과정에서 더 큰 이해를 할 수 있길!! 오늘도 수고 많았다 안뇨옹

'CS50' 카테고리의 다른 글

[CS50] Pset 4 : Volume, Filter-less, Recover  (5) 2024.10.22
[CS50] Week 4 Section  (1) 2024.10.10
[CS50] Week 4 Review  (4) 2024.10.09
[CS50] Pset 3 : Sort, Plurality, Runoff  (1) 2024.10.06
[CS50] Week 3 Section  (2) 2024.10.03

 

Pset 4이 왜 이렇게 늦었냐면... 코딩에 약간 벽을 느끼고 1보 후퇴했기 때문..

ㅎㅎ 사실 논 건 아니고 매일 시간 내서 문제를 풀려고 노력은 했지만 너무 어려워서 한참 걸렸어요..ㅠㅠ

뒤로 갈수록 배운 거에서 나오는 게 아니고 응용을 요구합니다 !!!

약간.. 개념 배우고 심화 문제 나오는 수능같은 그런 느낌

진짜로 literally 손으로 머리 싸매고 낑낑댐..

 

어쨋든 어제 두세시간을 쏟고 마지막 과제를 제출하고 나서야 문풀 리뷰를 할 수 있게 되었네용

리뷰하면서 못 다한 이해를 해보도록 하겠습니다 ^~^

1보 후퇴엔 3보 전진~~

 

레츠고 !


4-1) Volume

 

: 볼륨 조절하기

WAV files store audio as a sequence of “samples”: numbers that represent the value of some audio signal at a particular point in time. WAV files begin with a 44-byte “header”, and after the header, it contains a sequence of samples, each a single 2-byte (16-bit) integer representing the audio signal at a particular point in time. Scaling each sample value by a given factor has the effect of changing the volume of the audio. Write a program to modify the volume of an audio file.

 

 

<Distribution code>

  • The program should accept three command-line arguments. (input, output, factor)
  • The program should read the header from the input file and write the header to the output file.
  • The program should then read the rest of the data from the WAV file, one 16-bit(2-byte) sample at a time. It should multiply each sample by the factor and write the new sample to the output file. 

 

 

 

atof() : used to convert a string into a floating-point number

 


//TODO #1

: Copy header from input file to output file

uint8_t header[HEADER_SIZE];
fread(header, HEADER_SIZE, 1, input);
fwrite(header, HEADER_SIZE, 1, output);

* HEADER_SIZE는 이미 44로 defined 됨.

 

★Parameters of fread ()

fread(a pointer to a block of memory, size of each element, number of elements, a pointer to the FILE object);

 

 

 

 

//TODO #2

: Read samples from input file and write updated data to output file

int16_t buffer;
while (fread(&buffer, sizeof(int16_t), 1, input)
{
    buffer *= factor;
    fwrite(&buffer, sizeof(int16_t), 1, output);
}

 

 


4-2) Filter-less

 

: 사진에 필터 입히기

A file format (like BMP, JPEG, or PNG) that supports “24-bit color” uses 24 bits per pixel. A 24-bit BMP uses 8 bits to signify the amount of red in a pixel’s color, 8 bits to signify the amount of green in a pixel’s color, and 8 bits to signify the amount of blue in a pixel’s color. If the R, G, and B values of some pixel in a BMP are, say, 0xff, 0x00, and 0x00 in hexadecimal. In this problem, you’ll manipulate these R, G, and B values of individual pixels, ultimately creating your very own image filters.

 

 

<Distribution code>

 

 


//TODO #1

: Implement grayscale (흑백 필터)

 

We should make sure the red, green, and blue values are all the same value. But what value to make them?

→ If the original red, green, and blue values were all pretty high, then the new value should also be pretty high. If the original values were all low, then the new values should also be low.

 

You can take the average of the red, green, and blue values of each pixel to determine what shade of grey to make the new pixel. 

 

//Loop over all pixels
for(int i = 0; i < height; i++)
{
	for(int j = 0; j < width; j++)
    {
	//Take average of red, green, and blue
    int average = round((image[i][j].rgbtRed + image[i][j].rgbtGreen + image[i][j].rgbtBlue) / 3.0);
    
    //Update pixel values
    image[i][j].rgbtRed = average;
    image[i][j].rgbtGreen = average;
    image[i][j].rgbtBlue = average;
    }
}

 

 

The result being like :

 

 

 

 

//TODO #2
: Implement Sepia (오래된 붉은갈색 느낌 필터)

 

For each pixel, the sepia color values should be calculated based on the original color values per the below:

sepiaRed = .393 * originalRed + .769 * originalGreen + .189 * originalBlue
sepiaGreen = .349 * originalRed + .686 * originalGreen + .168 * originalBlue
sepiaBlue = .272 * originalRed + .534 * originalGreen + .131 * originalBlue

The results may not be an integer, but each value could be rounded to the nearest integer.

//Loop over all pixels
for (int i = 0; i < height; i++)
{
	for (int j = 0; j < width; j++)
    {
    	//Compute sepia values
        int sepiaRed = round(.393 * image[i][j].rgbtRed + .769 * image[i][j].rgbtGreen + .189 * image[i][j].rgbtBlue);
        int sepiaGreen = round(.349 * image[i][j].rgbtRed + .686 * image[i][j].rgbtGreen + .168 * image[i][j].rgbtBlue);
        int sepiaBlue = round(.272 * image[i][j].rgbtRed + .534 * image[i][j].rgbtGreen + .131 * image[i][j].rgbtBlue);
        
        if(sepiaRed > 255)
        {
        	sepiaRed = 255;
        }
         
        if(sepiaGreen > 255)
        {
        	sepiaGreen = 255;
        }
        
        if(sepiaBlue > 255)
        {
        	sepiaBlue = 255;
        }
        
        //Update pixel with sepia values
        image[i][j].rgbtRed = sepiaRed;
        image[i][j].rgbtGreen = sepiaGreen;
        image[i][j].rgbtBlue = sepiaBlue;
    }
}

 

 

The result being like:

 

 

 

 

//TODO #3

: Implement Reflect (수평으로 뒤집힌 필터)

 

  • Any pixels on the left side of the image should end up on the right, and vice versa.
//Loop over all pixels
for (int i = 0; i < height; i++)
{
	for (int j = 0; j < (width/2); j++)
    {
    	//Swap pixels
        RGBTRIPLE temp = image[i][j];
        image[i][j] = image[i][width-j-1];
        image[i][width-j-1] = temp;
    }
}

★ Notice that j loop is until (width/2) because only half of the pixels of a row should be reflected!

image[i][width-j-1] means the pixel on opposite side of a row.

 

 

The result being like:

 

 

 

 

//TODO #4

: Implement Blur (블러 처리된 필터) ★

 

가장 어려워서 며칠간 고민했던 부분!!

We will use "box blur", which works by taking each pixel and, for each color value, giving it a new value by averaging the color values of neighboring pixels. 

The new value of each pixel would be the average of the values of all of the pixels that are within 1 row and column of the original pixel (forming a 3x3 box). 

Ex) For pixel 6 : new value of averaged the original color values of pixels 1,2,3,5,6,7,9,10, and 11.

      For a pixel along the edge or corner, like pixel 15 : pixels 10,11,12,14,15and 16.

 

We are going to create a copy of image:

//Create a copy of image
RGBTRIPLE copy[height][width];
for(int i = 0; i < height; i++)
{
	for(int j = 0; j < width; j++)
    {
    	copy[i][j] = image[i][j];
    }
}

 

Let's write psedocodes first, stey-by-step!

//전체 이미지의 각 픽셀 iterate

	//copy 만들기
    
    //3x3 box 픽셀 iterate
    
    	//픽셀이 valid한 경우 (edge case, corner case 포함)
        
        	//각 픽셀에 새로운 RGB 값 할당
            
        //각 RGB 값의 평균을 copy에 직접 할당
        
        //전체 픽셀에 대해 copy 값을 image 값에 원래대로 할당

즉, 전체 이미지에서 3x3 사이즈의 픽셀 박스를 iterate하면서 각 RGB값의 평균을 새로이 할당하고, 마지막에는 copy에 할당한 값을 원래대로 image에 돌려놓으면 됩니다!

 


① 3x3 box 픽셀 반복문

★새로운 변수 int di, int dj를 만들어서 상하좌우 한 칸씩을 뜻하는 -1부터 1까지 반복하는 box iteration을 만든다.

이것을 전체를 반복하는 변수 i와 j에 더하여 전체 픽셀을 한정적인 3x3 box로 반복할 수 있게 새로운 변수 ni, nj로 정의한다.

 

 

② valid pixel 조건문

: 유효한 픽셀이라는 것은 edge나 corner 픽셀의 경우도 포괄해야 함을 뜻한다. 

  이는 새로이 정의된 변수 ni, nj가 0보다 크거나 같고 각각 height, width보다 작아야함을 의미한다. 

 

 

③ 새로운 RGB 값 할당하기

: 먼저, 새로운 RGB값을 담아둘 변수 Red, Green, Blue를 정의한 후 각각에 값을 할당한다.

: float으로 정의한 이유는 각 값이 딱 정수로 떨어지지 않을 수 있기 때문. 이렇게 정의하면 정확도가 올라갈 수 있다.

 

: 각 픽셀의 빨간색, 초록색, 파란색 값을 각각 Red, Green, Blue에 더해준다.

 

 

④ counter 만들기

: 평균을 구하기 위해서는 몇 개의 유효한 픽셀의 RGB 값이 더해졌는지 알아야 한다.

  따라서, 유효한 픽셀을 카운트 할 때마다 counter 변수를 하나씩 업 시켜준다.

 

먼저, counter 변수를 0으로 정의한다.

: 중요한 것은 counter을 0으로 정의하는 위치이다. 각 box를 반복한 후에 다시 0으로 reset 해야하기 때문에 box iteration 앞에 정의한다.

 

: 유효한 픽셀 조건문 안에 counter ++를 해준다. 

 

 

RGB 값 평균 구하고 할당하기

: RGB 값의 평균은 직접적으로 copy에 할당한다. 이 때 주의할 점은 변수 Red, Green, Blue 값이 float 이기 때문에 round()를 통해 가까운 정수로 변환해준다.

: 이 때에도 중요한 것은 위치이다. Box iteration 바깥에서 copy에 할당해준다. 

 

 

copy 값을 image 값으로 옮기기

각 픽셀의 값을 다 옮겨 할당해야 하므로, 전체 픽셀을 반복하는 for문 아래에서 할당시켜준다.

 

 

완성된 Blur 코드를 전체적으로 보면 다음과 같다:

 

 

The result being like:

 

 


4-3) Recover

 

: 삭제된 사진 복원하기

We spent the past several days taking photos around campus, all of which were saved on a digital camera as JPEGs on a memory card. Unfortunately, we somehow deleted them all! We’re hoping (er, expecting!) you can write a program that recovers the photos for us!

(약간 초등학교 수학 문제중에 달력을 찢었다.. 이런 거 생각났다 ㅋㅋㅋㅋㅋㅋ 왜 찢어.. 왜 삭제해..)

 

 

Background

  • JPEGs have "signatures", patterns of bytes that can distinguish them from other file formats. The first three bytes of JPEGs are 0xff, 0xd8 and 0xff.
  • The fourth byte is either 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, or 0xef. The fourth byte's first four bits are 1110.
  • Digital cameras only write to cards in units of 512 B. (block size = 512 bytes
    →It means you can read 512 of memory card's bytes at a time for efficiency's sake.

위 그림처럼 각 색이 각 JPEG 파일을 의미한다. JPEG가 시작하는 블럭이 있고, 시작하지 않는 블럭이 있다.

 

 

 

Let's write psedocodes first!

//Accept a single command-line argument

//Open the memory card

//While there's still data left to read from the memory

	//Create JPEGs from the data

 

 

① single command-line argument

: 하나의 command-line argument가 아닐 시 에러 메세지로 경고한다. (argc != 2)

 

 

② open the memory card

: card memory로 가서 argv[1]을 여는데, read 모드로 연다. ("r") 만일 file이 제대로 열리지 않을 시 에러메세지로 경고한다.

 

 

③ memory에서 더 이상 읽을 데이터가 없을 때까지 조건문

: 파일에서 데이터를 읽을 때, 일시적으로 그 데이터를 보관할 "buffer"가 필요하다. 데이터가 512 bytes로 저장되어 있으므로, 해당 사이즈로 buffer을 만들면,

uint8_t buffer[512];

이렇게 만들 수 있겠지만, 코드에 constant(magic number)를 쓰는 것은 바람직하지 않으므로 보완하여 다시 쓸 수 있다. 

Notice that there's no ; when you use #define

 

그 다음, fread()를 사용하여 계속해서 남은 데이터를 읽는다. 이 때, fread() returns the number of bytes it has read, so make sure that the condition is equal to BLOCKSIZE(512).

 

 

④ If start of new JPEG

: 앞에서 memory block은 JPEG의 시작일 수도, 아닐 수도 있다고 언급했다. 먼저 JPEG의 시작일 경우 조건문을 생각해보자.

  buffer[BLOCKSIZE] 중 첫 번째~세 번째의 bytes는 알고 있다.

if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && buffer[3] == ?)

 

여러가지 경우의 수가 있는 네 번째 byte는 어떻게 표현할까?

 

★ bitwise arithmetic (operation) = 비트 연산

: bit 단위로 연산을 할 때 사용되는 연산자. 

 

&는 대응되는 비트가 모두 1이면 1을 반환한다.  예를 들어보자.

byte binary
0xf0 1111    0000
buffer[3] whatever
0x  0 1111     0000

0xf0을 binary로 변환시키면 1111  0000이 되는데, 이는 buffer[3]가 뭐가 됐든 대응했을 때 결과가 1111  0000이 나온다는 것을 의미한다. 이는 결국 0x0으로 나올 것인데, 앞에서 0xe. 라고 했으므로 0xe0이 될 것이다.

 

따라서 다음과 같은 조건문을 쓸 수 있다:

 

 

JPEG이 시작됐을 경우, 순서에 맞게 파일을 새로운 이름으로 저장해야 한다. 순서를 알기 위해 먼저 counter을 정의해서 파일 수를 센다.

★ 000부터 시작해야 하므로 counter = -1로 정의한다.

또한, 파일을 넣어둘 변수 filename과 새로 쓴 JPEG 파일을 저장할 새로운 파일 outputFile을 정의한다.

%03i.jpg : %03을 앞에 붙이는 것은 세 자리 int 자리를 의미한다. 

 

그리고 outputFile에 새로이 쓴다:

 

 

 

이미 JPEG파일을 찾은 경우 

: outputFile을 닫는다. 하지만 이 부분은 새로운 파일을 열기 전에 먼저 실행되어야 하므로 앞의 fwrite 앞에 위치한다. 

 

 

 

새 JPEG 파일의 시작점이 아닌 경우 (중간 혹은 끝)

: 계속 write 한다.

 

 

 

⑤ 열어둔 파일을 모두 닫기

 

 

완성된 Recover 코드를 보면 다음과 같다 :

 

 


원래 시작보다 중간이 더 어려운 거라고 했나요..

누가 그랬더라.. 하지만 뭐든지 "지옥의 중간 과정"을 잘 넘어가면 더 깊은 깨달음이 있을 거라는 얘기를 어디선가 들은 것 같아!!

그러니까 포기하지 않는 것이 중요한 것이겠쬬 움움

 

다음 Week 5도 강의 듣고 내용 정리해서 오겠습니다아 ~.~ 화 이 팅 얏호!

'CS50' 카테고리의 다른 글

[CS50] Week 5 Review  (4) 2024.10.28
[CS50] Week 4 Section  (1) 2024.10.10
[CS50] Week 4 Review  (4) 2024.10.09
[CS50] Pset 3 : Sort, Plurality, Runoff  (1) 2024.10.06
[CS50] Week 3 Section  (2) 2024.10.03

 

1. Pointers (and why to use them)

 

  • A variable : a name for some value that can change → has an address somewhere in memory
  • type : int *
  • name : p
  • value : &calls (the address of calls)

 

Reasons to use pointers

 

Pointers enable additional capabilities like:

  • You could write a function that allows you to pass something in by reference and not just by copy.
    The code you write is cleaner as a result.
  • You could ask your program for dynamic memory depending on what the user actually needs.
    Your programs can now scale their usage of memory according to user behavior.

 

 


 

2. Passing by Copy vs Passing by Reference

 

 

<Passing by Copy>

This seemed to be swapped in swap, but it's still the same in main function !

 

 

<Passing by Reference>

By copying the addresses of a and b(the pointer to a/ the pointer to b), we actually copy in the address, and we change the values exactly where those values currently in memory.

 

 

Let's debug this in VS Code and see step-by-step:

Here's my code swapping two values, a and b.

If I run debug50 :

① Notice that a = 10, b = 50

    Let's step into the swap function!

② In variable window, you can see the addresses of a and b now. (&a, &b)

    Let's step over :

 

③ Notice that the value of location a(*a = 10) is handed over to temp.(temp = 10)

    Let's step over:

 

④ Now, the value of b(*b = 50) is handed over to a.(*a = 50)

    Let's step over for the last time :

 

⑤ Notice that the value of temp(10) is handed over to *b.(*b = 10)

    And now you can check that a and b are swapped.

 

 

 


 

3. FILE I/O

 

 

We can also open up files, read data from them, and write data to those files!

fopen opens a file for future reading/ writing.
fclose closes a file.

 

Always fclose all files you fopen!

 

fopen

"r" means 'read' mode.

"w" means 'write' mode.

 

fclose

 

 

 

<Reading and Writing>

fread reads data from a file into a buffer*.
fwrite writes data from a buffer* to a file.

*a buffer is a chunk of memory that can temporarily store some data from the file.

 

Why do we need a buffer?

→ We often don't know how big a file is, so the best we can do is to look at small bits of it, not the entire file itself.

 

 

<Reading from a File>

  • From where are you reading?
  • To where are you reading?
  • What size is each block of data you want to read? (ex. text file-1 byte/ image file-3 bytes)
  • How many blocks do you want to read?

fread(Towhere, what size , how many , Fromwhere);

ex) fread(buffer, 1, 4, f);

 

 

<Practice with Reading>

  • Create a program, pdf.c, that opens a file given as a command-line argument.
  • Check if that file is a PDF. A PDF always begin with a four-byte sequence, corresponding to these integers:
    37, 80, 68, 70

① Open up a file

② Create a buffer

③ Use fread to read 4 individual bytes from that file

④ Print those out

 

  • uint8_t : Unsigned Int of 8 bytes (always positive, takes 8 bits or 1 byte long)
                   Meaning, you can store a positive Integer with max number being 2^8-1.

If we use int buffer instead of uint8_t, we might not get the values we expect,

because int buffer[4] is 16 bytes long(an integer is 4 bytes long), whereas we're only going to read four of them.

 

 

 

 

<Writing to a File>

 

:basically the same as fread

fwrite(buffer, 1, 4, f);

 

'CS50' 카테고리의 다른 글

[CS50] Week 5 Review  (4) 2024.10.28
[CS50] Pset 4 : Volume, Filter-less, Recover  (5) 2024.10.22
[CS50] Week 4 Review  (4) 2024.10.09
[CS50] Pset 3 : Sort, Plurality, Runoff  (1) 2024.10.06
[CS50] Week 3 Section  (2) 2024.10.03

Week 4

: Memory


 

Lecture Contents

 

 

 

 

 

1. Hexadecimal (base-16)

 

 

: a system of counting that has 16 counting values, which are:

0 1 2 3 4 5 6 7 8 9 A B C D E F

 

 

When counting is hexadecimal, each column is a power of 16.

number 0 1 9 10 15 16 255
represented 00 01 09 0A 0F 10 FF

 

digit1  digit2

 □      □

16^1    16^0

 

ex) FF : 16 x 15 + 1 x 15 = 255 (the highest number you can count using a two-digit hexadecimal system.)

 

Hexadicamal is useful because it can be represented using fewer digits. It allows us to represent information more succinctly. 

 

 

 

 

 

2. Memory

 

 

When you apply hexadicimal numbering to each of blocks of memory, it can be visualized like this:

 

However, there may be confusion regarding whether the '10' block represents a location in memory of the value '10'.

Accordingly, all hexadecimal numbers are often represented with the '0x' prefix as follows:

 

C language has two powerful operators that relate to memory:

& provides the address of something stored in memory
* instructs the compiler to go to a location in memory
#include <stdio.h>

int main(void)
{
	int n = 50;
    printf("%p\n", &n);
}

→ would return an address of memory beginning with '0x.'

 

%p : allows us to view the address of a location in memory

&n : "the address of n"

 

 

 

 

 

3. Pointers

 

 

: a variable that contains the address of some value. 

#include <stdio.h>

int main(void)
{
	int n = 50;
    int *p = &n;
    printf("%p\n", p);
 }

Notice that p is a pointer that contains the address of an integer n.

 

It has the same effect as our previous code. We have simply leveraged our new knowledge of the & and * operators.

 

#include <cs50.h>

int main(void)
{
	int n = 50;
    int *p = &n;
    printf("%i\n", *p);
}

'printf' line prints the integer at the location of p. 

'int *p' creates a pointer whose job is to store the memory address of an integer.

P is a pointer storing the address of the 50.

Visualizing a pointer

 

 

 

 

 

4. Strings

 

 

Recall that string is simply an array of characters.

ex) string s = "HI!" :

→ What is s really?

Notice how a pointer called s tells the compiler where the first byte of the string exists in memory.

 

#include <cs50.h>
#include <stdio.h>

int main(void)
{
	string s = "HI!";
    printf("%p\n", s);
    printf("%p\n", &s[0]);
    printf("%p\n", &s[1]);
    printf("%p\n", &s[2]);
    printf("%p\n", &s[3]);
}

→ In this code, it prints the memory locations of each character in the string s.

    When running this code, elements 0, 1, 2, and 3 are next to one another in memory.

 

#include <stdio.h>

int main(void)
{
	char *s = "Hi!";
    printf("%s\n", s);
}

This code will present the string that starts at the location of s.

 

  • string = char *

 

Struct allows one to use a custom data type called string : 

typedef char *string

 

 

 

 

 

5. Pointer Arithmetic

 

 

To print each character at the location of s :

#include <stdio.h>

int main(void)
{
	char *s = "HI!";
    printf("%c\n", *s);
    printf("%c\n", *(s+1));
    printf("%c\n", *(s+2));
}

 

 

 

 

 

6. String Comparison

 

 

In case of comparing two integers:

#include <cs50.h>
#include <stdio.h>

int main(void)
{
	//Get two integers
    int i = get_int("i: ");
    int j = get_int("j: ");
    
    //Compare integers
    if (i == j)
    {
    	printf("Same\n");
    }
    else
    {
    	printf("Different\n");
    }
}

 

In case of comparing two strings

#include <cs50.h>
#include <stdio.h>

int main(void)
{
	//Get two strings
    char *s = get_string("s: ");
    char *t = get_string("t: ");
    
    //Compare strings' addresses
    if (s == t)
    {
    	printf("Same\n");
    }
    else
    {
    	printf("Different\n");
    }
}

→ If you type in HI! for both strings, it still results in the output of Different. But Why?

 

Now you realize that the code above is actually attempting to see if the memory addressess are different: not the strings

 

We can correct our code using strcmp :

#include <cs50.h>
#include <stdio.h>

int main(void)
{
	//Get two strings
    char *s = get_stirng("s: ");
    char *t = get_string("t: ");
    
    //Compare strings
    if (strcmp(s,t) == 0)
    {
    	printf("Same\n");
    }
    else
    {
    	printf("Different\n");
    }
}

 

 

 

 

 

6. Copying

 

 

: to copy one string to another

#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>

int main(void)
{
	//Get a string
    string s = get_string("s: ");
    
    //Copy string's address
    string t = s;
    
    //Capitalize first letter in string
    t[0] = toupper(t[0]);
    
    //Print string twice
    printf("s: %s\n", s);
    printf("t: %s\n", t);
}

string t = s : copies the address of s to t.

But the string is not copied - only the address is.

Notice that s and t are still pointing at the same blocks of memory.

 

We should not experience a segmentation fault through our code. 
We attempt to copy string s to string t, but string t does not exist.

 

We can employ the strlen function as follows:

#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>

int main(void)
{
	//Get a string
    string s = get_string("s: ");
    
    //Copy string's address
    string t = s;
    
    //Capitalize first letter in string
    if (strlen(t) > 0)
    {
		t[0] = toupper(t[0]);
    }
    
    //Print string twice
    printf("s: %s\n", s);
    printf("t: %s\n", t);
}

strlen is used to make sure string t exists.

 

To be able to make an authentic copy of the string, we need two new building blocks.

1) malloc : allows you to allocate a block of a specific size of memory

2) free : allows you to tell the compiler to free up that block of memory you previously allocated

 

#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>

int main(void)
{
	//Get a string
    char *s = get_string("s: ");
    
	//Allocate memory for another string
    char *t = malloc(strlen(s) + 1);
    
    //Copy string into memory, including '\0'
    for (int i = 0, n = strlen(s); i <= n; i++)
    {
    	t[i] = s[i];
    }
    
    //Capitalize first letter in string
    t[0] = toupper(t[0]);
    
    //Print string twice
    printf("s: %s\n", s);
    printf("t: %s\n", t);
}
  • malloc(strlen(s) + 1) : includes the null (\0) character in our final, copied string.
  • for loop : walks through the string s and assigns each value to the same location on the string t.
  •  i <= n : the equal sign (=) includes the null (\0) character

C language has a built-in function to copy strings called strcpy.

#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char *s = get_string("s: ");
    char *t = malloc(strlen(s) + 1);
    
    strcpy(t, s);
    
    t[0] = toupper(t[0]);
    
    printf("s: %s\n", s);
    printf("t: %s\n", t);
}

strcpy does the same work that our for loop previously did.

 

You can write code that can check for NULL condition as follows:

#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char *s = get_string("s: ");
	if(s == NULL)
    {
    	return 1;
    }
    
    char *t = malloc(strlen(s) + 1);
    if(s == NULL)
    {
    	return 1;
    }
    
    strcpy(t, s);
    
    if (strlen(t) > 0)
    {
    	t[0] = toupper(t[0]);
    }
    
    printf("s: %s\n", s);
    printf("t: %s\n", t);
    
    free(t);
    return 0;
}
  • If the string obtained is of length 0 or malloc fails, NULL is returned.
  • free lets the computer know you are done with this block of memory you created via malloc.

 

 

 

 

 

7. malloc and Valgrind

 

Valgrind : a tool that can check to see if there are memoroy-related issues with programs wherein you utilized malloc.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int *x = malloc(3 * sizeof(int));
    x[1] = 72;
    x[2] = 73;
    x[3] = 33;
}

In your terminal window, type in make memory and valgrind ./memory

: Notice that it points out some bugs here, which are

Invalid write of size 4 (in line 9)

12 bytes in 1 blocks are definitely lost in loss record 1 of 1 (in line 6)

 

We can fix this by 

① x[0] = 72;

    x[1] = 73;

    x[2] = 33;

We never freed x after using malloc, so this code below has to be involved at last

free(x);

 

 

 

 

 

 

8. Garbage Values

 

 

When you ask the compiler for a block of memory  → Is this memory empty?

: We cannot guarantee !

 

It's possible that this memory you allocated was previously utilized by the computer, so you may see junk or garbage values.

★ After you allocate a block of memory, you have to initialize it.

#include <stdio.h>

int main(void)
{
    int scores[1024];
    for (int i = 0; i < 1024; i++)
    {
        printf("%i\n", scores[i]);
    }
}

Running this code will allocate 1024 locations in memory for your array. In your terminal window, you'll see this:

garbage values

 

 

 

 

 

9. Swap

 

 

How do we swap two values?

 

#include <stdio.h>

void swap(int a, int b);

int main(void)
{
    int x = 1;
    int y = 2;

    printf("x is %i, y is %i\n", x, y);
    swap(x, y);
    printf("x is %i, y is %i\n", x, y);
}

void swap(int a, int b)
{
    int tmp = a;
    a = b;
    b = tmp;
}

Here, we created our own swap function whose arguments are int a and int b.

We used a temporary holding space  'int tmp' to swap a and b. But will it work?

Hmm... seems like two values are not swapped. Why?

 

Let's learn about scope first.

  • scope: (변수에 접근할 수 있는) 범위
  • Global scope : 전역 스코프. 전역에 선언되어 있어 어느 곳에서든 해당 변수에 접근 가능.

The values of x and y created in the { } of the main function only have the scope of the main function :

Various functions are stored in the stack in memory.

Now, consider the following image:

Notice that main and swap have two spearate frames or areas of memory

Even though we swapped two variables in swap(), the function will not exist afterwards. 

#include <stdio.h>

void swap(int *a, int *b);

int main(void)
{
    int x = 1;
    int y = 2;

    printf("x is %i, y is %i\n", x, y);
    swap(&x, &y);
    printf("x is %i, y is %i\n", x, y);
}

void swap(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

Now it works! What did I change?

 

★ Notice that variables are not passed by value but by reference.

     That is, the addresses of a and b are provided to the function. 

     Therefore, the swap function can know where to make changes to the actual a and b from the main function.

Follow the arrows !

 

 

 

 

 

 

10. scanf ★

 

 

Instead of functions like get_int in CS50, scanf is a built-in function that can get user input :

#include <stdio.h>

int main(void)
{
	int x;
    printf("x: ");
    scanf("%i", &x);
    printf("x: %i\n", x);
}

★ Notice that the value of x is stored at the location of x in the line (scanf("%i", &x))

 

However, attempting to reimplement get_string is not easy :

#include <stdio.h>

int main(void)
{
	char *s;
    printf("s: ");
    scanf("%s", s);
    printf("x: %s\n", s);
}

★ Notice that no & is required because strings are special. 

The program will not function, because we never allocated the amount of memory required for our string.

Indeed, we don't know how long of a string may be inputted by the user. (100? 123456?)

#include <stdio.h>

int main(void)
{
	char *s = malloc(4);
    if (s == NULL)
    {
    	return 1;
    }
    printf("s: ");
    scanf("%s", s);
    printf("s: %s\n",s);
    
    free(s);
    return 0;
}

We provided 4 bytes, so we might get an error if the user input is more than 4 bytes.

 

#include <stdio.h>

int main(void)
{
	char s[4];
    printf("s: ");
    scanf("%s", s);
    printf("s: %s\n", s);
}

We pre-allocate an array of size 4, but a string larger than this could create an error.

 

- Sometimes, the compiler or the system running it may allocate more memory than we indicate.

- We cannot trust the user will input a string that fits into our pre-allocated memory.

 

 

 

 

 

11. File I/O

 

 

You can read from and manipulate files.

#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    //Open CSV file
    FILE *file = fopen("phonebook.csv", "a");

    //Get name and number
    char *name = get_string("Name: ");
    char *number = get_string("Number: ");

    //Print to file
    fprintf(file, "%s,%s\n", name, number);

    //Close file
    fclose(file);
}

 

 

If we want to ensure that phonebook.csv exists prior to running the program, we can modify our code as follows :

#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    //Open CSV file
    FILE *file = fopen("phonebook.csv", "a");
    if(!file)
    {
    	return 1;
    }

    //Get name and number
    char *name = get_string("Name: ");
    char *number = get_string("Number: ");

    //Print to file
    fprintf(file, "%s,%s\n", name, number);

    //Close file
    fclose(file);
}

 

We can implement our own copy program :

#include <stdio.h>
#include <stdint.h>

typedef uint8_t BYTE;

int main(int argc, string argv[])
{
    FILE *src = fopen(argv[1], "rb");
    FILE *dst = fopen(argv[2], "wb");

    BYTE b;

    while(fread(&b, sizeof(b), 1, src) != 0)
    {
        fwrite(&b, sizeof(b), 1, dst);
    }

    fclose(src);
    fclose(dst);
}
  • This file creates our own data type called a BYTE that is the size of a uint8_t.
  • The file reads a BYTE and writes it to a file.

 

 

 

 


4주차에는 컴퓨터의 메모리에 대해서 알아보았다.
우리가 쓰는 코드에 따라서 메모리가 어디에, 어떻게, 얼마나 저장되는지 알 수 있었고 

pointer라는 개념을 통해서 메모리에서 변수와 값이 이동하는 복합적인 구조도 이해할 수 있었다!

또한 메모리와 관련된 debug는 valgrind를 통해 해결할 수 있어 더 간편하다고 생각했다. 

메모리라는 것이 눈으로 직접 보이지 않아 추상적이고 어렵게 느껴졌던 것도 분명하지만, 기기마다 메모리의 용량은 정해져 있기 때문에 메모리의 적절한 관리가 눈으로 보이는 코드보다 오히려 더 중요하다고 느껴졌다.

 

내일 4주차 section으로 돌아오겠숴! 그럼 오늘도 수고많았다 나 자신 ㅎ ㅏ ㅎ ㅏ

'CS50' 카테고리의 다른 글

[CS50] Pset 4 : Volume, Filter-less, Recover  (5) 2024.10.22
[CS50] Week 4 Section  (1) 2024.10.10
[CS50] Pset 3 : Sort, Plurality, Runoff  (1) 2024.10.06
[CS50] Week 3 Section  (2) 2024.10.03
[CS50] Week 3 Review  (1) 2024.10.01

 

 

 

 

알고리즘에 관한 3주차 과제 3가지입니다!

Sort, Plurality, Runoff 로 구성되어 있고, 각각의 문제 해결 방법과 코드, 중요한 부분들을 정리해보겠습니다.

 

 

누가 Tideman 고르겠냐구요... ??? very..very..very 아주가 3개나 들어간

 

사실 쉬운 버전과 어려운 버전 중에 1주차에는 호기롭게 어려운 Credit을 골랐다가 된통 혼났더랬죠...

쉬운 버전이 쉬운 버전이 아님.. Credit에도 한 여섯 시간 이상은 투자하고 머리 싸맸던 것 같아요

그래서 자존심이고 공부 가치관이고 뭐고 12월 말 안에 일단 쉬운 버전으로 과제는 다 제출해놓자고 마음을 먹고.. 이후에 코딩이라는 것에 좀 익숙해졌을 때쯤 어려운 버전들을 차근차근 해 나가 보겠다는 마인드.. ㅎㅎㅎ

 

아무튼 시작!

 


 

1. Sort

 

: Analyze three sorting programs(merge sort/ selection sort/ bubble sort) to determine which algorithms they use.

 

→ cs50에서 미리 작성되어 제공된 distribution code files를 다운받아 reversed/ random/ sorted list (5000/10000/50000) 각각을 sort하는 데에 걸리는 시간을 측정, 비교하여 어떤 sort type인지 맞히는 과제

 

우선 시간을 측정할 때에는 (terminal window):

time ./sort1 reversed5000.txt

 

위의 형식으로 명령어를 입력하면, 시간을 측정해 준다. 

단순히 프로그램이 list를 sort해주고, 시간도 측정해 주기 때문에 데이터를 분석하는 일이 내 몫인데, 아래 표를 참고하면 도움이 된다.

 

  the worst case (O) the best case (Ω)
merge sort O(n log(n)) Ω(n log(n))
selection sort O(n^2) Ω(n^2)
bubble sort O(n^2) Ω(n)

 

  • sort 1 : 다른 방법들보다 대체적으로 시간이 오래 걸리며, 50000개의 경우 reversed와 sorted list의 시간이 차이가 많이 난다      
                 → O와 Ω의 차이가 큰 BUBBLE SORT
  • sort 2 : 가장 적은 시간이 걸리며, O와 Ω의 차이가 거의 없다
                → MERGE SORT
  • sort 3 : 중간 정도의 시간이 걸리며, O와 Ω의 차이가 거의 없다
                → SELECTION SORT

 

 


2. Plurality

 

: In the plurality vote, every voter gets to vote for one candidate. At the end of the election, whichever candidate has the greatest number of votes is declared the winner of the election.

Implement a program that runs a plurality election.

 

2번째 과제도 이미 cs50에 의해 일부 작성되어 있는 distribution code를 다운받아 빈칸을 채우는 형태였습니다! 

오리지널 코드는 다음과 같습니다 :

 

#define MAX 9 

: candidate candidates[MAX] 에서 사용함으로써, 변수 candidates의 최대 숫자를 9로 제한한다는 뜻

 

★ if (argc < 2)

: command-line argument에 input이 적어도 하나 있어야 한다는 뜻

 

 

 

★ candidate_count = argc - 1

: 하나를 차지하는 './plurality' 를 빼고 센 숫자가 candidate_count에 들어간다는 뜻

 

★ for loop 안의 candidates[i].name = argv[i + 1];

: i가 0에서 후보자 수만큼 증가하는 루프 안에서, './plurality'를 뺀 첫 번째 후보자의 이름부터 candidates[i].name에 들어간다는 뜻

 

 

 

 

TODO #1

voter들이 투표할 때마다 각각의 후보자의 total vote 수를 업데이트 시켜야 함.

 

 

for (int i = 0; i < candidate_count; i++)
{
	if (strcmp(name, candidates[i].name) == 0)
    {
    	candidates[i].vote ++;
        return true;
    }
}
return false;

 

★ String끼리 비교할 때는 함수 strcmp(  ,  ) == 0 사용

 

 

 

TODO #2

가장 많은 표를 받은 사람을 찾아 print 하기 (sorting 사용할 필요 x)

void print_winner(void)
{
	int highest_vote = 0;
    for (int i = 0; i < candidate_count; i++)
    {
    	if (candidates[i].votes > highest_vote)
        {
        	highest_vote = candidate[i].votes;
        }
    }
    
    for(int i = 0; i < candidate_count; i++)
    {
		if(candidates[i].votes == highest_vote)
        {
    		printf("%s\n", candidate[i].name);
        }
   }
}

 

// Find the maximum number of votes

→ candidates[i].votes를 iterate 하기 위한 for loop를 만들고, 그 속에 highest_vote와 각 후보자의 투표 수를 비교해서 큰 수를 highest_vote에 넣어 업데이트 시킨다. 

 

// Print the candidate with maximum votes

→ candidates[i].votes를 iterate 하기 위한 for loop를 만들고, 그 속에 각 후보자의 투표 수가 highest_vote와 동일하다면 해당 후보자의 이름인 candidates[i].name을 프린트하는 함수를 사용하여 문제를 해결한다.

 

 

terminal window


 

3. Runoff

 

: In an instant runoff election, voters can rank as many candidates as they wish. If any candidate has a majority (more than 50%) of the first preference votes, that candidate is declared the winner of the election. The candidate who received the fewest number of votes is eliminated from the election, and anyone who originally chose that candidate as their first preference now has their second preference considered, and so on.

 

간략히 말해서 2번째 과제와 비슷한 선거 시스템을 만드는데, 앞의 plurality와 달리 투표 순서에 따라 선호도가 만들어져 반영됩니다. 50% 이상의 지지율을 받은 후보는 바로 그 선거의 우승자가 되지만, 그렇지 않을 경우 가장 적은 표를 받은 선거는 투표에서 제거되고, 두 번째로 선호도가 높은 후보가 선거 결과에 반영됩니다. 이 방식의 장점은 투표자들의 선호를 더 잘 반영한다는 장점을 가지고 있습니다.

 

 

이번에도 사전에 cs50에 의해 작성되어 다운받은 distribution code는 다음과 같습니다 :

#include <cs50.h>
#include <stdio.h>

// Max voters and candidates
#define MAX_VOTERS 100
#define MAX_CANDIDATES 9

// preferences[i][j] is jth preference for voter i
int preferences[MAX_VOTERS][MAX_CANDIDATES];

// Candidates have name, vote count, eliminated status
typedef struct
{
string name;
int votes;
bool eliminated;
} candidate;

// Array of candidates
candidate candidates[MAX_CANDIDATES];

// Numbers of voters and candidates
int voter_count;
int candidate_count;

// Function prototypes
bool vote(int voter, int rank, string name);
void tabulate(void);
bool print_winner(void);
int find_min(void);
bool is_tie(int min);
void eliminate(int min);

int main(int argc, string argv[])
{
// Check for invalid usage
if (argc < 2)
{
printf("Usage: runoff [candidate ...]\n");
return 1;
}

// Populate array of candidates
candidate_count = argc - 1;
if (candidate_count > MAX_CANDIDATES)
{
printf("Maximum number of candidates is %i\n", MAX_CANDIDATES);
return 2;
}
for (int i = 0; i < candidate_count; i++)
{
candidates[i].name = argv[i + 1];
candidates[i].votes = 0;
candidates[i].eliminated = false;
}

voter_count = get_int("Number of voters: ");
if (voter_count > MAX_VOTERS)
{
printf("Maximum number of voters is %i\n", MAX_VOTERS);
return 3;
}

// Keep querying for votes
for (int i = 0; i < voter_count; i++)
{

// Query for each rank
for (int j = 0; j < candidate_count; j++)
{
string name = get_string("Rank %i: ", j + 1);

// Record vote, unless it's invalid
if (!vote(i, j, name))
{
printf("Invalid vote.\n");
return 4;
}
}

printf("\n");
}

// Keep holding runoffs until winner exists
while (true)
{
// Calculate votes given remaining candidates
tabulate();

// Check if election has been won
bool won = print_winner();
if (won)
{
break;
}

// Eliminate last-place candidates
int min = find_min();
bool tie = is_tie(min);

// If tie, everyone wins
if (tie)
{
for (int i = 0; i < candidate_count; i++)
{
if (!candidates[i].eliminated)
{
printf("%s\n", candidates[i].name);
}
}
break;
}

// Eliminate anyone with minimum number of votes
eliminate(min);

// Reset vote counts back to zero
for (int i = 0; i < candidate_count; i++)
{
candidates[i].votes = 0;
}
}
return 0;
}

// Record preference if vote is valid
bool vote(int voter, int rank, string name)
{
// TODO
return false;
}

// Tabulate votes for non-eliminated candidates
void tabulate(void)
{
// TODO
return;
}

// Print the winner of the election, if there is one
bool print_winner(void)
{
// TODO
return false;
}

// Return the minimum number of votes any remaining candidate has
int find_min(void)
{
// TODO
return 0;
}

// Return true if the election is tied between all candidates, false otherwise
bool is_tie(int min)
{
// TODO
return false;
}

// Eliminate the candidate (or candidates) in last place
void eliminate(int min)
{
// TODO
return;
}

 

참 긴데요.. 하나하나 뜯어보면서 같이 과제를 해결해보아요 ^~^

 

 

 

2번째 과제와 다른 부분이라고 한다면 minimum votes수를 가진 후보를 제거한다는 점에서

candidate struct 안에 bool eliminated; 가 추가되었습니다!

 

 

투표자들이 투표함에 따라서 각각에 따른 Rank를 매기고, vote 형식에 맞지 않을 경우 'Invalid vote.'를 프린트하는 부분입니다.

 

ⓐ outer loop (int i)

: 각각의 투표자들을 iterate 하는 loop

ⓑ inner loop (int j)

: 각각의 투표자들의 각각의 rank를 iterate 하는 loop

ⓒ prompt name

string name = get_string("Rank %i:", j + 1);

: j + 1 인 이유는 rank가 0이 아니라 1에서부터 시작하기 때문

 

ⓓ record vote

if (!vote(i, j, name))

: vote라는 함수를 불러 invalid 할 경우 해당 문구를 프린트

 


 

그리고 여기서부터 작성해야 하는 과제 코드 시짝!!

 

1) Vote function

bool vote(int voter, int rank, string name)
{
	 //TODO
}

: VOTE function 에서는 투표를 통해 받은 이름과 후보자의 이름이 동일할 경우, preferences array를 투표자와 rank를 반영하도록 update 해야합니다.

 

이 부분이 제일 어려웠음 무슨 말인지...???

for (int i = 0; i < candidate_count; i ++)
{
	if (strcmp (name, candidates[i].name) == 0)
    {
    	preferences[voter][rank] = i;
        return true;
    }
}
return false;

 

① 모든 후보자를 포괄하도록 후보자 수만큼 loop를 돌려주고,

② 투표를 통해 받은 이름(name)과 후보자 이름(candidates[i].name)이 동일할 경우를 strcmp 함수로 조건문을 적어주기

③ 같을 경우, preferences[voter][rank] 배열에 랭크 i를 배치해주기

 

→ 이로써, 각 투표자들의 각 이름별로 rank가 형성된 preferences[] 가 생겨나게 됨!

 

 

 

 

2) Tabulate function

void tabulate(void)
{
	//TODO
}

: Tabulate function에서는 제거되지 않은 후보자들에 대하여 각 후보자들이 받은 투표 수 만큼 votes 수를 업데이트 해야합니다.

 

for (int i = 0; i < voter_count; i++)
{
	for (int j = 0; j < candidate_count; j++)
    {
		if (!candidate[preferences[i][j]].eliminated) 
        {
        	candidate[preferences[i][j]].votes ++;
            break;
        }
    }
}

 

① if 조건문

if (!candidate[preferences[i][j]].eliminated) : candidate[i].eliminated 대신에 이렇게 쓰인 것은 각 rank로 업데이트 된 선호도에 따라서 candidate를 더 세분화해서 봐야 하기 때문입니다.

 

 

 

 

3) Print_winner function

 

bool print_winner(void)
{
	//TODO
}

: Print_winner function에서는 만약 한 후보자의 득표수가 과반수를 넘긴 경우 이름을 프린트하고, 아무도 이긴 사람이 없다면 false로 마무리 해야합니다.

 

for (int i = 0; i < candidate_count; i++)
{
	if (candidate[i].votes > (voter_count / 2))
    {
    	printf("%s\n", candidates[i].name);
        return true;
    }
}
return false;

 

① if 조건문

candidate[i].votes 즉 후보자가 얻은 득표수가 (voter_count / 2) 즉 전체 투표자수 / 2 = 과반수를 넘길 때를 말하는 조건문

 

 

 

 

 

4) Find_min function

int find_min(void)
{
	//TODO
}

: find_min function에서는 여전히 선거에 참여하는 후보자들 중 가장 적은 득표수의 후보를 찾습니다.

 

int min = candidates[0].votes;

for (int i = 0; i < candidate_count; i++)
{
	if(!candidates[i].eliminated && candidates[i].votes < min)
    {
    	min = candidates[i].votes;
    }
}
return min;

 

① int min = candidates[0].votes;

: min에 첫 번째 후보자의 투표 수 값을 배정

② if 조건문

: 각 후보자가 제거되지 않았을 경우(!candidates[i].eliminated), 그 후보자의 득표수가 min보다 작을 때

③ min에 새로운 값 배정

: min = candidates[i].votes

 

 

 

 

 

5) is_tie function

bool is_tie(int min)
{
	//TODO
}

: is_tie function에서는 선거에 남아있는 후보자들의 득표수가 min으로 모두 같을 때 true를 return함

 

for (int i = 0; i < candidate_count; i++)
{
	if(!candidates[i].eliminated && candidates[i].votes != min)
    {
    	return false;
    }
}
return true;

 

여기서도 true일 경우의 조건문을 찾느라 고생고생하다가, 관점을 바꾸어 false일 경우를 정의하고, 그것이 아닐 때 return true;를 하게 되니 더 쉬웠습니다. (cs50 duck 덕분에 ㅎ..)

 

★ 후보자가 제거되지 않고(1), 득표 수가 하나라도 min과 동일하지 않을 때(2) → return false;

(1) !candidates[i].eliminated

(2) candidates[i].votes != min

 

 

 

 

 

6) Eliminate function

void eliminate(int min)
{
	//TODO
}

: Eliminate function에서는 min 득표수를 가지고 있는 후보자를 제거합니다.

 

for (int i = 0; i < candidate_count; i++)
{
	if (candidates[i].votes == min)
    {
    	candidates[i].eliminated = true;
    }
}
return;

 

이건 비교적 간단한 코드입니다!

주의할점은 candidates[i].eliminated = true; 에서 등호를 두 개가 아닌 하나 적는 것!

 

* = : 값을 할당한다

* == : 두 값이 같다

 


 

 

휴~ 여기까지 무사히 Week0부터 Week3까지 한 달치를 끝냈네요!

갈수록 Pset이 어려워지고 더 길어져서 무섭습니다만 차근차근 파고들어가보면 그렇게 못 할 것도 아닌 것 같기도 하고.. 모르겠어요

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

지레 겁먹기보다는 할 수 있다고 생각하고 덤비는 게 코딩을 대하는 좋은 자세 같아요.

컴퓨터 짜식이.... 나는 인간인데..... 

 

그래도 1/3을 마무리하는 이 시점이 아주 뿌듯 ! 행복 ! 합니다 ㅎㅎ 

시작할 때부터 원했던 정말 끈기를 길러주는 코딩 녀석.. 한 번 시작하니 오기가 생겨 정말 올해 안에 끝내고 싶네요 ^~^

다음 주도 화이팅! ♥

'CS50' 카테고리의 다른 글

[CS50] Week 4 Section  (1) 2024.10.10
[CS50] Week 4 Review  (4) 2024.10.09
[CS50] Week 3 Section  (2) 2024.10.03
[CS50] Week 3 Review  (1) 2024.10.01
[CS50] Pset 2 : Scrabble, Readability, Caesar  (1) 2024.09.29

 

 

 

1. Thought Question ( Ω notation )

 

  • Suppose that you create a new algorithm and access its runtime.
  • The fewest steps this algorithm will ever take is 2, and only 2.
  • What is the Ω notation for this algorithm?

→ My answer : Ω(2) 

→ The answer : Ω(1)

     :  Computer scientists like to think of things as being on the order of some number of steps. And whether it takes two, three, four, if it takes some fixed number of steps, we'll say it operates in  Ω(1) to symbolize that it just takes some finite number of steps in the best case.

 

★ We don't care about small numbers! We just care about these common notations:

  • O(1) / O(log(N)) / O(N) / O(N^2)
  •  Ω(1) /  Ω(log(N)) /  Ω(N) /  Ω(N^2)

 

 

 

 

2.   Factorial

 

: the product of an integer and all the integers below it 

 

ex)

            1! = 1

        2! = 2*1

    3! = 3*2*1

4! = 4*3*2*1 

...

 

→ The concept of recursion 

1! = 1 (base case)

2! = 2*1!

3! = 3*2!

4! = 4*3!

 

int factorial(int n)
{
	//Implement factorial function
    
    //Base case
    if (n == 1)
    {
    	return n;
    }
    
    return n * factorial(n - 1);
}

 

 

Q. Is recursion faster than using regular loop?

A. It certainly is. At the same time, it isn't always necessarily faster. 
     It just means we're trying to solve the problem by first solving a smaller problem in that question.

 

 

'CS50' 카테고리의 다른 글

[CS50] Week 4 Review  (4) 2024.10.09
[CS50] Pset 3 : Sort, Plurality, Runoff  (1) 2024.10.06
[CS50] Week 3 Review  (1) 2024.10.01
[CS50] Pset 2 : Scrabble, Readability, Caesar  (1) 2024.09.29
[CS50] Week 2 Section  (0) 2024.09.24

Week 3

: The design of algorithms and how to measure their efficiency


 

Lecture Contents

 

 

 

 

1.  Linear Search

 

Recall the idea of an array : blocks of memory that are consecutive 

Think of an array of seven red lockers

 

Q. Is the number 50 inside an array?

→ A computer could look at each locker to find if the number 50 is inside. (= searching)

 

algorithm

We can imagine various instructions we might provide our algorithm to undertake this task as follows (pseudocode):

For i from 0 to n-1
	If 50 is behind doors[i]
    	Return true
Return false

 

 

 

 

 

2. Binary search

 

: another search algorithm 

Assuming that the value within the lockers have been arranged from smallest to largest, the pseudocode being like:

If no doors left
	Return false
If 50 is behind doors[middle]
	Return true
Else if 50 < doors[middle]
	Search doors[0] through doors[middle - 1]
Else if 50 > doors[middle]
	Search doors[middle + 1] through doors[n-1]

 

 

 

 

 

3. Running Time

 

-uses big O notation

 

Computer scientists discuss efficiency in terms of the order of various running times

Some common running times we may see are:

  • O(n^2).     : think of each student raising his/her hand (the worst running time)
  • O(n log n) 
  • O(n)           : linear search 
  • O(log n)     : binary search
  • O(1)            : (the fastest running time)

big O notation: the worst case, or upper bound
omega () : the best case, or lower bound 

theta (Θ) : the upper bound and lower bound are the same

 

 

 

 

4. Search.C

 

Let's implement linear search by writing code (in case of int): 

#include <cs50.h>
#include <stdio.h>

int main(void)
{
	int numbers[] = {20, 500, 10, 1, 100, 5, 50};
    
    int n = get_int("Number: ");
    for (int i = 0; i < 7; i++)
    {
    	if (numbers[i] == n)
        {
        	printf("Found\n");
            return 0;
        }
    }
    printf("Not found\n");
    return 1;
}

 

-return 0 : to indicate success and exit the program

-return 1 : to exit the program with an error

 

(in case of string within an array) :

#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
	string strings[] = {"battleship", "boot", "cannon", "iron", "thimble", "top hat"};
    
    string s = get_string("String: ");
    for (int i = 0; i < 6; i++)
    {
    	if (strcmp(string[i], s] == 0)
        {
        	printf("Found\n");
            return 0;
        }
    }
    printf("Not found\n");
    return 1;
}

 

Notice that we used a function strcmp (), which comes from the string.h library, in order to compare strings.

 

We can combine these ideas of both numbers and strings into a single program (phonebook.c):

#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
	string names[] = {"Carter", "David", "John"};
    string numbers[] = {"+1-617-495-1000", "+1-617-495-1000", "+1-949-468-2750"};
    
    string name = get_string("Name: ");
    for (int i = 0; i < 3; i++)
    {
    	if (strcmp(names[i], name) == 0)
        {
        	printf("Found %s\n", numbers[i]);
            return 0;
        }
    }
    printf("Not found\n");
    return 1;
}

→ However, there are numerous inefficiency. 

    There is a chance that people’s names and numbers may not correspond.

 

 

 

 

5. Data Structures

 

Would it be nice to create our own data type where we could associate a person with the phone number?

  • typedef struct

        {

          variables;

         }

         new name of data type;

#include <cs50.h>
#include <stdio.h>
#include <string.h>

typedef struct
{
	string name;
    string number;
}
person;

int main(void)
{
	person people[3];
    
    people[0].name = "Carter";
    people[0].number = "+1-617-495-1000";
    
    people[1].name = "David";
    people[1].number = "+1-617-495-1000";
    
    people[2].name = "John";
    people[2].number = "+1-949-468-2750";
    
    string name = get_string("Name: ");
    for (int i = 0; i < 3; i++)
    {
    	if (strcmp(people[i].name, name) == 0)
        {
        	printf("Found %s\n", people[i].number);
            return 0;
        }
    }
    printf("Not found\n");
    return 1;
}

 

Notice that a new datatype called person is defined.

: inside a person is a string called name and a string called number.

 

Notice how the dot notation such as people[0].name

: allows us to access the person at the 0th location and assign that individual a name.

 

 

 

 

6. Sorting

 

: the act of taking an unsorted list of values and soriting it.

  There are many different types of search algorithms :

 

ⓐ Selection Sort

 

: n개의 숫자들을 처음부터 끝까지 스캔하면서 가장 작은 숫자와 자리를 바꾸는 것

for i from 0 to n-1
	Find smallest number between numbers[i] and numbers[n-1]
    Swap smallest number with numbers[i]

: a lot of comparison → inefficiency

 

▷ How many steps?

= (n-1) + (n-2) + ... + 1 = n(n-1)/2 = O(n^2)

= Ω(n^2), Θ(n^2)

 

 

ⓑ Bubble Sort

 

: n개의 숫자들을 양 옆에 있는 숫자끼리 비교해서 자리를 바꾸는 것

Repeat n-1 times
	For i from 0 to n-2
    	If numbers[i] and numbers[i+1] out of order
        	Swap them
        If no swaps
        	Quit

We only need to compare the pairs of numbers that haven't been sorted yet

 

▷ How many steps?

=  (n-1) * (n-1) = n^2 -2n +1 = O(n^2)

= Ω(n), because at least you have to check each pair)

 

 

 

★ <recursion>

a concept within programming where a function calls itself

 

Pick up phone book
Open to middle of phone book
Look at page
If person is on page
	Call person
Else if person is earlier in book
	Open to middle of left half of book
    Go back to line 3
Else if person is later in book
	Open to middle of right half of book
    Go back to line 3
Else
	Quit

In our pseudocode for Week 0, this could've been simplified using recursion as follows:

Pick up phone book
Open to middle of phone book
Look at page
If person is on page
	Call person
Else if person is earlier in book
	Search left half of book
Else if person is later in book
	Search right half of book
Else
	Quit

Notice that we used 'Search' function inside of it. (recursion)

 

In Week 1, we created a pyramid structure as follows:

#
##
###
####
#include <cs50.h>
#include <stdio.h>

void draw(int n);

int main(void)
{
    draw(1);
}

void draw(int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("#");
    }
    printf("\n");

    draw(n + 1);
}

Notice that the draw function calls itself : recursion used

However, this code may get caught in an infinite loop because there's nothing telling the program to end.

 

We can correct our code as follows:

#include <cs50.h>
#include <stdio.h>

void draw(int n);

int main(void)
{
    int height = get_int("Height: ");
    draw(height);
}

void draw(int n)
{
    //If nothing to draw
    if(n <= 0)
    {
        return;
    }

    //Draw pyramid of height n-1
    draw (n - 1);

    for (int i = 0; i < n; i++)
    {
        printf("#");
    }
    printf("\n");
}

if(n <=0) : terminates the recursion because the problem have been solved.

    At some point, n-1 will equal 0, resulting in the draw function returning and the program will end.

 

 

ⓒ Merge Sort

 

: n개의 숫자를 반으로 계속 갈라 합쳐나가는 방법, a very efficient sort algorithm

If only one number
	Quit
Else
	Sort left half of number
    Sort right half of number
    Merge sorted halves

 

▷ How many steps?

= log(base)2(of)n  * n = O(n log n)

= Ω(n log n), because the algorithm still must visit each place in the list

= Θ(n log n)

 


Week 3에는 알고리즘에 대해서 배워보았어요

알고리즘은 a procedure used for solving a problem or performing a computation인데, 직역하면 문제를 해결할 때 쓰는 과정이나 방법입니다. Computer science를 배우게 된 계기 중 하나도 문제 해결 능력과 관련해서 다양한 방면으로 생각해보고 싶어서였어요. 하나의 문제를 푸는 데에도 한 가지 방법만 있는 것이 아니라, 더 효율적인 방법들을 찾아나가는 방법을 고심해보게 되었습니당

 

이번 주에 배운 것들 중 기억에 남는 것은 나만의 data type을 만들어 그 속에 정보를 담아 사용하는 typedef struct, 그리고 함수 안에 스스로의 함수를 call 하는 효과적이었던 recursion 개념입니다! 특히 recursion은 저 같은 코딩 초보에게는 실제 코드에서 어떻게 쓸 수 있을지.. 많은 고민을 하게 하는 것 같아요! Pset3에서 직접 이 방법을 사용해보고 정리해서 오도록 하겠슴니당! 이번 주도 홧팅 홧팅 ㅎㅎ 

'CS50' 카테고리의 다른 글

[CS50] Pset 3 : Sort, Plurality, Runoff  (1) 2024.10.06
[CS50] Week 3 Section  (2) 2024.10.03
[CS50] Pset 2 : Scrabble, Readability, Caesar  (1) 2024.09.29
[CS50] Week 2 Section  (0) 2024.09.24
[CS50] Week 2 Review  (2) 2024.09.23


2주차 Pset2 과제는 총 3부분으로 이루어져 있습니다!

 

2-1) Scrabble

2-2) Readability
2-3) Caesar

 

 


 


<Pset 2-1. Scrabble>

: Implement a program in C that determines the winner of a short Scrabble-like game. Your program should prompt for input twice: once for “Player 1” to input their word and once for “Player 2” to input their word. Then, depending on which player scores the most points, your program should either print “Player 1 wins!”, “Player 2 wins!”, or “Tie!”

 

Score for each alphabet is :

 

 

Pseudocode being like:

//Prompt the user for two words

//Compute the score for each word

//Print the winner

 

 

★ strlen()

: string length를 줄인 함수, 문장의 길이를 알 수 있음. 뒤에서도 계속 쓰이는 함수라 기억해 둘 것!!

 

★각 단어의 char에 score 부여하고, 점수 합산하기

: for loop 사용.

('word[i] - 'A')나 ('word[i]  - 'a')는 word[] 의 각 char에 points[] 의 순서에 맞는 점수를 배분하는 것임.

 

ex) word = HELLO 일 때, word[0] = H임. 'H' - 'A' = 8이고, points[8]은 해당 배열의 8번째 점수를 H에 할당시키고,

score += points[] 의 형태로 각 char의 score을 합산하여 합계 score을 최종적으로 return.

 

 


 

 

<Pset 2-2. Readability>

:  Implement a program that calculates the approximate grade level needed to comprehend some text. Your program should print as output “Grade X” where “X” is the grade level computed, rounded to the nearest integer. If the grade level is 16 or higher (equivalent to or greater than a senior undergraduate reading level), your program should output “Grade 16+” instead of giving the exact index number. If the grade level is less than 1, your program should output “Before Grade 1”.

 

One readability test is the Coleman-Liau index :

index = 0.0588 * L - 0.296 * S - 15.8

L : the average number of letters per 100 words in the text

S : the average number of sentences per 100 words in the text

 

Pseudocode being like:

//Prompt the user for some text

//Count the number of letters/ words/ and sentences in the text

//Compute the Coleman-Liau index

//Print the grade level

 

The idea of counting the number of letters, words, and sentences

: letters(# of alphabets) - isalpha ()

  words(# of spaces+ 1) - isspace()

  sentences(# of punctuations) - ispunct()

→ Ensure to make each function below main()

 

★ The value of L and S should be float, not int 

Also, make sure to multiply 100 each when you compute the Coleman-Liau index. (to make it the average # per 100)

 

 

round() - 가까운 정수로 반올림

: a function that rounds a floating-point number to the nearest integer

 

★ When counting the # of words, make sure not to count double spaces or starting with a space.

→ It means that space should be followed by alpha or punct :

if (isspace(text[i]) && (isalpha(text[i + 1]) || ispunct(text[i + 1]))

 

★Notice that int sum_words = 1;

: This is to make sure that the text starts with a word.

 

★ When counting the # of sentences, make sure not to count punctuation other than '.', '?', or '!'

 

 


 

 

<Pset 2-3. Caesar>

: Caesar used to “encrypt” confidential messages by shifting each letter therein by some number of places. For instance, he might write A as B, B as C, C as D, …, and, wrapping around alphabetically, Z as A. And so, to say HELLO to someone, Caesar might write IFMMP instead. Upon receiving such messages from Caesar, recipients would have to “decrypt” them by shifting letters in the opposite direction by the same number of places.

 

Pseudocode being like:

//Make sure program was run with just one command-line argument

//Make sure every character in argv[1] is a digit

//Convert argv[1] from a 'string' to an 'int'

//Prompt the user for a plaintext

//For each character in the plaintext, rotate each one if it's a letter

//Print the ciphertext

★ Use argc to make sure there's only one command-line argument

if (argc != 2)

argc = 2일 때 c-l argument가 하나이기 때문에, 2가 아닌 경우를 조건에 기재하여 error message를 print함.

++  argv[1]이 digit으로 이루어져 있지 않은 경우에도 ||로 묶어 같은 error message print함.

 

atoi()

: a function in stdlib.h that converts a string to an 'int'

 

 

bool only_digits(string s)

: argv[1] 안에 있는 각각의 char이 digit인지 아닌지 판단하는 함수 → true or false

 

malloc()

: a function that allocates size bytes of uninitialized storage

pointer = malloc(size_in_bytes);

 

위 코드에 적은 malloc function을 풀어서 해석해보면 :

string ciphertext = malloc((strlen(plaintext) + 1) * sizeof(char));

→ char의 bytes size인 4 * (plaintext의 길이+1)ciphertext라는 string에 배정하는 것.

 

strlen(plaintext) + 1

: ensure that there's enough memory allocated to store the entire string, including the null terminator. ('\0')

 

ciphertext[length] = '\0'

: ensures that the last character of the ciphertext string is the null terminator, marking the end of the string.
This is necessary for functions like printf to correctly identify where the string ends.

 

char new_c = (c - 'a' + n) % 26 + 'a';

ⓐ c - 'a' : converts c to zero-based index (where 'a' becomes 0, 'b' becomes 1, and so on up to 'z' becomes 25)

ⓑ (c - 'a' + n) : shifts the character by n positions in the alphabet

ⓒ % 26 : ensures that the result wraps around if it goes past 'z'.
ⓓ + 'a' : converts the zero-based index to a character 

 

 

 


이번 과제를 하면서 수정을 100번도 더 했는데...

느낀 점은 코딩은 반복을 싫어한다! 위에서 정의한 변수일 경우 아래에서는 간단하게 사용할 것.

그리고 본래 library에 있는 함수들을 많이 사용하면 편리하다. 예를 들면 atoi(), malloc(), islower()... 같은 것들!

그리고 확실히 지난 주 과제보다 스스로 할 수 있는 부분이 늘어났고, 앞선 과제에서 사용했던 방법들을 응용할 수도 있었다.

물론 아직 수정에 수정에 수정을 거듭하지만, 오류를 계속해서 디버그하고 고민하는 과정 속에서 배울 점들이 있었다.

Week 3 로 돌아올게 팟팅 ♥ㅎ

'CS50' 카테고리의 다른 글

[CS50] Week 3 Section  (2) 2024.10.03
[CS50] Week 3 Review  (1) 2024.10.01
[CS50] Week 2 Section  (0) 2024.09.24
[CS50] Week 2 Review  (2) 2024.09.23
[CS50] Week 1 Section + Pset 1 : Mario-more  (0) 2024.09.15

+ Recent posts