- Processing開發(fā)實戰(zhàn)
- 黃文愷 吳羽 伍馮潔
- 1899字
- 2019-01-03 10:41:30
第2章 語言基礎(chǔ)
2.1 變量
變量是儲存指定數(shù)據(jù)類型的空間。在一個程序中,同一變量可以多次引用,變量儲存的值也可以更改。你可以把變量理解為存儲某種類型東西的箱子,可以把東西放進(jìn)去,也可以在用的時候取出來。變量的名字就是箱子的標(biāo)簽,這樣在取出里面東西的時候會更加方便。使用變量的時候要提前聲明。
int variable; //前面是數(shù)據(jù)類型,后面是變量的名字
2.1.1 基礎(chǔ)變量類型
不同的數(shù)據(jù)類型就好像不同大小的箱子,可以放不同的數(shù)據(jù)。數(shù)據(jù)類型有基礎(chǔ)數(shù)據(jù)類型和復(fù)雜數(shù)據(jù)類型,如表2-1所示。有些數(shù)據(jù)類型內(nèi)存空間大小一樣,但是它們的取值范圍不一樣,這說明類型決定了數(shù)據(jù)在內(nèi)存空間中的表示方式。計算機(jī)中的數(shù)據(jù)都是以二進(jìn)制位來儲存的,一個位也就是0或1。boolean類型的數(shù)據(jù)只有一位,也就是占用一個二進(jìn)制位來儲存這種數(shù)據(jù)。
表2-1 數(shù)據(jù)類型

2.1.2 變量名字
變量是儲存數(shù)據(jù)的箱子,變量名字是箱子的標(biāo)簽。為了更方便地提取數(shù)據(jù),變量的命名有一些規(guī)則。
?變量名字在同一作用域下是獨一無二的。
?變量名字的第一個符號是字母或下劃線。
?變量名字區(qū)分大小寫,不一樣的大小寫是不同的變量。
?構(gòu)成變量名字的只能是字母、數(shù)字、下劃線。
?變量名字最好有實際意義,能表達(dá)所儲存的內(nèi)容。
2.1.3 變量賦值
把數(shù)據(jù)存入變量這個箱子叫作變量賦值。通過運算符“=”來把數(shù)據(jù)存入變量。有以下兩種方式來進(jìn)行變量賦值。
2-1 int variable = 2;
2-2 int variable; variable = 3;
Processing內(nèi)置了打印函數(shù),可以用print()或println()函數(shù)把變量的數(shù)值在控制臺輸出。
示例:
2-3 int variable; variable = 3; println(variable); //輸出:3
執(zhí)行上面的例子可以在文本框下的控制臺上看到輸出結(jié)果為3。
2.1.4 系統(tǒng)變量
有一些變量是系統(tǒng)內(nèi)置的,不需要聲明。這些變量不允許賦值,這些變量的值可以讓編程人員獲得系統(tǒng)在運行時的一些參數(shù)。比如,mouseX、mouseY的值是鼠標(biāo)在畫板上停留的坐標(biāo)。利用好系統(tǒng)變量可以更方便地設(shè)計交互效果。表2-2列出了部分Processing系統(tǒng)變量。
表2-2 系統(tǒng)變量舉例

示例:
下面例子打印系統(tǒng)變量mouseX、mouseY,并顯示在畫板上。
2-4 void setup() { size(900,600); textSize(26); } void draw() { background(0); text("x:"+mouseX,100,200); text("y:"+mouseY,300,200); }
還有一些關(guān)鍵字,比如final,可以讓變量變成常量,當(dāng)后續(xù)代碼再修改該變量的時候就會報錯。通常用于設(shè)置不能修改的常量值。
示例:使用final關(guān)鍵字來聲明變量。
2-5 final int a = 2; a = 3; //出錯
2.2 運算符
運算符可以對變量進(jìn)行基本的運算。運算符包括基本運算符、自增自減運算符、關(guān)系運算符邏輯運算符和條件運算符。
2.2.1 基本運算符
表2-3所示是Processing的基本運算符。
表2-3 運算符

加減乘除是常用的基本運算,求余是兩個數(shù)相除所得的余數(shù)。一般都是同類型的數(shù)據(jù)通過運算符進(jìn)行運算得出結(jié)果。比如兩個int類型的1相加得出2。
示例:
2-6 int a = 1; int b = 1; int c = a+b; println(c); //輸出:2
同一數(shù)據(jù)類型的相加得到相同數(shù)據(jù)類型的結(jié)果。還有一些特例是不同數(shù)據(jù)類型的數(shù)相加,如float類型和int類型的數(shù)相加,得到的是float數(shù)據(jù)類型的結(jié)果。后面章節(jié)會介紹運算符的其他作用,例如:在字符串中可以連接組合字符串。
2.2.2 自增自減運算符
自增“++”、自減“--”運算是非常方便的操作,可以把變量的值自動增加1或者減少1。
示例:
2-7 int i = 5; i++; println(i); //輸出:6
此處i++等同于i+=1或i=i+1。變量賦值的順序非常重要,i++和++i在參與運算的時候,前者i是先參與運算再自增,后者是先自增再參與運算,請讀者仔細(xì)區(qū)分兩者的差別。
示例:
2-8 int i = 1; int a = 0; a = i++; println(i); //輸出:2 println(a); //輸出:1
自減運算同理。
2.2.3 關(guān)系運算符
關(guān)系運算符可以得到兩個數(shù)據(jù)關(guān)系判斷的真假,例如4>3是真(true),3>4是假(false)。為了區(qū)分等于和賦值,要注意等于用了兩個等于號“==”,而賦值是一個等于號“=”。關(guān)系運算符如表2-4所示。
表2-4 關(guān)系運算符

基本數(shù)據(jù)類型的變量和變量進(jìn)行判斷時,檢查值是否相等而不是數(shù)據(jù)類型是否相同。
示例:
2-9 int a = 65; float b = 65.0; char c = ' A' ; println(a == c); //輸出:true println(a == c); //輸出:true println(b == c); //輸出:true
2.2.4 邏輯運算符
邏輯運算符有與、或、非3種,對應(yīng)的符號分別是&&、‖、! 。它們設(shè)定復(fù)合判斷條件,返回一個boolean值,要么是true,要么是false。
1.與運算
語法結(jié)構(gòu):
表達(dá)式1 && 表達(dá)式2
如果表達(dá)式1和表達(dá)式2的值都是true,那么整個表達(dá)式的值也是true;如果兩個表達(dá)式中任何一個為false,那么整個表達(dá)式的值為false。這個運算符可控制整個子表達(dá)式的求值順序。
2-10 a>5 && a<10
&&運算符的優(yōu)先級比>和<運算符低,所以子表達(dá)式是按照下面這種方式進(jìn)行組合的:
2-11 (a>5) && (a<10)
但是盡管&&操作符的優(yōu)先級較低,但它仍會對兩個關(guān)系表達(dá)式施加控制。它的工作原理如下:&&操作符的左操作數(shù)總是首先進(jìn)行求值,如果它為真,就緊接著對右操作數(shù)進(jìn)行求值。如果左操作數(shù)為假,那么右邊就不再進(jìn)行求值,因為整個表達(dá)式的值一定是假的。
2.或運算
語法結(jié)構(gòu):
表達(dá)式1 ‖ 表達(dá)式2
兩個表達(dá)式中任何一個值為true,或者兩個都為true,則返回值為true。如果兩個表達(dá)式都是false,則返回值為false。
邏輯運算符還可以組合使用:
2-12 println((2<3) && (4>1 ‖ 2<5)); //輸出:true
3.非運算
語法結(jié)構(gòu):
!表達(dá)式
非運算符可以把一個布爾值變成相反的值,即true變成false, false變成true。
示例:
2-13 if(! (2>3)) { print("hello"); //輸出:hello }
2.2.5 條件運算符
條件運算接收3個表達(dá)式,它會控制子表達(dá)式的求值順序。
語法結(jié)構(gòu):
條件表達(dá)式1 ?表達(dá)式2:表達(dá)式3;
條件操作符的優(yōu)先級很低,所以它的各個表達(dá)式即使不加括號也沒什么問題。但是為了清楚起見,人們還是傾向在它的各個子表達(dá)式兩端加上括號。
首先計算的是表達(dá)式1,如果它的值是true,那么整個表達(dá)式的值就是表達(dá)式2的值,表達(dá)式3不會進(jìn)行求值。但是,如果表達(dá)式1的值是false,那么整個條件語句就是表達(dá)式3的值,表達(dá)式2不會進(jìn)行求值。
示例:
2-14 a>5 ? b-6 : c/2
可以讀作:“a是不是大于5?如果是,就執(zhí)行b-6,否則執(zhí)行c/2”。
2.3 條件語句
在編程時可能會處理一些較復(fù)雜的分支情況,需要進(jìn)行判斷,并跳轉(zhuǎn)到不同的語句中去。下面介紹如何使用兩種條件語句。
2.3.1 if條件語句
使用條件語句,可以使程序執(zhí)行某些代碼,而跳過另一些代碼,可以使程序在符合某些特定條件時才執(zhí)行特定代碼。
if條件語句語法結(jié)構(gòu):
2-15 if (條件表達(dá)式) { 代碼; }
先判斷if后面小括號中表達(dá)式的值是真還是假,如果是true執(zhí)行{}里面的代碼,如果是false,則直接跳過{}。
示例:
2-16 int a = 2; if(a>1) { a = 0; } println(a); //輸出:0
上例中因為a的初始值是2,所以進(jìn)入if語句,a被重新賦值為0。
if else語句語法結(jié)構(gòu):
2-17 if (條件表達(dá)式) { 代碼1; }else { 代碼2; }
如果if后面的條件表達(dá)式值為true,程序會運行代碼1,否則執(zhí)行else后面的代碼2。
示例:
2-18 void setup() { size(200,200); } void draw() { background(255); if (mouseX>100) { fill(0); rect(100,100,50,50); //鼠標(biāo)在右半屏?xí)r畫方形 } else { fill(0); ellipse(100,100,50,50); //鼠標(biāo)在左半屏?xí)r畫圓形 } }
運行結(jié)果如圖2-1所示。

圖2-1 鼠標(biāo)移動到左半屏?xí)r畫圓形,移動到右半屏?xí)r畫方形
上述代碼只有兩個分支,但是可以通過嵌套else if來插入更多分支的情況。
else if語句語法結(jié)構(gòu):
if (條件表達(dá)式1) { 代碼1; }else if (條件表達(dá)式2) { 代碼2; } …… else if(條件表達(dá)式n) { 代碼n; } else { 代碼n+1; }
如果條件表達(dá)式1為true,只執(zhí)行代碼1;否則判斷條件表達(dá)式2,如果條件表達(dá)式2為true,只執(zhí)行代碼2;否則,判斷條件表達(dá)式3,以此類推,直到判斷條件表達(dá)式n為止。如果條件表達(dá)式n為false,則執(zhí)行最后else{}里面的代碼n+1。
示例:
2-19 int a = 2; if(a>1) { println("t"); } else if(a<-5) { println("f"); } else { println("m"); }
控制臺輸出:t。
要注意的是,在使用多個else if條件語句時,程序會按順序執(zhí)行判斷,從上到下直到返回true。如果在中間某個過程中條件成立,即使后面也有成立的條件,程序都不會執(zhí)行。
2.3.2 Switch條件語句
switch條件語句用于需要進(jìn)行多次判斷才能做出選擇的情況。
語法結(jié)構(gòu):
switch(條件表達(dá)式) { case 常量1: 代碼1; break; case常量2: 代碼2; break; … case常量n: 代碼n; break; default: 代碼n+1; break; }
switch后面()里的表達(dá)式就是要進(jìn)行判斷的條件,switch語句首先計算條件表達(dá)式的值,這個值限定了數(shù)據(jù)類型,只能是byte、char、int三種數(shù)據(jù)類型,返回其他數(shù)據(jù)類型程序會報錯。
每一個case到break代表一個分支結(jié)構(gòu),case后面跟的是常量表達(dá)式,用來判斷是否與條件表達(dá)式相等,若相等,就執(zhí)行分支里面的語句,直到遇見break。若每個分支的值都和表達(dá)式的值不相等,程序會執(zhí)行默認(rèn)分支default后面的語句。default語句也可以省略,如果分支條件都不成立的話,程序就什么都不執(zhí)行。
示例:
2-20 int a = 2; switch (a) { case 1: println("hello"); break; case 2: println("world"); break; }
如果去掉break那么就會從標(biāo)簽位置繼續(xù)向下執(zhí)行,而不會執(zhí)行完分支語句就馬上退出。
2.4 循環(huán)語句
循環(huán)結(jié)構(gòu)可以在滿足某一個條件之前反復(fù)執(zhí)行一個語句,讓更少的代碼完成更多的事情。下面是兩種循環(huán)結(jié)構(gòu)的使用方式。
2.4.1 while循環(huán)語句
用while來實現(xiàn)循環(huán)操作,它的語法結(jié)構(gòu)如下:
while(條件表達(dá)式) { 循環(huán)體語句; }
條件表達(dá)式就是這個循環(huán)是否繼續(xù)進(jìn)行的條件,而循環(huán)體語句就是這個循環(huán)需完成的事情。while語句首先判斷條件表達(dá)式的值(布爾型數(shù)據(jù)的值)。如果表達(dá)式的值為true,則執(zhí)行循環(huán)體語句,否則,結(jié)束while語句。當(dāng)循環(huán)語句執(zhí)行完一次時,會再次判斷表達(dá)式的值,根據(jù)表達(dá)式的值決定是否要進(jìn)行下一次循環(huán)。如此不斷循環(huán),直到表達(dá)式的值為false,此時循環(huán)結(jié)束。
示例:
2-21 int i = 10; void setup() { size(900,600); } void draw() { while (i< 900) { ellipse(i, i,50,50); i+=50; } }
運行結(jié)果如圖2-2所示。

圖2-2 用while循環(huán)畫圓
2.4.2 for循環(huán)語句
for循環(huán)語句的語法結(jié)構(gòu):
for(初始化語句;判斷循環(huán)條件;更新語句) { 循環(huán)體語句; }
程序進(jìn)入for循環(huán)語句之后,首先會執(zhí)行初始化語句,然后計算判斷循環(huán)條件語句,如果判斷循環(huán)條件的值為true,則執(zhí)行循環(huán)體語句,再執(zhí)行更新語句,修改循環(huán)控制變量。接著又開始計算判斷條件的值,根據(jù)其值決定是否需要繼續(xù)下一次循環(huán):如果判斷條件的值為true,則繼續(xù)下一次循環(huán);反之,則結(jié)束整個循環(huán)。
示例:
2-22 void setup() { size(900,600); } void draw() { for (int i = 0; i < 10; i++) { ellipse(50*i,50*i,50,50); } }
運行結(jié)果如圖2-3所示。

圖2-3 用for循環(huán)畫圓
2.4.3 加強(qiáng)循環(huán)結(jié)構(gòu)
1.跳出循環(huán)
循環(huán)中可以使用break語句來永久終止循環(huán);也可以使用continue語句,用于永久終止當(dāng)前那次循環(huán),而在執(zhí)行之后,重新測試表達(dá)式的值,決定是否繼續(xù)執(zhí)行循環(huán)。
下面例子是輸出10以內(nèi)的偶數(shù):
2-23 for (int i = 0; i < 10; i++) { if (i%2 ! = 0) { continue; } println(i); //輸出:0 2 4 6 8 }
下面的示例是用break關(guān)鍵字跳出循環(huán):
2-24 int a = 0; for (int i = 0; i < 10; i++) { if (i > 5) { break; } a++; } println(a); //輸出:6
兩條語句任何一條如果出現(xiàn)在了嵌套的循環(huán)內(nèi)部,它只對最內(nèi)層的循環(huán)起作用,無法使用break或continue語句影響外層循環(huán)的執(zhí)行。
2.嵌套循環(huán)
嵌套循環(huán)指在一個循環(huán)之內(nèi)還有另一個循環(huán)。內(nèi)部循環(huán)在外部循環(huán)的每個周期做著相同的事情。通過使內(nèi)部循環(huán)的一部分依賴于外部循環(huán),可以使內(nèi)部循環(huán)在每個周期中的表現(xiàn)不同。
示例:
2-25 for (int i = 0; i < 5; i++) { for (int j = 0; j < 6; j++) { println(j); } }
輸出:012345012345012345012345012345。
2.5 函數(shù)
函數(shù)是用于完成特定任務(wù)的程序代碼的單元。在Processing中很常見的函數(shù)有size()函數(shù)、line()函數(shù)等。這一節(jié)將要展示如何通過編寫新的函數(shù),來擴(kuò)展Processing的功能特性。
為什么要使用函數(shù)?當(dāng)要處理的問題越來越復(fù)雜,程序越來越龐大的時候,如果把這些程序代碼都放到主函數(shù)中,將使主函數(shù)異常臃腫,這樣會給程序的維護(hù)帶來麻煩。在這種情況下,可以按照功能的不同,把大問題劃分成小問題,把大程序劃分成小模塊。函數(shù)則成為模塊劃分的基本單元,是對一個復(fù)雜問題處理過程的一種抽象。
函數(shù)的使用可以省去復(fù)雜代碼的編寫。如果程序中需要多次使用某種特定的功能,那么只需寫一個合適的函數(shù)即可。程序可以在任何需要的位置調(diào)用該函數(shù),并且同一個函數(shù)可以在不同的程序中調(diào)用。即使某些功能在程序中只使用一次,將其以函數(shù)的形式實現(xiàn)也是有必要的,因為函數(shù)讓程序變成模塊化,從而有利于程序的閱讀、修改和完善。
函數(shù)內(nèi)部的計算是非常復(fù)雜的,特別是由多人共同完成的程序,會創(chuàng)建大量的新函數(shù)。但函數(shù)的優(yōu)點在于不必了解它們是如何運作的,只需知道如何使用它們就已經(jīng)足夠了,即了解輸入的是什么和它們是如何影響輸出的。這種抽象使編程人員能專注于程序整體的設(shè)計,而無須考慮過多細(xì)節(jié)。
2.5.1 定義函數(shù)
定義函數(shù)有3部分:函數(shù)名、參數(shù)體、返回值。
函數(shù)名就是為了標(biāo)志一個函數(shù)而取的名字,通過該函數(shù)名可以快速找到這個函數(shù)。函數(shù)的命名規(guī)則和變量的命名規(guī)則相同。如果說變量命名重在說明它“是什么”,那么函數(shù)的命名則重在說明它要“做什么”。
在程序里函數(shù)是可以傳入?yún)?shù)的,可以針對函數(shù)的不同值,來進(jìn)行相應(yīng)的操作。當(dāng)調(diào)用函數(shù)時,它會執(zhí)行{}里的語句,函數(shù){}里的所有語句叫作函數(shù)體。
語法結(jié)構(gòu):
返回值類型 函數(shù)名(參數(shù)類型1 參數(shù)名1,參數(shù)類型2 參數(shù)名2, ...) /*可以接收任意個參數(shù),中間用逗號隔開*/ { 語句; return 返回值; }
函數(shù)在執(zhí)行完任務(wù)后,往往需要給它的調(diào)用者一個返回值,表示函數(shù)運行的結(jié)果或者其他意義。如下面的示例,在這個加法函數(shù)中,函數(shù)名為add,返回值類型為int,表示該函數(shù)在完成加法計算后,將計算的結(jié)果作為返回值返回給它的調(diào)用者。若函數(shù)只是執(zhí)行一系列動作,無需返回值,則可以使用void作為返回值類型。
示例:
2-26 void draw() { println(add(5,4)); } int add(int a, int b) //此處有兩個參數(shù),中間用逗號隔開 { return a+b; } //輸出:9
還有一些函數(shù)是沒有返回值的,類型是void。下面的示例是定義一個名為circle的函數(shù),它的作用是畫圓。
示例:
2-27 void draw() { circle(); } void circle() { ellipse(50,50,50,50); }
2.5.2 函數(shù)重載
有一些函數(shù)功能相同,但是接收的參數(shù)不一樣,它們就可以用相同的名稱。比如一個加法函數(shù),接收兩個參數(shù)相加一起。而另一個加法函數(shù)也實現(xiàn)了相同的功能,只不過變?yōu)榻邮?個參數(shù)相加。如果起不同的名字,在調(diào)用函數(shù)時需要起很多名字,也要記錄很多名字,會讓人覺得很混亂。函數(shù)重載可以讓其只有一個名字,調(diào)用時,根據(jù)參數(shù)個數(shù)和類型自動識別,調(diào)用相應(yīng)的函數(shù)。
示例:兩個求最小值的函數(shù),第一個是兩個數(shù)求最小,第二個是3個數(shù)求最小。功能相似,用一個名字更容易使用,減少函數(shù)數(shù)量。
2-28 int minNumber(int a, int b) { int temp = a > b? b:a; return temp; } int minNumber(int a, int b, int c) { int temp = a > b? b:a; temp = temp < c ? temp:c; return temp; }
2.5.3 函數(shù)遞歸
函數(shù)遞歸就是一個函數(shù)直接或間接調(diào)用自身。遞歸有一定的適用條件,在某些情況下使用遞歸可以方便獲得想要的結(jié)果。當(dāng)一個函數(shù)自己調(diào)用自己時,如果編程沒有設(shè)定可以終止遞歸的條件檢測,它會無限制地進(jìn)行遞歸調(diào)用,所以要謹(jǐn)慎使用遞歸。
遞歸一般可以用循環(huán)來代替,但是遞歸更為強(qiáng)大,有時是循環(huán)無法處理的。遞歸方法使得程序結(jié)構(gòu)優(yōu)美,但是執(zhí)行效率卻往往沒有循環(huán)高。
下面的示例是計算階乘。
示例:
2-29 void setup(){ println(Factorial(8)); //求8的階乘 } int Factorial(int i) { int num; num=i; if(num==1)return1; else return num *Factorial(--num); }
2.6 數(shù)組
2.6.1 數(shù)組的概念
數(shù)組是一組有序且數(shù)據(jù)類型相同的變量的集合,每一個數(shù)組里面的項目被稱為元素,每一個索引值標(biāo)記其在數(shù)組中的位置。索引值即元素序號從0開始計數(shù)。例如,第一個元素在數(shù)組中的索引值是0,第二個元素在數(shù)組中的索引值是1,以此類推。如果有20個數(shù)值在數(shù)組中,那么最后一個元素的索引值就是19。如圖2-4所示是一個數(shù)組的結(jié)構(gòu)概念圖。

圖2-4 數(shù)據(jù)結(jié)構(gòu)概念圖
對集合中數(shù)組元素的訪問可以采用下標(biāo)的形式,也可以采用指針的形式進(jìn)行。數(shù)組可以是一維的,也可以是多維的。當(dāng)數(shù)組只有一個下標(biāo)時,這類數(shù)組稱為一維數(shù)組,當(dāng)下標(biāo)不止一個時,稱為多維數(shù)組。常用的數(shù)據(jù)集基本在三維以內(nèi)。
創(chuàng)造一個數(shù)組可分為3個步驟:
1)聲明和定義數(shù)據(jù)類型。
2)創(chuàng)建新的關(guān)鍵字和數(shù)組長度。
3)給每個元素分配值。
使用數(shù)組類似于使用單獨的變量,但遵循的是同種模式。如,寫一個整數(shù)變量x如下:
Int x;
而寫一個數(shù)組,只需在數(shù)據(jù)類型后加上括號:
int[ ] x;
例如,下面的代碼表示定義了一個intArray整型數(shù)組,下標(biāo)從0~4,共5個數(shù)組元素,如圖2-5所示。
int[ ] intArray=new int[5];

