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

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

示例:
下面例子打印系統變量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); }
還有一些關鍵字,比如final,可以讓變量變成常量,當后續代碼再修改該變量的時候就會報錯。通常用于設置不能修改的常量值。
示例:使用final關鍵字來聲明變量。
2-5 final int a = 2; a = 3; //出錯
2.2 運算符
運算符可以對變量進行基本的運算。運算符包括基本運算符、自增自減運算符、關系運算符邏輯運算符和條件運算符。
2.2.1 基本運算符
表2-3所示是Processing的基本運算符。
表2-3 運算符

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

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

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

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

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

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

圖2-5 長度為5的整型數組
數組的優點在于只需要一行代碼就能寫多個變量。例如,下面這行代碼是2000個整型變量:
int[ ] x=new int[2000];
注意
每個數組僅可以儲存一種類型的數據(布爾、浮點、整數、PImage等)。不能將不同類型的數據混在一個數組中。如果必須這樣,可以使用對象來代替。
2.6.2 遍歷數組
在Processing中,很多時候需要制作并處理多圖片,來達到所需要的圖片效果,如果一個一個地對圖片的信息進行處理,會消耗很多的精力和時間,并且需要大量的工作量。因此,利用數組,通過for循環語句操作,可以提高工作效率。
1)用for循環遍歷數組。
下面就是一個利用for循環語句,繪制單位為10的熊貓來回運動的效果,如圖2-6所示。
2-30 float [ ] x = new float[10]; //儲存每幅圖的橫向運動坐標組 float [ ] y = new float[10]; //儲存每幅圖的縱向運動坐標組 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隨機賦值 y[i] = random(0, 300); //0~300隨機賦值 s[i] = 0.5; //偏移量 } } void draw() { background(200); //背景填充 for (int i =0; i<x.length; i++) { panda(x[i], y[i]); //載入熊貓函數 x[i] += s[i]; if (x[i] > 300 ‖ x[i] <0) { s[i] = -s[i]; } //防止圖片移出窗口 } //for循環運行10次,載入10次熊貓函數 } void panda(float x, float y) { pushMatrix(); //矩陣移動 translate(x, y); //移動函數 // 熊貓頭像繪制函數 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 數組熊貓運動圖
2)用for循環的另一種形式來遍歷數組。
語法結構:
for(數據類型 變量名:數組){ println(變量名); }
示例:
2-31 int[ ] array={0,1,2,3,4}; //創建數組 for(int i:array){ println(i); }
輸出:0
1
2
3
4
3)通過printArray()函數遍歷數組。
printArray()函數用于將信息顯示在消息控制臺中。每一行都會顯示一個數組元素。這個函數只能應用于一維數組。
示例:
2-32 float[ ] f = { 0.3, 0.4, 0.5 }; printArray(f);
輸出:[0] 0.3
[1] 0.4
[2] 0.5
利用數組繪制五角星,如圖2-7所示。

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

