函式 ( function ) 的宣告 (1)
DESCRIPTION
函式 ( function ) 的宣告 (1). 當有一件事 需要重複去作 或 過程比較複雜 時, 1) 可以寫成函式,方便偵錯維護與往後呼叫!. int sum(int N) { int k, total=0; for ( k=1; kTRANSCRIPT
函式 ( function ) 的宣告 (1)
2) 以 #define 的形式,來定義需要重複呼叫的程式部分!
當有一件事需要重複去作或過程比較複雜時,1) 可以寫成函式,方便偵錯維護與往後呼叫!
int sum(int N)
{ int k, total=0;
for ( k=1; k<=N; ++k ) total += k;
return total;
}
#define PI 3.141592653 // 定義常數#define random(n) ( rand() % n ) // 定義函式 (side effect)
#define new Line( ) printf(“\n”); // 簡化換行指令
#define PI 3.141592653 // 定義常數
建議的方式:
1) const : 表示變數不能再被改變( read-only )
2) 明確的宣告常數的型態,如下的 double
const double PI = 3.141592653;
函式 ( function ) 的宣告 (2)
(a) - (b)
#define random(n) ( rand() % n ) // 定義函式 (side effect)
Ex. random( 5+1 ) ==> 原意:產生 0 ~ 5 的隨機數
事實是 ==> ( rand() % 5 + 1 ) // 1 <= value < 5
#define subtract( a, b) a - b
subtract( 10, 5 ) ==> 10 - 5 = 5
subtract( 10, 2+3 ) ==> 10 - 2+3 = 11 != 5
函式 ( function ) 的宣告 (3)
傳回型態 函式名稱 ( [ 參數列 ,] )
{ ………..
函式主體 ;
return ( [ 具有傳回型態的結果 ] ) ;
}
// 傳回型態可以是 void ,表示函式不必傳回任何值。
// 當函式執行過程遇到 return 時,便會立即結束該函式的執 行,並返回原呼叫函式的下一個指令繼續執行。需傳回 的資料則緊接著在 return 後面。
// 形式參數( formal argument )列可有可無!
函式的範例void line(){ for (k=0; k<10; ++k) printf(“*”);}
void line1(int n){ for (k=0; k<n: ++k) printf(“*”);}
void line2(int n, char ch){ for (k=0; k<n; ++k) printf(“%c”,ch);}
無參數時呼叫ex. line();
一個整數型態參數ex. line1(10);
具有兩個參數
ex. line2(10,’*’);
函式呼叫時的執行流程void main(String args[]){ int squ; squ = square(10); System.out.println(squ); System.out.println( “The End !”);}
int square(int k){ rerurn ( k*k );}
更正確的說 :當執行到 return時 ,便會自程式所呼叫的函式返回 !
變數在函式間的可視範圍規則:
在變數宣告以下的函式,理論上均可存取該變數,但 { } 會將變數隱藏起來,此時外界便無法直接存取或引用此變數!函式尋找變數的順序為:由內而外向上找。
int showData(int n)
{ int k;
for ( k=0; k<n; ++k)
printf(“%d\n”, canYouSeeMe ); // error
}
int canYouSeeMe=10; // exists from here.
int main(void)
{ printf(“%d\n”, showData(10) );
printf(“%d\n”, canYouSeeMe ); // O.K.
}
int canYouSeeMe=10;
int main(void)
{ int canYouSeeMe=20;
{ int hello=99;
printf(“%d\n”, sum(10) );
printf(“%d\n”, canYouSeeMe ); // blue selected
printf(“hello=%d\n”, hello);
}
printf(“hello=%d\n”, hello); // error
}
函式的複載 (overload) 宣告 (C++/Java)
複載( overload ):重複載入
*具有相同的函式名稱,但不同的形式參數個數 或型態。 1. 形式參數個數不同, 或
2. 參數個數相同,但具有不完全相同的型態。
*若參數完全相同,僅傳回值不同時,編譯 器也會認為這兩個函式是相同的!
函式的傳回值 (C++/Java)
1) 具傳回值 : int square(int k) { return ( k*k );}
float square(float f) { return ( f*f );}
int abs(int k) { if ( k>=0 ) return (k); else return (-k); }
經 return 傳回值並跳離該函數
float abs(float f);double abs(double d);
int abs(int k);float abs(int k);
兩者相同
函式參數的預設初值設定 (for C++ only)
int sum(int x, int y=0, int z=1)
{ return ( x+y+z); }
ex.
k=sum(10);
k=sum(10, 12);
k=sum(10, 12, 9)
// k=10+0+1=11
// k=10+12+1=23
// k=10+12+9=31
具初值的參數必須靠右排列
錯誤例 :int sum(int x=1, int y, int z)
函式呼叫時參數的傳遞方式 (for C/C++)
傳值呼叫 :(call by value)void exchange(int x, int y){ int temp; temp=x; x=y; y=temp;}
void main(){ int a=3, b=8; cout << a << b << endl; exchange(a, b); cout << a << b << endl;}
傳址呼叫 :(call by address)void exchange(int *x, int *y){ int temp; temp=*x; *x=*y; *y=temp;}
void main(){ int a=3, b=8; cout << a << b << endl; exchange(&a, &b); cout << a << b << endl;}
給的是位址注意 &符號
函式呼叫時參數的傳遞方式 (for C++ only)
傳參數別名 :(call by reference)viod exchange(int &x, int &y){ int temp; temp=x; x=y; y=temp;}
main(){ int a=3, b=8; cout << a << b <<ednl; exchange(a, b); cout << a << b <<endl;}
Call by reference 的reference 可以當成是該參數的一個別名 , 於是在使用這個別名時就跟直接使用原來的變數是一樣的 !
注意 !宣告的方式
函式呼叫時參數的傳遞方式 (in Java)
1) Call by value ( primitive data type )
ex. char, int, float, double, String
2) Call by reference ( objects and array )
reference 表示該類別所在的位址、或視為此 object 的代名。因此經由 reference 對該 object 所代表的內容的任何改變 ,也將真正反應在該 object 上。
當函式在傳遞 object 參數時,其實是在該被呼叫函式中產生一個 reference ,指向原呼叫函式中對應實際參數所在的位址。
常用函式群簡介 (for C/C++)
• 數學類 : (math.h)
sin(x)
cos(x)
tan(x)
atan(x)
exp(x) [=ex]
sqrt(x)
pow(y, x) [=yx]
pow10(x) [=10x]
log(x) [=ln(x)]
log10(x)
• 字串類 : (string.h)
strlen(s)
strlwr(s) // 轉成小寫strupr(s) // 轉成大寫strcat(s1, s2) // s1 <= s1+s2
strcpy(s1, s2) // s1 <= s2
參數是弳度radian
常用函式群簡介 ( in Java )
要使用 Java 所提供的數學函式,必須引用類別 Math 。
例如:
Math.E = 2.718281828459045
Math.PI = 3.141592653589793
Math.abs(.);
Math.sin(.);
….
參考課本 8-40 頁
函式的遞迴呼叫 (recursive call)所謂的遞迴呼叫 :
就是函式在執行過程中又呼叫到自己 , 而同樣自我呼叫的方式一再的繼續下去 ! 這種情形就稱為遞迴 (recursion).
* 使用時 , 先歸納出一再重複部分的規則 .
* 必須有一個能終止一再呼叫的條件產生 , 否則就成了無盡
迴圈的狀況 , 將導致堆疊 (stack) 爆滿 , 程式當掉 !
* 遞迴程式能以非常精簡的方式來巧妙的解決問題 !
* 一種四兩撥千斤 , 叫人嘆為觀止的藝術展現 !
1+2+3+…+(n-1)+n = sum(n)||
sum(n-1)+ n||
sum(n-2)+(n-1)||
sum(n-3)+(n-2)
…..
||sum( 2 ) + 3
||sum( 1 ) + 2
…..
n
n-1
n-2
3
2
:
stack||
1
Save to
Recursive Demo :
類別 (class) 的定義與宣告( 1 )類別( class )可以視為自訂之新的資料型態。
其成員包含資料與方法。
資料成員或屬性( data member or property ):
用以儲存描述該物件特性或相關屬性及記錄目前類別狀況的各種型態的變數。
成員函式或方法( member function or method ):
處理、運算或展現資料的相關方法或函式( function )。
類別僅是描述所有物件都會具有的特性與功能;而物件( object )才是具該類別所陳述之所有功能的真正個體。
類別 (class) 的定義與宣告( 2 )類別( class )可以想成是對物件的完整描述。因此在物件導向程式設計( OOP )中,我們可由外而內、經巨觀到細微的思考我們想要創造物件的概觀,再逐步的設計與實現其被期待的功能。這種想法與現實世界我們處理事務的方法是一致的。
一旦類別確立了,接下來就把它當成是新的資料型態,所以就能夠像宣告一般變數一樣的來宣告物件。 class ClassName [extends SuperClass]
{ class content……
}
ClassName objectName;
objectName = new ClassName(.); or
ClassName objectName = new ClassName(.);
類別 (class) 的定義與宣告( 3 )// define the class Student
class Student //等同於: class Student extends Object
{ String name;
float height, weight;
int grade;
void whoIAm()
{ System.out.println(“My name is “+name);
}
. . . . .
}
Student stu1; // 宣告而已,尚未真正的指向物件,
// 所以對成員的引用會造成錯誤!
stu1 = new Student(); // 要求配置該物件,可以引用資料成員。
Student stu2 = new Student(.); // 宣告並要求配置該物件
Student stus[] = new Student[12]; // array of Student
類別 (class) 的定義與宣告( 4 )Import javax.swing.JOptionPane;
class Person
{ // data members,
String name;
float weight;
float height;
boolean sex; //final boolean sex;
// member functions,
void show()
{ String sex;
if ( this.sex ) sex = "male"; else sex = "female";
System.out.println("I am "+name+" ("+sex+
"),\nmy height is "+height+" cm,\nmy weight is "+weight+" kgs.");
}
類別 (class) 的定義與宣告( 5 )static void whatIsThis() // 可經由類別直接呼叫
{ String msg;
msg = "String name;\n" + "float weight;\n" + "float height;\n" +"boolean sex;\n";
JOptionPane.showMessageDialog(null, msg);
}
}
public class TestClass
{ static public void main(String args[])
{ Person.whatIsThis(); // class-level member function invoked without via object.
Person person = new Person(); // declare a new object with type Person;
// assigning value to object person
person.name = "Jack"; person.sex = true;
person.weight = 21; person.height = 117;
person.show();
while (true);
}
}
類別 (class) 的定義與宣告( 6 )class Person
{ // data members,
private String name // original : String name;
float weight;
float height;
boolean sex; //final boolean sex;
………………………. public class TestClass
{ static public void main(String args[])
{ Person.whatIsThis(); // class-level member function invoked without via object.
Person person = new Person(); // declare a new object with type Person;
// assigning value to object person
person.sex = true; person.name = "Jack"; // Error !
person.weight = 21; person.height = 117;
person.show();
}
}
不希望資料直接經由物件修改,所以設成 privat
e
類別 (class) 的定義與宣告( 7 )class Person
{ String name;
float weight;
float height;
boolean sex; //final boolean sex;
……………………….
public Person(String name, float ht, float wt, boolean male)
{ this.name = name;
height = ht;
weight = wt;
sex = male;
}
public class TestClass
{ static public void main(String args[])
{ Person pMing = new Person(“Casey”,128, 25, false); // 呼叫建構子
}
}
建構子的目的:設定資料成員的初始值
使用 this 以便與形式參數的 n
ame區別
一旦有宣告建構子在 new新物件時,就必須使用適當的建構子
類別 (class) 的定義與宣告( 8 )class Person
{ String name;
float weight;
float height;
boolean sex; //final boolean sex;
……………………….
public Person() { }; // (1) 不需宣告傳回型態
public Person(String name, float ht, float wt, boolean male) // (2)
{ this.name = name;
height = ht;
weight = wt;
sex = male;
}
……..
Person pMing = new Person(“Casey”, 128, 25, false); // 呼叫建構子 (2)
Person pJack = new Person(); // 呼叫建構子 (1)
建構子可以有很多個,祇要符合 overload 規則即可,且不可有傳回型態
類別 (class) 的定義與宣告( 9 )class Pig{
String name;
boolean sex;
float weight;
void whoIAm()
{ System.out.println("I am a Pig, named "+ name); }
}
class ColorPig extends Pig // ColorPig 繼承自 Pig
{ String color = "black"; // new data member added,
// override the method, whoIAm(), in super class Pig.
1) void whoIAm() // added somthing new:
{ super.whoIAm(); // invoke the method in super class
System.out.println("with "+ color +" hairs.");
}
2) void whoIAm() // completely rewrite:
{ System.out.println("I am a "+ color +" Pig, named "+ name+"."); }
}
類別的繼承使用 extends
Pig : 父類別, super classColorPig :子類別, derived class
類別 (class) 的定義與宣告( 9 )class Pig{
String name;
…
}
class ColorPig extends Pig // ColorPig 繼承自 Pig
{ String color = "black";
public ColorPig() { … } ;
void whoIAm() // completely rewrite:
{ System.out.println("I am a "+ color +" Pig, named "+ name+"."); }
}
使用例: ColorPig cpig = new ColorPig();
cpig.name = “Rich”; // 父類別的資料就像是子類別的一樣
cpig.whoIAm();
類別的繼承使用 extends
類別 (class) 的定義與宣告( 10 )class Pig{
private String name;
…
}
class ColorPig extends Pig // ColorPig 繼承自 Pig
{ String color = "black";
public ColorPig() { … } ;
void whoIAm() // completely rewrite:
{ System.out.println("I am a "+ color +" Pig, named "+ name+"."); }
}
使用例: ColorPig cpig = new ColorPig();
cpig.name = “Rich”; // Error !
cpig.whoIAm();
子類別無法存取父類別中的 private
資料
類別 (class) 的定義與宣告( 11 )class Pig{
………………
}
class ColorPig extends Pig // ColorPig 繼承自 Pig
{ private String color = "black";
public String getColor() // called “getter” , 有條件的取得 private 資料
{ return( color); }
public void setColor(String c) // called “setter” ,有條件的設定 private 資料
{ color=c; }
}
使用例: ColorPig cpig = new ColorPig();
cpig.color = “brown”; // Error !
cpig.setColor(“brown”); // It’s O.K. !
……………………
物件無法存取所屬類別中的 private
資料