圖2-5 長度為5的整型數(shù)組
數(shù)組的優(yōu)點在于只需要一行代碼就能寫多個變量。例如,下面這行代碼是2000個整型變量:
int[ ] x=new int[2000];
注意
每個數(shù)組僅可以儲存一種類型的數(shù)據(jù)(布爾、浮點、整數(shù)、PImage等)。不能將不同類型的數(shù)據(jù)混在一個數(shù)組中。如果必須這樣,可以使用對象來代替。
2.6.2 遍歷數(shù)組
在Processing中,很多時候需要制作并處理多圖片,來達(dá)到所需要的圖片效果,如果一個一個地對圖片的信息進(jìn)行處理,會消耗很多的精力和時間,并且需要大量的工作量。因此,利用數(shù)組,通過for循環(huán)語句操作,可以提高工作效率。
1)用for循環(huán)遍歷數(shù)組。
下面就是一個利用for循環(huán)語句,繪制單位為10的熊貓來回運動的效果,如圖2-6所示。
2-30 float [ ] x = new float[10]; //儲存每幅圖的橫向運動坐標(biāo)組 float [ ] y = new float[10]; //儲存每幅圖的縱向運動坐標(biāo)組 float [ ] s = new float[10]; //儲存每幅圖的偏移量組 void setup() { size(300, 300); //窗口大小 smooth(); //線段光滑 for (int i = 0; i < x.length; i ++) { x[i] = random(0, 300); //0~300隨機(jī)賦值 y[i] = random(0, 300); //0~300隨機(jī)賦值 s[i] = 0.5; //偏移量 } } void draw() { background(200); //背景填充 for (int i =0; i<x.length; i++) { panda(x[i], y[i]); //載入熊貓函數(shù) x[i] += s[i]; if (x[i] > 300 ‖ x[i] <0) { s[i] = -s[i]; } //防止圖片移出窗口 } //for循環(huán)運行10次,載入10次熊貓函數(shù) } void panda(float x, float y) { pushMatrix(); //矩陣移動 translate(x, y); //移動函數(shù) // 熊貓頭像繪制函數(shù) fill(0); strokeWeight(1); ellipse(-35, -25, 35, 35); //左耳 ellipse(35, -25, 35, 35); //右耳 fill(255); strokeWeight(1); stroke(0); ellipse(0, 0, 100, 90); //頭部 fill(0); ellipse(-25, 5, 30, 35); //左眼 ellipse(25, 5, 30, 35); //右眼 fill(255); ellipse(-25, 0, 6, 6); //左眼球 ellipse(25, 0, 6, 6); //右眼球 fill(0); ellipse(0, 25, 7, 5); //鼻子 noFill(); stroke(0); strokeWeight(1); bezier(-2.5, 35, -2.5, 37, 2.5, 37, 2.5, 35); //用貝塞爾曲線繪制嘴巴 popMatrix(); //矩陣閉合 }