圖2-8 定義一個3行2列的intArray整型數組
2-35 int[ ][ ] intArray; //數組聲明 intArray =new int[3][2]; //數組初始化
在第二個[ ]中的數字一般可省略,然后通過下標來分別初始化數組中的數組,以此來得到包含不同長度的二維數組。
String[ ][ ] strArray=new String[5][ ];
對第一個數組進行長度初始化。
strArray[0]=new String[5];
對第二個數組進行長度初始化。
strArray[1]=new String[4];
對第三個數組進行長度初始化。
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}};
注意
數據類型可以是int、float、char等,數組名用標識符充當。數組大小是指構成數組的元素個數。為數組分配的存儲空間為一連續的存儲空間,這是利用指針訪問數組的物理基礎。訪問數組時,下標始終從0開始。
1)用for循環遍歷二維數組。
通過for循環遍歷二維數組,編程畫一個隨機的灰度噪聲圖,如圖2-9所示。
2-36 size(300, 300); int cols = width; int rows = height; // 聲明二維數組 int[ ][ ] myArray= new int[cols][rows]; // 初始化二維數組變量 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 二維數組繪制灰度圖
2)用另一種for循環遍歷二維數組。
示例:
2-37 String[ ][ ] strArray=new String[3][4]; // 創建一個3行4列的字符串數組 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循環遍歷二維數組。
示例:
3-38 String[ ][ ] strArray=new String[3][4]; // 創建一個3行4列的字符串數組 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"
將一個數組變量值賦值給另一個數組變量,改變其中一個變量的值,會影響另一個變量的值。
2-39 int[ ]x={1,3,5,7,9}; int[ ]y=x; y[1]=101; println(x[1]); //輸出:101
2.6.4 數組綜合應用
接下來,本書提供了5個數組綜合應用的實用范例。
范例一
功能:利用數組,繪制大小變化的圓。
示例:
2-40 float[] a=new float[10]; //定義一個長度為10的浮點型數組 int j=0; void setup() { frameRate(1); //程序每秒刷新一次畫面 for (int i=0; i<10; i++) a[i]=random(100); //把隨機生成的數放入數組 } void draw() { background(204); //背景設置為灰色 if (j<10) ellipse(50, 50, a[j], a[j]); j++; }
范例二
功能:利用數組的連續遍歷功能,制作漸變圖效果,如圖2-10所示。
示例:
2-41 size(600, 600); //窗口大小600×600 float[ ] coswave= new float[width]; // 利用new運算符將像素的width值賦予數組 for (int i = 0; i < width; i++) { float amount= map(i, 0, width, 0, PI); // map(value,最小值區間,最大值區間) 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 數組繪制漸變圖
范例三
功能:為數組分配一塊連續數組大小1×數組大小2的存儲空間
說明:二維數組可以當作矩陣來看待和處理,二維數組中第1個下標表示行,第2個下標表示列。
示例:
在這個例子中,有兩個數組來儲存鼠標的狀態:一個用于x坐標,一個用于y坐標,這些數組儲存鼠標在過去幀內的位置。每一幀新的出現,保存最久的x、y坐標的值都會被現在的mouseX和mouseY代替,新的值則被添加到數組的第一個位置,但是在這之前,數組中的每一個值都會被向右移動(從后到前),從而為新的數值騰出空間。
同樣,每一幀,所有的60個坐標都被用于在屏幕上畫出一系列的圓,如圖2-11所示。

圖2-11 鼠標動態圓繪制
示例:
2-42 int num=60; //用于定義數組長度 int[ ] x=new int[num]; //儲存x坐標 int[ ] y=new int[num]; //儲存y坐標 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]; } //從后往前復制數值到數組 x[0]=mouseX; //設置第一個元素 y[0]=mouseY; //設置第二個元素 for (int i=0; i<x.length; i++) { fill(i*4); ellipse(x[i], y[i], 40, 40); //畫圓 } }
案例分析:
數組存儲值分析,如下:
原始數組如下:

開始循環復制第二到最后一個數值到最后的位置。

第二次循環,復制元素2到元素3。

第三次通過循環,復制元素1到元素2。

第四次也是最后一次通過循環,復制元素0到元素1。

復制新的元素到元素0。

