大學生活暫時告一段落,最近終於騰出時間跟精力來認真讀這堂觀望許久的harvard神課CS50。今天這篇主要會是我自己看完David的上課影片+Brian詳細講解作業的影片+寫完所有作業並交出去確定正確之後的筆記,供我自己之後回來複習,如果可以幫到也要修課的同學就更好了!

code: https://github.com/yunchipang/cs50x


basics

David在開始動手寫code之前先定義了好的程式碼應該是correctness, good design, good style 缺一不可。

  • correctness → your program runs correctly
  • design → how well your code is written (do not repeat yourself!)
  • style → readable & elegant

最基本的C起手式,先示範了印出hello, world

#include <stdio.h>

int main(void)
{
    printf("hello, world");
}

老實說我從來沒有想過要學C,畢竟商院出身的就算走資料分析,也比較多用用Python, R, SQL而已,對我來說C就是一個遙遠的、困難的、艱澀的、與我無關的程式語言。但David第二堂課就教C,下定決心要好好讀完cs50的我只好硬著頭皮開始寫。之前聽實習好夥伴W說C是古老而嚴謹的語言,自己稍稍摸過一次之後終於懂了這種感覺!講直接一點,python有時候隨便寫寫都跑得起來,C卻是所有變數定義跟前置作業缺ㄧ不可。

problem set部分,我不知道我code這樣寫是不是最正確或是最快的方法,但他跑得動而且submit50系統完全接受,所以先幫自己的心得跟產出做個紀錄,有空再來檢查跟優化,也歡迎批評指教!


pset1/hello

第一份作業(也是最簡單的暖身題)要求大家首先請user填入自己的名字name,然後程式會return "hello, name"給user。

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

int main(void)
{
    // prompt the user for their name
    string name = get_string("What is your name?\n");

    // print out hello
    printf("hello, %s\n", name);
}


pset1/mario

這份「印金字塔」的作業我卡了幾小時才解出來。當時自己覺得能力有限,所以先做了”less comfortable”的版本,不過觀念通了之後解”more comfortable”也就幾分鐘的事情(痛哭流涕)。

雖然之前寫python也有多多少少用過巢狀迴圈,但這種print出指定形狀的練習我從來沒做過,因此一開始多多少少有點困惑,但摸索之中好像有漸漸抓到訣竅。因為本身屬於圖像理解派(?),我自己的方法是畫出二維方格子,x軸的變數用i, y軸用j,幫所有方格子都編上(i, j)這樣的號碼之後,會比較好想像印出指定形狀的#的時候邏輯要怎麼寫。

1. less comfortable

印出向左傾斜的金字塔後,要印出向右傾斜的金字塔最重要的就是if (i + j < n - 1)這行,因為用正方形來看的話,左下到右上的對角線以左都需印出空白,這區的條件就是i + j < n - 1(不懂的話可以畫出來加加看!)

for (int i = 0; i < n; i++)
{
    for (int j = 0; j < n; j++)
    {
        if (i + j < n - 1)
        {
            printf(" ");
        }
        else
        {
            printf("#");
        }
    }
    printf("\n");
}

2. more comfortable

跟上ㄧ題是舉一反三的用法,一步一步由左到右寫出來。

  1. 向右傾斜金字塔金字塔(同上題)
  2. 中間margin=2的空白
  3. 向左傾斜金字塔(最簡單!)


pset1/cash

cash這個作業是練習寫簡單的greedy algorithm(貪婪演算法),方法是把手上有的零錢做成一個array:money,拿要找零的數字: cents迴圈減掉比他自己小的最大的零錢money[i],每做一次就coins++,最後得到coins的數量。

// define the coins available
int money[4];
money[0] = 25;
money[1] = 10;
money[2] = 5;
money[3] = 1;

// counting coins
int coins = 0;
for (int i = 0; i < 4; i++)
{
    while (cents >= money[i])
    {
        cents -= money[i];
        coins++;
    }
}


pset1/credit

credit無疑是week1最難的作業(畢竟是最後一份more comfortable),我大概斷斷續續解了三天才解出來,最後發現出錯的地方還是基本數學跟資料型態問題,在這裡記錄一下免得自己再犯一樣的錯。

1. modulo

上網查資料時發現有範例是用 % 9 來取得乘以二之後兩位數單獨相加的值(例如66*2=12,取1+2=3),但credit.c第一次成功complie再使用check50檢查時卻發現放進去跑的credit card numbers輸出的output卻有對有錯!回頭print出number, lengthsum進一步檢查,發現問題應該出在數字9。因為9*2=181+8=9,但是如果用 % 9處理的話程式會return 0(天啊就是這種初丁數學問題)。所以正確的解法應該如下,把兩位數分開處理。

sum += ((two_n / 10) + (two_n % 10));

2. int vs char

第二個我犯的錯是把判讀這串數字是AMEX, MASTERCARD還是VISA的跟開頭數字有關的條件都寫錯資料型態了。例如VISA卡號都是4開頭,我就寫str[0] == 4然後complie之後所有卡號都return INVALID,後來才發現問題就出在str字串的一個字元str[0]是char,所以要寫成str[0] == '4'才對。


【課後心得】

耶依終於寫完C了!希望今年上半年可以努力堅持把CS50讀完,不可以放棄。不知道自己真心喜歡什麼的話,直接動手寫就會知道了。啊這裡再額外感謝一下父親總是可以一眼看出我bug在哪裡,雖然他取笑我的變數名稱取很爛,哼(好啦我會加油)