圖2-6 數(shù)組熊貓運動圖
2)用for循環(huán)的另一種形式來遍歷數(shù)組。
語法結(jié)構(gòu):
for(數(shù)據(jù)類型 變量名:數(shù)組){ println(變量名); }
示例:
2-31 int[ ] array={0,1,2,3,4}; //創(chuàng)建數(shù)組 for(int i:array){ println(i); }
輸出:0
1
2
3
4
3)通過printArray()函數(shù)遍歷數(shù)組。
printArray()函數(shù)用于將信息顯示在消息控制臺中。每一行都會顯示一個數(shù)組元素。這個函數(shù)只能應(yīng)用于一維數(shù)組。
示例:
2-32 float[ ] f = { 0.3, 0.4, 0.5 }; printArray(f);
輸出:[0] 0.3
[1] 0.4
[2] 0.5
利用數(shù)組繪制五角星,如圖2-7所示。

圖2-7 五角星
繪圖思路:描繪五角星10個頂點坐標(biāo),再通過vertex()函數(shù)連接相鄰點。
實例點坐標(biāo):
(50,18)(61,37)(83,43)(69,60)(71,82)(50,73) (29,82)(31,60)(17,43)(39,37)
示例:
2-33 int[ ] x={50,61,83,69,71,50,29,31,17,39}; // 數(shù)組x儲存點橫坐標(biāo) int[ ] y={18,37,43,60,82,73,82,60,43,37}; // 數(shù)組y儲存點縱坐標(biāo) beginShape(); //發(fā)起創(chuàng)建新圖形信號 for(int i=0; i<x.length; i++){ vertex(x[i], y[i]); //定義x、y軸坐標(biāo) } endShape(CLOSE); //結(jié)束信號
注意
避免在draw()內(nèi)創(chuàng)建數(shù)組,因為在每一幀創(chuàng)建一個數(shù)組會降低幀速率。
2.6.3 二維數(shù)組
可以這樣理解:一維數(shù)組解決的是線性問題,而二維數(shù)組解決的是面的問題。定義二維數(shù)組的語法格式如下:
數(shù)據(jù)類型[ ][ ] 變量名;
例如,定義一個整型數(shù)組,變量名為intArray:
2-34 int[][] int Array ;
使用new關(guān)鍵字初始化的語法格式:
new 數(shù)據(jù)類型[數(shù)組長度1][數(shù)組長度2] ;
二維數(shù)組可以當(dāng)作矩陣來看待,二維數(shù)組中第1個下標(biāo)表示行,第2個下標(biāo)表示列。功能可以看作為該數(shù)組分配一塊連續(xù)數(shù)組長度1×數(shù)組長度2的存儲空間。
例如,定義一個存放3個長度為2的整型數(shù)組,如圖2-8所示。

圖2-8 定義一個3行2列的intArray整型數(shù)組
2-35 int[ ][ ] intArray; //數(shù)組聲明 intArray =new int[3][2]; //數(shù)組初始化
在第二個[ ]中的數(shù)字一般可省略,然后通過下標(biāo)來分別初始化數(shù)組中的數(shù)組,以此來得到包含不同長度的二維數(shù)組。
String[ ][ ] strArray=new String[5][ ];
對第一個數(shù)組進(jìn)行長度初始化。
strArray[0]=new String[5];
對第二個數(shù)組進(jìn)行長度初始化。
strArray[1]=new String[4];
對第三個數(shù)組進(jìn)行長度初始化。
strArray[2]=new String[3];
…………
例如:
int x[2][5]={{1,2,3,4,5}, {4,5,6,7,8}};
其等效的形式如下:
int x[2][ ]={{1,2,3,4,5}, {4,5,6,7,8}};
注意
數(shù)據(jù)類型可以是int、float、char等,數(shù)組名用標(biāo)識符充當(dāng)。數(shù)組大小是指構(gòu)成數(shù)組的元素個數(shù)。為數(shù)組分配的存儲空間為一連續(xù)的存儲空間,這是利用指針訪問數(shù)組的物理基礎(chǔ)。訪問數(shù)組時,下標(biāo)始終從0開始。
1)用for循環(huán)遍歷二維數(shù)組。
通過for循環(huán)遍歷二維數(shù)組,編程畫一個隨機(jī)的灰度噪聲圖,如圖2-9所示。
2-36 size(300, 300); int cols = width; int rows = height; // 聲明二維數(shù)組 int[ ][ ] myArray= new int[cols][rows]; // 初始化二維數(shù)組變量 for (int i = 0; i < cols; i++) { for (int j = 0; j < rows; j++) { myArray[i][j] = int(random(255)); } } // 畫點 for (int i = 0; i < cols; i++) { for (int j = 0; j < rows; j++) { stroke(myArray[i][j]); point(i, j); } }