范例四
功能:Processing提供PImage類用于加載圖片,通過對圖片像素提取及重構,實現對圖片的基層處理及預想編輯效果。
說明:通過載入原圖片像素點RGB,并通過數組循環重排,實現對圖片的還原以及鏡像處理后所得到的圖片,如圖2-12所示。
2-43 void setup() { pic=loadImage("pic.jpg"); //載入原圖片 size(pic.width*2, pic.height); //設置窗口大小 background(0); //背景填充 smooth(); //圖片光滑 } void draw() { int x=int(random(pic.width)); //隨機獲取原圖片橫坐標 int y=int(random(pic.height)); //隨機獲取原圖片縱坐標 int sum=x+y*pic.width; //像素點矩陣計算 loadPixels(); //載入像素點RGB值 float r=red(pic.pixels[sum]); //復制red值 float b=blue(pic.pixels[sum]); //復制blue值 float g=green(pic.pixels[sum]); //復制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根目錄下,亦可通過設置url值綁定路徑。通過對原圖片的像素點RGB提取,并重新繪制來得到新圖片,同時通過對繪制的路徑變化得到所需要的圖片效果,亦可通過設置繪制像素的大小來得到精準的圖片。當繪制像素大小為1px時,可得到精確原圖,基于此方法得到的圖片在后期處理上更為完美。
范例五
利用二維數組輸出楊輝三角形前10行。
int[][]sz=new int[10][10]; //定義一個10×10列的數組 int i, j; for( i=0; i<sz.length; i++){ sz[i][0]=1; sz[i][i]=1; } //輸出外圍的數值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]; } } //內嵌循環輸入數值 for(i=0; i<10; i++){ for(j=0; j<=i; j++){ print(sz[i][j]+" "); if(j==i) println(); } } //內嵌循環輸入
輸出:
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 數組函數
Processing提供了處理數組的全局函數,詳細函數如表2-5所示。讀者可以使用這些函數對數組進行處理。
這些函數有個共同的特點,就是它們在調用的時候不會改變原數組元素,而是返回一個新的數組。
表2-5 數組函數

示例一:
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}; //創建并賦值 println(data1.length); int[] data2 = expand(data1); // 增加數組長度 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個元素進行排序 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); //數組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”,在引號“”內的都叫字符串。在Processing中字符串被封裝成了類String,并且提供了一些成員方法和全局函數來處理字符串數據。
字符串在存儲上類似于字符數組,所以它每一位的單個元素都是可以提取的,給編程人員提供了方便,如高精度運算時每一位都可以轉化為數字存入數組。
字符串的初始化有兩種方式。第一種用雙引號來初始化:
String str ="Processing";
第二種用new來初始化,需要把字符數組當作參數傳入:
2-50 char[ ] charData={' I' , ' l' , ' o' , ' v' , ' e' , ' P' , ' r' , ' o, ' g' , ' r' , ' a' , ' m' }; String str1=new String(charData); println(str1); // 輸出:IloveProgram
也可把字節數組作為參數:
Byte[ ] byteData={80,114,111,99,101,115,115,105,110,103} ; String str2=new String(byteData); println(str2); // 輸出:Processing
String()類重載了構造函數,使得可以通過參數來指定字符數組的一段字符來初始化字符串。
String(data, offset, length) /* data:字符數組或者字節數組 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()函數返回字符串的每個字符,并判斷字符串中大寫字母的個數。
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); //輸出大寫字母的個數 // 輸出:2
3)indexOf()成員方法返回指定字符串的索引位置
indexOf(str) indexOf(str, fromIndex) /*str:要搜索的字符或字符串 fromIndex:開始搜索的索引位置,前面即使有匹配字符也會被忽略 */
程序會從左向右開始搜索返回一個與指定字符串匹配的索引位置,如果沒有找到則返回-1。
字符串第一位索引位置為0。
注意
indexOf()函數只返回第一個字符的索引位置,若查找的字符在字符串中多次出現也只返回第一個字符的索引位置。
4)substring()重載函數用于返回字符串中從指定開始位置到結束位置的子字符串。
substring(beginIndex) substring(beginIndex, endIndex) /* beginIndex:截取的開始位置 endIndex:截取的結束位置,實際字符串長度減1 */
5)利用indexOf()函數查找定位字符或者字符串,并結合substring()函數即可實現Word中的查找替換功能。
2-55 String str="We love Processing."; //定義并賦值字符串 int index=str.indexOf("love"); //索引所查內容的位置 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()成員方法返回一個新的字符串,并把字符串中所有小寫字母全替換成大寫字母。
以下示例對字符串使用凱撒加密,它的基本思想是:通過把字母移動一定的位數來實現加密和解密。明文中的所有字母都在字母表上向后(或向前)按照一個固定數目進行偏移后被替換成密文。例如,當偏移量是3的時候,所有的字母A將被替換成D, B變成E,以此類推,X將變成A, Y變成B, Z變成C。
2-56 String str="We loves Processing."; //定義并賦值字符串 String upstr=str.toUpperCase(); //轉換所有字母為大寫字母 char[ ] ch=new char[upstr.length()]; //定義字符數組用于儲存加密后的字符 for (int i=0; i<upstr.length (); i++) { // if判斷當前字符是否為大寫字母,符合則對字符加密,不符合返回原字符 if (upstr.charAt(i)>=' A' && upstr.charAt(i)<=' Z' ) { ch[i]=char((upstr.charAt(i)-' A' )%26+' A' +3); //字母循環移動 } else ch[i]=upstr.charAt(i); } String Caesastr=new String(ch); //字符數組作為參數傳遞給字符串進行賦值 println(Caesastr); //輸出:ZH ORYHV SURFHVVLQJ
2.7.3 字符串處理函數
Processing提供了處理字符串的全局函數,詳細函數如表2-6所示。它們為編程者使用字符串處理數據提供很大幫助。
表2-6 字符串函數

- Raspberry Pi for Python Programmers Cookbook(Second Edition)
- Oracle WebLogic Server 12c:First Look
- Spring Cloud Alibaba微服務架構設計與開發實戰
- Interactive Applications Using Matplotlib
- 微信小程序開發解析
- BIM概論及Revit精講
- SSM開發實戰教程(Spring+Spring MVC+MyBatis)
- 持續集成與持續交付實戰:用Jenkins、Travis CI和CircleCI構建和發布大規模高質量軟件
- Java并發編程之美
- Android Studio Cookbook
- 征服C指針(第2版)
- XML程序設計(第二版)
- JavaScript Unit Testing
- 深度學習的數學:使用Python語言
- Mathematica Data Visualization