官术网_书友最值得收藏!

第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 字符串函數

主站蜘蛛池模板: 平舆县| 内江市| 灵山县| 屏东市| 桦南县| 隆化县| 麻栗坡县| 大安市| 桑日县| 富裕县| 宁乡县| 乌鲁木齐县| 铜梁县| 湟中县| 长岛县| 云安县| 浑源县| 嘉定区| 大丰市| 招远市| 嘉禾县| 井研县| 蒲城县| 镇巴县| 芦山县| 略阳县| 凤凰县| 黑水县| 新野县| 伊川县| 明光市| 望都县| 巍山| 米泉市| 逊克县| 九龙城区| 蒲城县| 云梦县| 柯坪县| 邛崃市| 阿拉善盟|