圖2-9 二維數(shù)組繪制灰度圖
2)用另一種for循環(huán)遍歷二維數(shù)組。
示例:
2-37 String[ ][ ] strArray=new String[3][4]; // 創(chuàng)建一個3行4列的字符串?dāng)?shù)組 for(int i=0; i<strArray.length; i++){ for(int j=0; j<strArray[i].length; j++){ strArray[i][j]=i+"_"+j; //字符串樣式 } } for(int i=0; i<strArray.length; i++){ for(String j:strArray[i]){ print(j+", "); } println(); }
控制臺輸出:
0_0,0_1,0_2,0_3,
1_0,1_1,1_2,1_3,
2_0,2_1,2_2,2_3,
3)第三種for循環(huán)遍歷二維數(shù)組。
示例:
3-38 String[ ][ ] strArray=new String[3][4]; // 創(chuàng)建一個3行4列的字符串?dāng)?shù)組 for(int i=0; i<strArray.length; i++){ for(int j=0; j<strArray[i].length; j++){ strArray[i][j]=i+"_"+j; //字符串樣式 } } for(int i=0; i<strArray.length; i++){ printArray(strArray[i]); }
控制臺輸出:
[0] "0_0"
[1] "0_1"
[2] "0_2"
[3] "0_3"
[0] "1_0"
[1] "1_1"
[2] "1_2"
[3] "1_3"
[0] "2_0"
[1] "2_1"
[2] "2_2"
[3] "2_3"
將一個數(shù)組變量值賦值給另一個數(shù)組變量,改變其中一個變量的值,會影響另一個變量的值。
2-39 int[ ]x={1,3,5,7,9}; int[ ]y=x; y[1]=101; println(x[1]); //輸出:101
2.6.4 數(shù)組綜合應(yīng)用
接下來,本書提供了5個數(shù)組綜合應(yīng)用的實用范例。
范例一
功能:利用數(shù)組,繪制大小變化的圓。
示例:
2-40 float[] a=new float[10]; //定義一個長度為10的浮點型數(shù)組 int j=0; void setup() { frameRate(1); //程序每秒刷新一次畫面 for (int i=0; i<10; i++) a[i]=random(100); //把隨機(jī)生成的數(shù)放入數(shù)組 } void draw() { background(204); //背景設(shè)置為灰色 if (j<10) ellipse(50, 50, a[j], a[j]); j++; }
范例二
功能:利用數(shù)組的連續(xù)遍歷功能,制作漸變圖效果,如圖2-10所示。
示例:
2-41 size(600, 600); //窗口大小600×600 float[ ] coswave= new float[width]; // 利用new運算符將像素的width值賦予數(shù)組 for (int i = 0; i < width; i++) { float amount= map(i, 0, width, 0, PI); // map(value,最小值區(qū)間,最大值區(qū)間) coswave[i] = abs(cos(amount)); //絕對值 } for (int i = 0; i<width; i++) { stroke(coswave[i]*255); //灰度值深化 line(i, 0, i, height/3); } //第一個漸變 for (int i = 0; i<width; i++) { stroke(coswave[i]*255/4); //灰度值變化 line(i, height/3, i, height/3*2); } //第二個漸變 for (int i = 0; i<width; i++) { stroke(255 - coswave[i]*255); //灰度值亮化 line(i, height/3*2, i, height); }

圖2-10 數(shù)組繪制漸變圖
范例三
功能:為數(shù)組分配一塊連續(xù)數(shù)組大小1×數(shù)組大小2的存儲空間
說明:二維數(shù)組可以當(dāng)作矩陣來看待和處理,二維數(shù)組中第1個下標(biāo)表示行,第2個下標(biāo)表示列。
示例:
在這個例子中,有兩個數(shù)組來儲存鼠標(biāo)的狀態(tài):一個用于x坐標(biāo),一個用于y坐標(biāo),這些數(shù)組儲存鼠標(biāo)在過去幀內(nèi)的位置。每一幀新的出現(xiàn),保存最久的x、y坐標(biāo)的值都會被現(xiàn)在的mouseX和mouseY代替,新的值則被添加到數(shù)組的第一個位置,但是在這之前,數(shù)組中的每一個值都會被向右移動(從后到前),從而為新的數(shù)值騰出空間。
同樣,每一幀,所有的60個坐標(biāo)都被用于在屏幕上畫出一系列的圓,如圖2-11所示。

圖2-11 鼠標(biāo)動態(tài)圓繪制
示例:
2-42 int num=60; //用于定義數(shù)組長度 int[ ] x=new int[num]; //儲存x坐標(biāo) int[ ] y=new int[num]; //儲存y坐標(biāo) void setup() { size(240, 120); //定義窗口大小 smooth(); //線段光滑 noStroke(); //禁止描邊 } void draw() { background(0); //填充刷新背景色 for (int i=x.length-1; i>0; i--) { x[i]=x[i-1]; y[i]=y[i-1]; } //從后往前復(fù)制數(shù)值到數(shù)組 x[0]=mouseX; //設(shè)置第一個元素 y[0]=mouseY; //設(shè)置第二個元素 for (int i=0; i<x.length; i++) { fill(i*4); ellipse(x[i], y[i], 40, 40); //畫圓 } }
案例分析:
數(shù)組存儲值分析,如下:
原始數(shù)組如下:

開始循環(huán)復(fù)制第二到最后一個數(shù)值到最后的位置。

第二次循環(huán),復(fù)制元素2到元素3。

第三次通過循環(huán),復(fù)制元素1到元素2。

第四次也是最后一次通過循環(huán),復(fù)制元素0到元素1。

復(fù)制新的元素到元素0。

范例四
功能:Processing提供PImage類用于加載圖片,通過對圖片像素提取及重構(gòu),實現(xiàn)對圖片的基層處理及預(yù)想編輯效果。
說明:通過載入原圖片像素點RGB,并通過數(shù)組循環(huán)重排,實現(xiàn)對圖片的還原以及鏡像處理后所得到的圖片,如圖2-12所示。
2-43 void setup() { pic=loadImage("pic.jpg"); //載入原圖片 size(pic.width*2, pic.height); //設(shè)置窗口大小 background(0); //背景填充 smooth(); //圖片光滑 } void draw() { int x=int(random(pic.width)); //隨機(jī)獲取原圖片橫坐標(biāo) int y=int(random(pic.height)); //隨機(jī)獲取原圖片縱坐標(biāo) int sum=x+y*pic.width; //像素點矩陣計算 loadPixels(); //載入像素點RGB值 float r=red(pic.pixels[sum]); //復(fù)制red值 float b=blue(pic.pixels[sum]); //復(fù)制blue值 float g=green(pic.pixels[sum]); //復(fù)制green值 int diameter=int(random(8,15)); //繪制像素點大小 noStroke(); //不描邊 fill(r, g, b,100); //填充RGB值 ellipse(x, y, diameter, diameter); //繪制原圖 ellipse(pic.width*2-x, y, diameter, diameter); // 繪制鏡像圖片 }

圖2-12 鏡像處理
分析:載入圖片一般存在Processing根目錄下,亦可通過設(shè)置url值綁定路徑。通過對原圖片的像素點RGB提取,并重新繪制來得到新圖片,同時通過對繪制的路徑變化得到所需要的圖片效果,亦可通過設(shè)置繪制像素的大小來得到精準(zhǔn)的圖片。當(dāng)繪制像素大小為1px時,可得到精確原圖,基于此方法得到的圖片在后期處理上更為完美。
范例五
利用二維數(shù)組輸出楊輝三角形前10行。
int[][]sz=new int[10][10]; //定義一個10×10列的數(shù)組 int i, j; for( i=0; i<sz.length; i++){ sz[i][0]=1; sz[i][i]=1; } //輸出外圍的數(shù)值1 for(i=2; i<sz.length; i++){ for(j=1; j<i; j++){ sz[i][j]=sz[i-1][j-1]+sz[i-1][j]; } } //內(nèi)嵌循環(huán)輸入數(shù)值 for(i=0; i<10; i++){ for(j=0; j<=i; j++){ print(sz[i][j]+" "); if(j==i) println(); } } //內(nèi)嵌循環(huán)輸入
輸出:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 84 36 9 1
2.6.5 數(shù)組函數(shù)
Processing提供了處理數(shù)組的全局函數(shù),詳細(xì)函數(shù)如表2-5所示。讀者可以使用這些函數(shù)對數(shù)組進(jìn)行處理。
這些函數(shù)有個共同的特點,就是它們在調(diào)用的時候不會改變原數(shù)組元素,而是返回一個新的數(shù)組。
表2-5 數(shù)組函數(shù)

示例一:
2-44 String[ ] sa1 = { "A", "B", "C"}; String[ ] sa2 = append(sa1, "D"); println(sa2); //輸出:A B C D
示例二:
2-45 String[ ] sa1 = { "A", "B", "C"}; String[ ] sa2 = { "D", "E", "F"}; String[ ] sa3 = concat(sa1, sa2); println(sa3); //輸出 A B C D E F
示例三:
2-46 int[] data1 = {0, 1, 3, 4}; //創(chuàng)建并賦值 println(data1.length); int[] data2 = expand(data1); // 增加數(shù)組長度 println(data2.length); println(data1[data2.length-5]); /*輸出: 4 8 4 */
示例四:
2-47 String[ ] s={ "deer", "elephant", "bear", "aardvark", "cat" }; s = sort(s, 3); //對前面3個元素進(jìn)行排序 println(s); //輸出:bear deer elephant aardvark cat
示例五:
2-48 String[ ] a = { "OH", "NY", "CA" }; a = splice(a, "KY", 1); //"KY"插入a中a[1]位置 println(a); String[ ] b = { "VA", "CO", "IL" }; a = splice(a, b, 1); //數(shù)組b全體插入a中a[1]位置 println(a); /* 輸出: OH KY NY CA OH VA CO IL KY NY CA */
示例六:
2-49 String[] sa1 = { "OH", "NY", "CA", "VA", "CO", "IL" }; String[] sa2 = subset(sa1, 1); //提取sa1中a[1]后的元素 println(sa2); String[] sa3 = subset(sa1, 2, 3); // 提取sa1的a[2]后的長度為3的元素 println(sa3); /* 輸出: NY CA VA CO IL CA VA CO */
2.7 字符串
2.7.1 字符串基本概念
字符串是由多個字符的組成的集合,例如“Good morning”,在引號“”內(nèi)的都叫字符串。在Processing中字符串被封裝成了類String,并且提供了一些成員方法和全局函數(shù)來處理字符串?dāng)?shù)據(jù)。
字符串在存儲上類似于字符數(shù)組,所以它每一位的單個元素都是可以提取的,給編程人員提供了方便,如高精度運算時每一位都可以轉(zhuǎn)化為數(shù)字存入數(shù)組。
字符串的初始化有兩種方式。第一種用雙引號來初始化:
String str ="Processing";
第二種用new來初始化,需要把字符數(shù)組當(dāng)作參數(shù)傳入:
2-50 char[ ] charData={' I' , ' l' , ' o' , ' v' , ' e' , ' P' , ' r' , ' o, ' g' , ' r' , ' a' , ' m' }; String str1=new String(charData); println(str1); // 輸出:IloveProgram
也可把字節(jié)數(shù)組作為參數(shù):
Byte[ ] byteData={80,114,111,99,101,115,115,105,110,103} ; String str2=new String(byteData); println(str2); // 輸出:Processing
String()類重載了構(gòu)造函數(shù),使得可以通過參數(shù)來指定字符數(shù)組的一段字符來初始化字符串。
String(data, offset, length) /* data:字符數(shù)組或者字節(jié)數(shù)組 offset:開始位置 length:長度 */
示例:
2-51 char[ ] charData={' p' , ' e' , ' r' , ' f' , ' e' , ' c' , ' t' }; String str=new String(charData,2,3); // 將從charDate[2]開始長度為3的元素賦予str println(str); // 輸出:rfe
用“+”可以連接兩個字符串,得到一個新的字符串。
2-52 String name="Bob"; String age="twenty"; String str="NAME:"+name+" AGE:"+age; println(str); //輸出:NAME:Bob AGE:twenty
2.7.2 字符串的方法
Processing中,字符串被封裝成類,并提供了一些成員方法。
1)length()成員方法返回字符串的長度。
下面例子中,字符串“processing”的長度是12而不是10,因為空格也是一個字符。
2-53 String str=“processing”; println(str.length()); // 輸出: 12
2)charAt(index)成員方法返回制定索引位置的字符。
字符串可以通過索引位置返回指定位置的字符,索引位置從0到字符串的長度減1。
index為返回字符的索引位置,類型為int,取值范圍是0到字符串的長度減1。
下面的例子為通過charAt()函數(shù)返回字符串的每個字符,并判斷字符串中大寫字母的個數(shù)。
2-54 String str="Hi, Processing"; //定義字符串并賦值 int count=0; for (int i=0; i<str.length (); i++) { char ch=str.charAt(i); //索引字符 if (ch>= ' A' &&ch<=' Z' ) //if判斷是否大寫字母 count++; } println(count); //輸出大寫字母的個數(shù) // 輸出:2
3)indexOf()成員方法返回指定字符串的索引位置
indexOf(str) indexOf(str, fromIndex) /*str:要搜索的字符或字符串 fromIndex:開始搜索的索引位置,前面即使有匹配字符也會被忽略 */
程序會從左向右開始搜索返回一個與指定字符串匹配的索引位置,如果沒有找到則返回-1。
字符串第一位索引位置為0。
注意
indexOf()函數(shù)只返回第一個字符的索引位置,若查找的字符在字符串中多次出現(xiàn)也只返回第一個字符的索引位置。
4)substring()重載函數(shù)用于返回字符串中從指定開始位置到結(jié)束位置的子字符串。
substring(beginIndex) substring(beginIndex, endIndex) /* beginIndex:截取的開始位置 endIndex:截取的結(jié)束位置,實際字符串長度減1 */
5)利用indexOf()函數(shù)查找定位字符或者字符串,并結(jié)合substring()函數(shù)即可實現(xiàn)Word中的查找替換功能。
2-55 String str="We love Processing."; //定義并賦值字符串 int index=str.indexOf("love"); //索引所查內(nèi)容的位置 String repl="beat"; //定義代替的字符串 if (index! =-1) { String restr=str.substring(0, index)+repl+str.substring(index+5); /* str.substring(0, index)返回love字符前的所有字符 str.substring(index+5)返回love后面的所有字符*/ } println(restr); //輸出新字符串 //輸出:We beat Processing.
6)equals(str)成員方法用于判定兩個字符串是否相等,如果相等返回true,否則返回false。注意,在processing中不能夠使用“==”來判斷字符串是否相等。
7)toLowerCase()成員方法返回一個新的字符串,并把字符串中所有大寫字母全替換成小寫字母。
8)toUpperCase()成員方法返回一個新的字符串,并把字符串中所有小寫字母全替換成大寫字母。
以下示例對字符串使用凱撒加密,它的基本思想是:通過把字母移動一定的位數(shù)來實現(xiàn)加密和解密。明文中的所有字母都在字母表上向后(或向前)按照一個固定數(shù)目進(jìn)行偏移后被替換成密文。例如,當(dāng)偏移量是3的時候,所有的字母A將被替換成D, B變成E,以此類推,X將變成A, Y變成B, Z變成C。
2-56 String str="We loves Processing."; //定義并賦值字符串 String upstr=str.toUpperCase(); //轉(zhuǎn)換所有字母為大寫字母 char[ ] ch=new char[upstr.length()]; //定義字符數(shù)組用于儲存加密后的字符 for (int i=0; i<upstr.length (); i++) { // if判斷當(dāng)前字符是否為大寫字母,符合則對字符加密,不符合返回原字符 if (upstr.charAt(i)>=' A' && upstr.charAt(i)<=' Z' ) { ch[i]=char((upstr.charAt(i)-' A' )%26+' A' +3); //字母循環(huán)移動 } else ch[i]=upstr.charAt(i); } String Caesastr=new String(ch); //字符數(shù)組作為參數(shù)傳遞給字符串進(jìn)行賦值 println(Caesastr); //輸出:ZH ORYHV SURFHVVLQJ
2.7.3 字符串處理函數(shù)
Processing提供了處理字符串的全局函數(shù),詳細(xì)函數(shù)如表2-6所示。它們?yōu)榫幊陶呤褂米址幚頂?shù)據(jù)提供很大幫助。
表2-6 字符串函數(shù)

- 數(shù)據(jù)庫系統(tǒng)原理及MySQL應(yīng)用教程(第2版)
- 架構(gòu)不再難(全5冊)
- Mastering Spring MVC 4
- PHP+MySQL網(wǎng)站開發(fā)技術(shù)項目式教程(第2版)
- 基于Java技術(shù)的Web應(yīng)用開發(fā)
- 零基礎(chǔ)學(xué)MQL:基于EA的自動化交易編程
- Mastering JBoss Enterprise Application Platform 7
- 網(wǎng)站構(gòu)建技術(shù)
- 表哥的Access入門:以Excel視角快速學(xué)習(xí)數(shù)據(jù)庫開發(fā)(第2版)
- Arduino家居安全系統(tǒng)構(gòu)建實戰(zhàn)
- 一塊面包板玩轉(zhuǎn)Arduino編程
- Node學(xué)習(xí)指南(第2版)
- 深入實踐DDD:以DSL驅(qū)動復(fù)雜軟件開發(fā)
- 現(xiàn)代CPU性能分析與優(yōu)化
- Tableau Dashboard Cookbook