Week 1 主要課程素材 :
助教課程:
註:我在 2019 年又重看了一次 2018 年新出的影片,所以內容有些不一樣。
Week 0 的 pseudocode (虛擬碼)如圖,對於一個懂英文的成年人應該可以了解其中的內容。
但要是對一個小朋友說:「請先翻開書的一半」。
他可能連「書」跟「一半」的概念是什麼都還不懂,那要怎麼期待會回應正確的動作呢?語言大部分都很抽象,更何況電腦語言。所以我們要盡可能的把指令描述明確且具體,讓電腦接收到我們的指令時不產生誤解,正確的執行我們下的命令。
Week 1 以一個好玩的實驗開頭,來證明 指令精準的重要性。
實驗是這樣的,David 請兩位同學上台,桌上擺了三組「製作三明治的食材與工具」,讓台下同學發號施命,一個口令一個動作,讓台上的三人執行一個「製作三明治」的動作。
目的是讓大家很清楚的意識到,台上的都是讀到哈佛的高材生,對英文的理解自然沒有問題,但是每個人對指令的解讀還是天差地遠。(當然很大程度是故意搞笑,但不影響理解這個概念)
這節開始比較 Week 0 所使用的圖像化 Scratch 轉換成傳統的 程式語言 C 。
什麼?你說上節分明就沒有介紹到 Scratch ,對我懶但你不能跟著懶,請自己點進去 開始一個專案 玩玩~
之所以使用 Scratch 當做入門,而不是 C 語言,是因為讓初學者可以專心在邏輯設計上,而不會又出現哪裡有報錯而感到很無力。(像我一開始學程式的時候,就很不解為什麼結束非得要加 ;
)
話說最近在練習 Canvas,引入 CreateJs 實作 電流急急棒 ,看似很簡單,但還是碰了不少壁,但如果只是想練習寫個小程式,真得有必要熟讀操作文件去實現那些簡單的邏輯嗎?
但再看看網路上找到的 學校老師做的 scratch 實作電流急急棒 ,用圖像化把語言模塊相接在一起,比我那一大篇 js 還要親民多了吧,而且最終成果也比我屌很多哈哈哈哈(我太弱還做不出障礙物)
所有程式教學第一步都要對世界打個招呼!
這是一個最簡單的程式,功能只有:印出一行 “ hello, world “
在 scratch 裡可以這麼做,很清楚一看就懂
那用C語言寫的話,會是以下這段
#include <stdio.h>
int main(void) // → (黃色塊)小綠旗
{
printf("hello, world\n"); // → (紫色塊)say
}
\n
代表的是「這行文字已經結束了」的表示符號。
if else
也是程式語言中共通的判斷式,以下圖為例:
if
括號內的內容如果正確,就執行對應 { }
裡的內容,else if
括號內容,正確就執行對應 { }
內容,else
對應的 { }
內容。這邊 David 提出一點思考,在第三個判斷式中,我們需不需要寫成 else if ( x == y )
?
從比較兩個數字大小的動作中,我們知道只會得到三種結果,大於、小於跟等於。 也就是說,前面已經比較完大於跟小於,最後剩下的只有 等於 這個結果,那到底需不需要寫呢?
程式非常的明確好懂。
因為這是個很簡單、大家都可以輕易理解,所以並沒有太大問題。 但要是今天換成一個複雜的判斷,或者是只有你懂的情況,接手同事可能會感到很困擾。
電腦多了一層判斷。
就算現在電腦的速度跑非常快,就算多了這一行判斷的時間,也快到你察覺不了。 但如果平常沒有維持好習慣,努力縮小這些不必要的判斷時間,在你寫出成千甚至上萬行的程序後,會漸漸體會到這些差異。
前面是 說一次 “hello, world”,現在來試試 不斷地說 “hello, world”
實現循環在程式語言中叫做 loops
上圖右邊的程式碼就是說,當 while
括號內容 為真 (true),就會不斷執行大括號 { }
裡的內容
while (true) // → 括號內容為 true
{
// 不斷地運作這裡的程式碼
}
懂得 不斷地 說 hello, world
,那要是想要做 有限次數 的循環呢?
說 100 次、 50 次、 2 次 hello, world
,這時候就可以用 for loops
for
後括號內容為:(起始值; 條件式; 更新值)
迴圈順序如下:
{ }
內容 )for (var i = 0; i < 50; i++) // 當i小於50,就繼續執行
{
// 執行50次這裡的程式碼
printf("hello, world\n");
}
以下是讓使用者輸入名字,讓電腦在螢幕上輸出的小程式。 檔名為 hello.c
#include <cs50.h>
#include <stdio.h>
int main(void)
{
string name = get_string("what's your name?\n");
printf("hello, %s\n", name);
}
C 語言中沒有字串這種資料型態,所以透過引入課程中已經寫好的 cs50.h
的函式庫 (或稱 library),我們就可以使用 string
當作變數,也可以使用 get_string
來得到使用者輸入的變數。
同樣的,printf( )
也是透過引入 stdio.h
後才能夠使用,將電腦接收到的文字輸出在螢幕上。
程式在電腦執行的過程應該是這樣的。
所以當我們寫好上一個例子 hello.c,要怎麼讓他執行呢?這裡並沒有像 Scratch 裡有個神奇的綠色旗子可以按,所以我們要輸入一些指令,將 hello.c編譯並執行編譯後的機械碼 。
在這裡課程也包裝了一個指令 make
,可以將編譯的指令寫得比較精簡,如下:
make hello
,這行代表著「 請將 hello.c 的檔案編譯成 hello 」
如果沒有 make
可以用,就不能編譯了嗎?當然不是,只是要打的字多一點而已,如下:
clang -o hello hello.c
,跟上述是一樣意思,但使用 make
是不是簡單多了呢?
執行很簡單,只要在剛剛編譯好的機械碼前加上 ./
就好了,所以當我們要執行剛剛的程式為 ./hello
。
2 除以 10 的答案是什麼?
「 0.2 阿!還用說嗎?」 「這要看變數存什麼樣的資料型態而定,還有看指定回傳的數字有多精確。」
int
,會回傳 0
,因為 int
是整數型,小數點以下就忽略不計。float
,會回傳 0.200000
,不過能夠存到小數點後第 8 位數,超過就不精準了,佔用 4 byte。double
,會回傳 0.200000
,不過能夠存到小數點後第 16 位數,超過就不精準了,佔用 8 byte。例如上圖,使用者輸入的 x
y
雖然是整數,但以 float
的資料型態儲存 (截的這張圖看不出來),在 printf( )
裡指定要存到小數點後 50 位數 ( %.50f
),結果就是小數點後第 17 位數開始,登愣!壞掉了。
為什麼會造成這樣的結果呢?
電腦的記憶體是有限的,當我們想運算無限位數的數字該怎麼辦呢?結果就是出現 bug 了,電腦能做的只有非常接近,但無法達到100%的準確,所以應該要盡量避免去比較太多位數的數字。
台下同學發問,那是不是永遠都使用
double
以保持更精確?
當然可以這麼想,但是要付出代價。使用 double
變數佔用的記憶體空間是 float
的兩倍,記憶體空間是有限的,代表這也佔去了其他重要的位置,更何況如果只是儲存更多的 0 呢?所以是端看當時的情況來做出取捨。
剛剛的例子說明了記憶體空間的有限性,那當然 int
整數型也是有上限的,一般來說是 32 bit (4 byte),即為 -2147483648 ~ 2147483647。一但數字超過這範圍,系統就會出錯。
例如著名的波音 787 overflow 事件,請參考 Jserv與他愉快的小夥伴 - 臉書貼文。
想深入了解搞錯資料型態的可以參考: