1. 程式人生 > >1.1 開始第一幅“碼繪”——以程式設計作畫的基本方法

1.1 開始第一幅“碼繪”——以程式設計作畫的基本方法

引言

請看下面兩幅圖,並思考:二者有何聯絡?

對於完全沒有接觸過程式設計的小朋友來說,估計現在表情和圖中人物一樣。就通常而言,程式設計和繪畫可謂風馬牛不相及。

程式設計與繪畫的關聯

現在,認真地對照下圖中左右兩邊:

圖中,左邊是一段程式程式碼,右邊是一段實際作畫的過程。作畫分為兩個主要階段:準備階段和作畫階段。在準備階段,主要就是搞定“畫布”;而在作畫階段,則是按照一定的順序畫出的卡通頭像的各個部分。左邊的程式程式碼與實際作畫過程完全對應。儘管不明白程式碼中的英文、符號和數值的具體含義,但已經可以看出,這段程式程式碼精確描述了畫畫的過程。由此,引出介紹一個基礎概念,姑且算是個定義吧:

程序式程式設計:用程式碼來描述做事的過程。

對應在本教程中,程序式程式設計就是“用程式碼指揮畫畫的過程”。

邁出了第一步,即可以開始去理解這些程式碼的精確含義。

程式設計的基本概念

為了講解和溝通,要了解一些基本概念,包括

語句、註釋;

函式、函式定義、函式呼叫,引數、形式引數、實際引數;

關鍵詞;

庫、框架;

觀察下圖:

在圖中,每一個分號結尾的一行字元就是一個”語句“,其實就是表達了做了一個小的步驟。

而每一行以雙斜槓“//”為起始的一行字元就是一行“註釋”,它不是實際執行的內容,而是用於解釋程式碼。事實上,因為它不會在程式執行中實際地執行,寫任何內容都不會對執行效果產生影響,但是寫錯誤地內容會對理解這段程式製造人為障礙。因此,寫註釋也要注意,應該做到對程式碼的正確解釋。

此外,上述程式碼可以明確分解為兩個獨立地段落,即:

function setup() { // 1. 準備階段 // ......程式碼略}function draw() { // 2. 繪製階段 // ......程式碼略 }這是兩個函式,並且是二者的函式定義,它們的名稱分別為setup和draw,即它們的函式名;函式名前面的詞語function是一個關鍵詞,就是在程式設計中有特殊預設意義的詞,這個function就是用於定義函式的關鍵詞;函式名後的小括號也是格式要求必須要寫上的,有了這個括號,就代表它是一個函數了。後面的大括號{}中則是定義了這個函式具體要執行的內容。

對於上述setup函式的定義,可以用口語翻譯為:定義了一個名為setup的函式,它執行的具體行為是{}中的一系列內容。

事實上,筆者總覺得“函式”這個術語過於學術化,一看就是數學用語,很容易嚇跑一大群小朋友。若叫做“功能“、”行為“、"作用”恐怕更為貼切,因為它表達的就是一個行為,即將一大段瑣碎的具體過程用一個簡單的稱呼來概括的表達,例如函式draw中那麼一大段程式碼,對其概括段落大意,即為”畫“,也就是函式名”draw"。就“函式”這一術語的來源"function“一詞,其實直觀翻譯便是”功能“,而非數學意義上的”函式“。

再舉個例子來說明”函式“吧。看下面一段描述:

睜開眼,緩緩坐起來,伸伸懶腰,掀開鋪蓋,左腳先落地,試探著找到鞋子,然後右腳落地,伸入鞋子,站起來,看看窗外的陽光,說到“又是美好的一天”;

概括段落大意,可以表述為“起床”。現在,仿造上面的“函式定義”的程式碼格式寫下來,即定義函式“起床”:

function 起床(){ 睜開眼; 緩緩坐起來; 伸伸懶腰; 掀開鋪蓋; 左腳先落地,試探著找到鞋子; 然後右腳落地,伸入鞋子; 站起來; 看看窗外的陽光; 說到“又是美好的一天”;}如此解釋,是否明白?當然,上述程式碼只是示意而已,實際上是無法執行的,因為常用的程式語言都不能用中文寫。此外,函式setup和draw的具體定義中,也就是那一大堆語句,其實也都是函式。但在這些語句中不是去定義函式,而是在進行函式呼叫,或者說是對函式的執行/使用/施放/發招。例如,"createCanvas(640,480);"就是”執行/使用/施放/發招/呼叫"了一個名為createCanvas的函式,括號中的640和480是“引數”,將這一句翻譯程中文,即”建立一塊方形畫布,寬度為640畫素,高度為480畫素“。再如“ellipse(320,240,200,200);”,翻譯過來,即“畫一個橢圓,中心位於座標(320,240),橫軸長度為200畫素,縱軸長度為200畫素"。由此可見,函式還可以有”引數“,在函式的定義中的引數便是形式引數,即數值未確定的量;而且在實際呼叫時,這些引數可以指定不同的具體數值,即實際引數,從而達到不同的具體效果。作為類比,假設”吃“這個行為也定義為一個函式,那麼為了表達吃不同的量,例如“吃1斤,吃2斤,吃100斤”等不同的效果,就可以把具體吃的量設定為引數,表達為類似於上述程式碼的形態,寫為語句,即:函式定義:function 吃(var amount){ //程式碼略}函式呼叫:吃(1);吃(2);吃(100);下面再圖示一下函式的相關概念:這裡恐怕會產生個兩個疑問:
  • 疑問1. createCanvas、ellipse、line這些函式是哪冒出來的?怎麼沒個具體定義就能用了?
解答:這些都是來自於p5.js的。它們其實是我們所要學習的程式設計環境p5.js提供的,相當於有人幫忙定義好了,我們在這裡只需要呼叫即可。上述程式中的函式ellipse(*)和line(*)亦是如此。p5.js可以看作是一個已經定義好的龐大的“”,它裡面定義的一大堆函式都是可以直接呼叫的,這樣我們就可以反覆地將它們以不同順序進行組織和呼叫,從而編寫出各種酷程式。p5.js提供的大量函式將在後續課程中慢慢展開。一個庫就相當於提供了零件/預製件/工具/手冊的倉庫,這些物件都是前人預先開發好的,為他人提供了直接可以使用的大量積木塊,讓開發更便捷。
  • 疑問2. 函式setup()和draw()僅僅是有了定義,為什麼就實際產生作用,畫出東西來了?這不就和剛剛提到的“函式呼叫了才產生實際作用”產生矛盾了?
解答:它們其實都是在p5.js的框架中呼叫的。p5.js不僅僅提供一大堆可以直接用的”零件“,而且還提供了一個完整的可執行程式的模板,即設計了程式的基本結構,劃分好了內部的各個功能模組和協作關聯,其中一些模組的功能已經完成,另一些模組則是規定好了它與其它模組之間連線的規範。於是,在程式設計時就只需要按照它規定的模式,將為完工的模組開發完畢即可。也就是說p5.js本身就提供了一個可執行的程式,其中setup()和draw()函式是兩個需要有程式設計者來完成的部分,它們在整個程式中的呼叫方式已經預先定好了。因此,我們只需要吧setup()和draw()寫出來,p5.js就能夠去指揮排程它們了。為了深化框架這兩個概念的理解,我們以作畫來類比:庫相當於繪畫工具包,裡面有一系列畫筆、顏料等材料,而框架相當於預先定好的要繪製作品的基本格式,例如”畫布是平面的,畫布用亞麻布,畫框的長寬比例是4:3”等,於是,畫家要做的就是用一定的“技法”去操作工具包中的各種材料,再規定好的“框架”上畫出自己想要的內容。還可以用製造汽車來類比:庫相當於製造車間,裡面有各種零件/預製件/工具,而框架相當於汽車的基本規格,比如”有兩個軸,裝四個輪子,軸長和軸間距在某個範圍,四個輪子上會有個承載區域,區域上需要有空間安裝引擎,並裝一個供司機操作的駕駛艙“,並且這個框架上可能已經預製了一些部件,比如承載版/軸和引擎。於是,汽車的設計和製作就是要完成其它未確定的部件。瞭解了這些概念,再去理解最前面那堆程式碼就容易了:函式setup()和draw()分別定義了繪畫的兩個階段:準備階段和繪製階段;在setup()中,做了一件事,即語句"createCanvas(640,480);",含義就是建立畫布,寬度640畫素,高度480畫素;而在draw()中,以一定步驟依次做了一系列事情,一部分事情是通過呼叫函式ellipse()完成,例如,畫臉即語句“ellipse(320,240,200,200);”,含義就是畫一個橢圓,中心在座標(320,240),橫軸和縱軸長度都是200畫素,還有一部分事情(即畫頭髮)是呼叫函式line完成,例如,畫第一根頭髮的語句是“line(260,180,260,100);”,含義就是,畫一根直線,兩個端點座標分別為(260,180)和(260,100);此外,在畫眼珠前還呼叫了一個函式“fill(0);",其含義是堆後續繪製的幾何形體時填充黑色(數值0表示黑色),於是,畫出的圓形就成了黑色實心的效果。

相關資源: