Feeds:
文章
留言

Archive for 九月 19th, 2006

函數

函數用來封裝特定的工作和重複使用程式碼。依據官方文件描述 AS3.0支援兩種函數:method 和 function closure。這兩種函數依據被定義的所在位置而有不同名稱。假如你將函數定義在類別中或附加在物件實體上稱之為 method。除此之外,其他方式所定義的都稱之為 function closure。雖然官方文件說了一堆,可是我覺得這些名詞不是很重要@_@…

定義自己的函數

函數定義方式有兩種第一種稱之為 function statement 也就是一般常見的函數定義方式,如下:

function max(a:int, int b){
   // your algorithm
}
max(1, 2);

第二種稱之為寫法稱之為 function expression,如下:

var max:Function = function(a:int, b:int){
  // your algorithm
};

max(1, 2);

基本上我覺得兩者沒有什麼太大的差別,只是 var max:Function 的這種 Function Object 寫法有點類似 C# 中的 delegate 概念,可將某個功能委託給某個函數物件處理。在架構系統上是相當方便的一種寫法。下面是一個說明此概念的範例,將排序的演算法委託給客戶端應用程式來決定: 

// CFOTest.as
package {
	import flash.display.Sprite; 	

	public class CFOTest extends Sprite{		
		public function CFOTest(){
			var o:CMySort = new CMySort();			

			var descSort:Function = function(a:int, b:int):Boolean{
				return a<b;
			};

			var ascSort:Function = function(a:int, b:int):Boolean{
				return a>b;
			};

			trace('Init: ' + o.Data );

			o.sort(descSort);
			trace("After DESC sort: " + o.Data );
			
			o.sort( ascSort );
			trace( 'After ASC sort: ' + o.Data );
 		}


	}	
}

// CMySort.as
package{
	public class CMySort{
		private var data:Array;
		public function CMySort(){
			data = [9, 3, 4, 5, 77, 10, 1, 4];
		}

		public function sort(sortMethod:Function):void{
			var tmp:int;
			for(var i:int = 0; i<data.length; ++i){
				for(var j:int = i+1; j<data.length; ++j){
					if (sortMethod(data[i], data[j])){
						tmp = data[i];
						data[i] = data[j];
						data[j] = tmp;
					}
				}
			}
		}
		
		public function get Data():Array{
			return data;
		}		
	}
}

執行結果: 

 

巢狀函數(Nest Function)

 巢狀函數的意思是函數中又定義了函數,下面實作一個 getInfo() 函數以巢狀方式寫法取得資訊:

// CNestFunctionTest.as
package {
	import flash.display.Sprite; 	

	public class CNestFunctionTest extends Sprite{		
		public function CNestFunctionTest(){
			trace( getInfo() );	// CNestFunctionTest v1.0
 		}
	
		public function getInfo():String{
			function productName():String{
				return 'CNestFunctionTest';
			}

			function productVersion():String{
				return '1.0';
			}

			return productName() + ' v' + productVersion();
		}
	}	
}

函數的參數傳遞方式

雖然官方文件說明 primitive 將參數以傳值方式傳遞,而complex 將參數以傳址方式傳遞。但是我的測試結果卻令人 confuse,下面是一個測試傳址/傳值的常見 swap 範例:

// CNestFunctionTest.as
package {
	import flash.display.Sprite; 	

	public class CSwapTest extends Sprite{		
		public function CSwapTest(){
			var o1:Object = {Name: 'Arick'};
			var o2:Object = {Name: 'Mavis'};
			trace( 'o1: ' + o1.Name + ', o2: ' + o2.Name);

			swapObject(o1, o2);
			trace( '[after swapObject] o1: ' + o1.Name + ', o2: ' + o2.Name);			

			swapObjectMember(o1, o2);
			trace( '[after swapObjectMember] o1: ' + o1.Name + ', o2: ' + o2.Name);

			var v1:int = 100;
			var v2:int = 200;
			trace( 'v1: ' + v1 + ', v2: ' + v2);
			swapValue(v1, v2);
			trace( '[after swapValue] v1: ' + v1 + ', v2: ' + v2);


			var s1:String = 'Arick';
			var s2:String = 'Mavis';
			trace( 's1: ' + s1 + ', s2: ' + s2);
			swapString(s1, s2);
			trace( '[after swapString] s1: ' + s1 + ', s2: ' + s2);
 		}

		public function swapString(s1:String, s2:String):void{
			var tmp:String = s1;
			s1 = s2;
			s2 = tmp;
			trace( '[swapString] s1: ' + s1 + ', s2: ' + s2);
		}

		public function swapObject(o1:Object, o2:Object):void{
			var tmp:Object = o1;
			o1 = o2;
			o2 = tmp;
			trace( '[swapObject] o1: ' + o1.Name + ', o2: ' + o2.Name);
		}

		public function swapObjectMember(o1:Object, o2:Object):void{
			var tmp:String = o1.Name;
			o1.Name = o2.Name;
			o2.Name = tmp;			
			trace( '[swapObjectMember] o1: ' + o1.Name + ', o2: ' + o2.Name);
		}

		public function swapValue(v1:int, v2:int):void{
			var tmp:int = v1;
			v1 = v2;
			v2 = tmp;
			trace( '[swapValue] v1: ' + v1 + ', v2: ' + v2);
		}
	}	
}

執行結果:

我檢查了程式並沒有錯誤,可是執行結果不知道你是不是也很困惑,雖然文件上說他的 complex 是以傳址方式傳遞,但是 swapObject 執行結束後 呼叫端的 o1 與 o2 並沒有交換,這和其他語言(如:C/C++)預期結果不同,可是 swapObjectMember 的執行結果卻又使呼叫端的變數產生變化。所以,我想 AS3.0 無論是 primitive 或 complex 都是將呼叫端的值,以副本方式傳遞給函數。只是 primitive 是傳遞值的副本,而 complex 是傳遞記憶體位址的副本,如果是這樣說的話,兩個記憶體副本在函數中交換,並不會影響呼叫端的 complex 值,但是,因為他擁有呼叫端 complex 值的記憶體位址,所以函數內部的修改會影響呼叫端的值。

參數預設值

由於函數部份參數可能大部分都一樣,此時可以給這些參數預設值,方便呼叫端的使用,這是個相當便利的設計,部份的語言(如:PHP, VB)也支援這個功能,下面的範例即可瞭解參數預設值的寫法:

// CDefaultParamTest.as
package {
	import flash.display.Sprite; 	

	public class CDefaultParamTest extends Sprite{		
		public function CDefaultParamTest(){
			trace( func(1) );		// 501
			trace( func(1, 2) );	// 303
			trace( func(1, 2, 3) );	// 6
 		}

		private function func(a:int, b:int = 200, c:int = 300):int{
			return a+b+c;
		}
	}	
}

預設值的寫法如上述範例所示,只需要在參數宣告後面直接使用 = 後接其值即可。使用預設值寫法只有一點需要注意,亦即某個參數指定預設值,則其後的參數也必須指定預設值,如下範例就會出現編譯錯誤:

// CDefaultParamTest.as
package {
	import flash.display.Sprite; 	

	public class CDefaultParamTest extends Sprite{		
		public function CDefaultParamTest(){
			trace( func(1) );		// 501
			trace( func(1, 2) );	// 303
			trace( func(1, 2, 3) );	// 6
 		}
                  // Error: Required parameters are not permitted after optional parameters.
		private function func(a:int, b:int = 200, c:int):int{ 
			return a+b+c;
		}
	}	
}

arguments 物件

arguments 物件用來表示傳遞給函數的參數資訊。arguments 本身是一個陣列用來存放每一個傳遞給函數的參數,arguments.length 表示參數個數,arguments.callee 表示函數本身。下面是使用 arguments 進行間單的加法運算範例:

// CArgumentsTest.as
package {
	import flash.display.Sprite; 	

	public class CArgumentsTest extends Sprite{		
		public function CArgumentsTest(){

			var func:Function = function ():int{
				var result:int = 0;
				for(var i:int = 0; i<arguments.length; ++i){
					result += arguments[i];
				}

				return result;
			};

			trace( func(1) );		// 1
			trace( func(1, 2) );	// 3
			trace( func(1, 2, 3) );	// 6
 		}

	}	
}

這個方法在 class method 中是無效且會發生編譯錯誤,如下範例:

// CArgumentsTest.as
package {
	import flash.display.Sprite; 	

	public class CArgumentsTest extends Sprite{		
	  public function CArgumentsTest(){
		trace( func(1) );		//  Error: Incorrect number of arguments.  Expected no more than 0.
		trace( func(1, 2) );	//  Error: Incorrect number of arguments.  Expected no more than 0.
		trace( func(1, 2, 3) );	//  Error: Incorrect number of arguments.  Expected no more than 0.
 	  }

	  private function func():int{
		var result:int = 0;
		for(var i:int = 0; i<arguments.length; ++i){
			result += arguments[i];
		}
		return result;
  	  }
	}	
}

 

 
下面使用 Factorial 為範例說明 arguments.callee 的用法:

// CArgumentsTest.as
package {
	import flash.display.Sprite; 	

	public class CArgumentsTest extends Sprite{		
		public function CArgumentsTest(){
			var factorial:Function = function (x:uint) {
				if(x == 0) {
					return 1;
				} else {
					return (x * arguments.callee(x - 1));
				}
			}
			trace(factorial(5)); // output: 120
 		}


	}	
}
注意:如果你使用 ... 來修飾參數則會使 arguments 物件失效。而且如果你某個參數名稱取名為 arguments 也會使 arguments 物件無效。

… 函數參數修飾字

… 是 AS3 新的語法,他允許你指定任意數量任意型別的參數,經由 … 修飾的參數,他具有類似 arguments 物件的功能,只是不具備 callee 方法。如下 func() 和 mix() 的測試。

// CArgumentsTest3.as
package {
	import flash.display.Sprite; 	

	public class CArgumentsTest3 extends Sprite{		
		public function CArgumentsTest3(){

			var func:Function = function (a:int, ...params):int{
				var result:int = 0;
				for(var i:int = 0; i<params.length; ++i){
					result += a * params[i];
				}

				return result;
			};

			var mix:Function = function(a:int, ...params):String{
				var result:Array = [ a.toString() ];				
				for (var i:int; i<params.length; ++i){
					result.push( params[i].toString() );
				}

				return result.join();
			};

			trace( func(9) );		// 0
			trace( func(9, 2) );	// 18
			trace( func(9, 2, 3) );	// 45		
			trace( mix(9, 'Arick', 3.99, 789, true) ); // 9,Arick,3.99,789,true
 		}
	}	
}

Read Full Post »