Feeds:
文章
留言

Archive for 九月 16th, 2006

在開始這個主題之前,我最近又發現一種寫法可以更方便測試 ActionScript。下面是一個簡單的 hello world 框架

// MyClass.as
package {
    import flash.display.Sprite; // 引入 Sprite  所在的 Namespace

    public class MyClass extends Sprite { // 定義一個繼承自 Sprite 的類別
        public function MyClass() { // 建構子
            trace("hello world"); 
        }
    }
}

只要使用上面的框架,然後一樣使用 mxmlc MyClass.as 就可以產生 MyClass.swf 而不需要牽扯到目前尚未了寫的 MXML,而且對於熟悉 C++/Java 的 OO 開發者來說應該比較熟悉。至於Sprite 是什麼等到後續看到 Display 時就知道了,這次筆記改用這種寫法試試看。

所謂資料型別就是定義一組可供使用的值,如 Boolean 定義了 true 和 false 兩個值。在ActionScript 3.0 的型別又區分為 Primitive 和 complex ,只要是 Boolean, int, uint, Null, void, Number 和 String 都屬於 primitive,其餘的都屬於 Complex value(Object, Array, Date, RegExp, XML, XMLList, Error, Function)。在效能上,由於 Primitive 在 ActionScript 使用特別方式儲存,所以處理效能上優於 Complex 。不過這兩者在 ActionScript 3.0 中皆屬於物件(object)。所以下面兩行程式在 AS3.0 中是相等的:

var ia:int = 3;
var ib:int = new int(3);

除了的 Primitive 可以使用上述寫法外,Complex 也可以使用字面常數取代 new 運算子,如下:

var aa:Array = [1, 2, 3]; // 字面常數寫法(literal value)
var ab:Array = new Array(1, 2, 3); // 使用 Array 的建構子

如果你熟悉 Javascript 對於上述語法應該不會陌生。一般像我這種不喜歡打太多字的人而言,字面常數的寫法我比較喜歡。

AS3.0 中對於型別驗證支援編譯時期和執行時期的驗證,在 struct 模式之下支援編譯和執行時期驗證,但標準模式下只支援執行時期驗證。所謂的編譯和執行時期驗證分別表示程式在編譯階段驗證型別正確與否或是在程式真正執行階段進行驗證。編譯時期驗證可以提高程式的正確性,但失去程式的靈活度,但是執行時期驗證則剛好相反。因此一般大型專案都會採用編譯時期驗證,所以Flex Builder 預設使用 struct 模式,但你也可以是需要將其切換為標準模式。

如果你使用了 struct 模式的編譯時期資料型別驗證,你的程式必須提供編譯器所有相關型別的資訊來進行驗證,下面的範例說明如果在編譯階段提供明確的型別會發生編譯錯誤:

package {
    import flash.display.Sprite;

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

            var o1:Object = 3;
            dymamicCheck(o1);

            var o2:Object = "Arick"
            dymamicCheck(o2);
        }

        // 
        private function dymamicCheck(p:Object):void{
            if (p is String){
                  var s:String = p;// 在 struct 模式會出現編譯錯誤
                  trace("String Type:" + s); 
            }

            if (p is Number){
                  var n:Number = p;// 在 struct 模式會出現編譯錯誤
                  trace("Number Type:" + n); 
            }
        }

      }
}

執行結果:

 

使用強制型別轉換後就可以通過編譯:

package {
    import flash.display.Sprite;

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

            var o1:Object = 3;
            dymamicCheck(o1);

            var o2:Object = "Arick"
            dymamicCheck(o2);
        }

        // 
        private function dymamicCheck(p:Object):void{
            if (p is String){
                  var s:String = String(p);// 強制型別轉換

                  trace("String Type:" + s); 
            }

            if (p is Number){
                  var n:Number = Number(p); // 強制型別轉換

                  trace("Number Type:" + n); 
            }
        }

      }
}

執行結果:

 

is 和 as 運算子

在上面的範例中你會看到 is 這個運算子,這個運算子是用來檢測變數或運算是是否屬於指定資料型別的成員(member),如果是的就得到 true 否則為 false。

package {
    import flash.display.Sprite;

    public class ex3_1 extends Sprite {
        public function ex3_1() { 
            var o2:Object = "Arick";           

            trace(o2 is String);  // true
            trace(o2 is Object); // true
            trace(o2 is Number); // false

        }

      }
}

有一個與 is 運算子類似功能的 instanceof 運算子,這個是以前版本用來檢測變數是否屬於某個類別實體用的,但是該運算子無法測試出變數是否實作某個介面。例如:

package {
    import flash.display.Sprite;
    import flash.events.*;

    public class ex3_1 extends Sprite {
        public function ex3_1() { 
            var spr:Sprite = new Sprite();
            trace(spr is IEventDispatcher); // true
            trace(spr instanceof IEventDispatcher); // false

        } 
   }
}

AS3.0 另一個新運算子為 as ,這個運算子和 is 不太一樣,如果你有用過 C# 你應該很熟悉 is 與 as 運算子,簡單的說 is 是用來檢驗變數是否轉行為指定的型別或介面,如果可以回傳 true 否則為 false。as 運算子則是將變數轉形成指定型別,如果轉型成功就是指定的型別,如果轉型失敗則為左側性別的預設值。不過和 C# 不太一樣的是如果兩個型別差異太大無法轉換,則在編譯階段就會出現錯誤,但是C#並不會只會回傳 null。例如:

package {
    import flash.display.Sprite;

    public class ex3_1 extends Sprite {
        public function ex3_1() { 
            var o2:Object = "Arick";
            var s:String = "Arick"

            var n:Number = s as Number; // 0
            trace(n);

            var s2:String = s as String; // Arick
            trace(s2);

            //var a2:Array = s as String; // struct mode: Compiler Error

            var a3:Array = o2 as Array; // null
            trace(a3); 
        }

      }
}

動態型別

如果你寫過 Javascript  會發現這個真是個很動態的語言,可以在執行時期隨時新增屬性和方法。AS3.0也支援類似的功能,首先必須在類別前面使用 dynamic 這個特性(attribute)修飾字。之後就可以像Javascript 在執行階段動態擴充這個型別。以下面範例來說明這個主題:

// dc.as
package {
  public dynamic class dc{
    public function dc(){
      trace("dc constructor");
    }
  }
}

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

  public class ex3_2 extends Sprite {
     public function ex3_2() {
        var dcObj:dc = new dc();
        dcObj.newMethod = function():void{
           trace("dynamic create method");
        }; // 動態新增一個 member function

    dcObj.newMethod(); // 喚起動態新增的 member functiom

    dcObj.newProperty = "Arick"
    trace( dcObj.newProperty ); // 使用動態新增的 member field

  }
 }
}

執行結果:
dc constructor
dynamic create method
Arick

 

資料型別描述

AS3.0 定義 primitive 型別包含 Boolean, int, Null, Number, String, uint 和 void。ActionScript 核心類別也定義了下面的 Complex 型別:Object, Array, Date, Error, Function RegExp, XML 和 XMLList。

基本上 AS3.0 的 primitive 型別和其他語言沒有什麼太大的差別,Number/int/uint 這三個型別比較混淆,Number 型別是舊版 AS 舊支援的,他可以用來儲存整數、無符號整數和浮點數。int 和 uint 是 AS3.0 基於效能考量而提供的新型別,int / uint 是用來存放 32 位元整數,Number 則用來存放 64 位元數值,且實作 IEEE-754 實作浮點數。下面是這三種型別所能表示的值域:

package {
 import flash.display.Sprite;  

 public class CTypeTest extends Sprite{  
  public function CTypeTest(){
   trace( int.MAX_VALUE); // 2147483647
   trace( int.MIN_VALUE); // -2147483648

   trace( uint.MAX_VALUE); // 4294967295
   trace( uint.MIN_VALUE); // 0

   trace( Number.MAX_VALUE); // 1.79769313486231e+308
   trace( Number.MIN_VALUE); // 4.9406564584124654e-324   
   }
 } 
}

雖然使用 int/uint 可以比 Number 得到較加的效能,可是如果你的數值為浮點數或超出 int/uint 所能表示的範圍時,你還是只能使用 Number 型別。

String 型別是很常用的,他是一連串的字元所構成,每個字元以16位元來表示,他使用的是 UTF-16編碼。另外有一點跟效能相關的是,字串建立後就不能進行修改,即時你使用String 類別提供的方法,他只會將處理後的新字串回傳回來,原始的字串內容並不會改變。所以,如果你進行大量的字串合併時應該避免使用 + 來進行,因為每一個 + 運算都會產生一個新的字串實體,如下每個 ( ) 都是一個新的字串實體:
var s4:String = "a" + "b" + "c" + "d" + "e" + "f"; 
                 //= ((((("a" + "b") + "c" ) + "d") + "e") + "f")

而每個字串產生新實體時又需要呼叫建構子來進行初始化,這個過程相當浪費時間。最後 String 型別的預設值威null。

void 型別只包含 undefined 值,如果函數的回傳型別指定為 void 表示函數沒有回傳值。如果沒有明確指定函數回傳型別,則預設是 * 型別(未定義型別)而非無回傳型別。 

型別轉換

當一個型別的值指定給另一個型別變數就會發生型別轉換。型別轉換分為隱式(implicit 或 correction)型別轉換和顯式(explicit 或 casting)型別轉換,隱式轉換就是設計者沒有明確的指定兩個不同型別之間的轉換,而是由 Flash Player 在執行階段自行將型別轉換,而顯式轉換就是明確指定轉換後的型別。如果設計者要明確將某個資料型別轉成另一個型別,必須使用型別加上圓括弧來進行宣告,與 C 語言語法不太一樣,如下:

var n:Number = 1000.999;
var x:String = String(n); // 轉 String 型別
var y:int = int(n);  // 轉 int 型別

最後來看看不同型別之間的轉換關係(以下範例完整程式需要者請點選下載)

1. 轉型為 int/uint/Number
* 將浮點數轉型為 int/uint ,小數部份會被截斷
* Boolean 型別 true 為 1,其餘為 0。
* Date 型別則回傳從 1970/1/1 到目前為止總毫秒數
* null 型別回傳 0
* Object 型別如果為 null 則同 null 型別,否則 int/uint 為 0 而 Number 為 NaN
* String 型別如果可以順利轉換則轉為一個數值,否則 int/uint 為 0 而 Number 為 NaN,注意,如果 int/uint 所能表示的值域。
* String 型別字串如果包含以 0x 開頭的字串表示是一個 16 進位的數值,但不支援 8 進位以 0 開頭的字串。
* String 轉 int/uint/Number 會忽略字串前後的空白字元,但是如果字串數值之間包含空白,則 int/uint 為 0 而 Number 為 NaN
* undefined 型別,int/uint 為 0 而 Number 為 NaN
* 假如指定一個型別變數的值超出該型別所能表示的範圍,則會出現一個未預期內的值要特別小心。

範例程式片段(CNumberCast.as):

package { 
 /**
  * 數值轉型測試
  *
  **/
 public class CNumberCast implements ICast{  
  public function CNumberCast(){
   }
  public function Test():void{
   trace('** [CNumberCast] **');
   BooleanToNumberTest();
   intTest();
   uintTest();
   StringToNumberTest();
   undefinedToNumber();
   nullToNumber();
   DateToNumber();
   ObjectToNumber();
   unexpectedTest();
  }
  /**
   * Boolean 轉數值測試
   *
   **/
  private function BooleanToNumberTest():void{
   trace('== BooleanToNumberTest() ==');
   var bTrue:Boolean = true;
   var bFalse:Boolean = false;
   trace("bTrue: " + bTrue);
   trace("bFalse: " + bFalse);
   var iTrue:int    = int(bTrue);  // 1
   var iFalse:int   = int(bFalse);  // 0
   trace("iTrue: " + iTrue);
   trace("iFalse: " + iFalse);
   
   var uiTrue:uint  = uint(bTrue);  // 1
   var uiFalse:uint = uint(bFalse); // 0
   trace("uiTrue: " + uiTrue);
   trace("uiFalse: " + uiFalse);
   var nTrue:Number = Number(bTrue); // 1
   var nFalse:Number = Number(bFalse); // 0
   trace("nTrue: " + nTrue);
   trace("nFalse: " + nFalse);
  }
  private function undefinedToNumber():void{
   trace('== undefinedToNumber() ==');
   trace('int(undefined): ' + int(undefined));   // 0
   trace('uint(undefined): ' + uint(undefined));  // 0
   trace('Number(undefined): ' + Number(undefined)); // NaN
  }
  private function nullToNumber():void{
   trace('== nullToNumber() ==');
   trace('int(null): ' + int(null));  // 0
   trace('uint(null): ' + uint(null));  // 0
   trace('Number(null): ' + Number(null)); // 0
  }
  public function DateToNumber():void{
   trace('== DateToNumber() ==');
   var d:Date = new Date(1970, 1, 1, 0, 0 ,0);
   trace('int(null): ' + int(d));  // 0
   trace('uint(null): ' + uint(d));  // 0
   trace('Number(null): ' + Number(d)); // 0
   d = new Date();
   trace('int(null): ' + int(d));  // 0
   trace('uint(null): ' + uint(d));  // 0
   trace('Number(null): ' + Number(d)); // 0
  }
  private function ObjectToNumber():void{
   trace('== ObjectToNumber() ==');
   var o:Object = null;
   trace(int(o));   // 0
   trace(uint(o));   // 0
   trace(Number(o));  // 0   
   o = new Object();
   trace(int(o));   // 0
   trace(uint(o));   // 0
   trace( Number(o));  // NaN   
  }
  /**
   * int 測試
   *
   **/
  private function intTest():void{
   trace('== intTest() ==');
   var iMin:int = int(Number.MAX_VALUE);
   var iMax:int = int(Number.MIN_VALUE);
   trace('iMin: ' + iMin); // 0
   trace('iMax: ' + iMax); // 0 
  }
  /**
   * uint 測試
   *
   **/
  private function uintTest():void{
   trace('== uintTest() ==');
   var uiMin:uint = uint(Number.MAX_VALUE);
   var uiMax:uint = uint(Number.MIN_VALUE);
   trace('uiMin: ' + uiMin); // 0
   trace('uiMax: ' + uiMax); // 0  
   
  }

  /**
   * 字串轉數值測試
   *
   **/
  private function StringToNumberTest():void{
   trace("==  StringToNumberTest() ==");
   trace('Number(" 5"):' + Number(" 5"));  // 5
   trace('Number(" 5 "):' + Number(" 5 ")); // 5
   trace('Number("-5"):' + Number("-5"));  // -5
   trace('Number("5.98"):' + Number("5.98")); // 5.98
   trace('Number("0x0a"):' + Number("0x0a")); // 10
   trace('Number("044"):' + Number("044")); // 44
   trace('int(" 5"):' + int(" 5"));   // 5
   trace('int(" 5 "):' + int(" 5 "));   // 5
   trace('int("-5"):' + int("-5"));   // -5
   trace('int("5.98"):' + int("5.98"));  // 5
   trace('int("0x0a"):' + int("0x0a"));  // 10
   trace('int("044"):' + int("044"));   // 44
   trace('uint(" 5"):' + uint(" 5"));   // 5
   trace('uint(" 5 "):' + uint(" 5 "));  // 5
   trace('uint("-5"):' + uint("-5"));   // 4294967291                   
   trace('uint("5.98"):' + uint("5.98"));  // 5
   trace('uint("0x0a"):' + uint("0x0a"));  // 10
   trace('uint("044"):' + uint("044"));  // 44
  }
  
  public function unexpectedTest():void{
   trace('== unexpectedTest() ==');
   var ui:uint = -3;
   trace(ui); // 4294967293
   var n:Number = ui;
   trace(n); // 4294967293
   var iOverMax:int = uint.MAX_VALUE + 1;
   trace(iOverMax); // 0
   iOverMax = int.MAX_VALUE + 1;
   trace(iOverMax); // -2147483648
  }
 } 
}

2. 轉型為 Boolean
* String 型別如果為 null 或空字串回傳 false, 否則為 true
* null 型別回傳 false
* int/uint/Number 如果為 0 或 NaN 回傳 false 否則為 true
* Object 型別如果為 null 回傳 false,否則為 true 

範例程式(CBooleanCast .as):
package { 
 /**
  * Boolean 轉型測試
  *
  **/
 public class CBooleanCast implements ICast{  
  public function CBooleanCast(){

   }

  public function Test():void{
   trace(‘** [CBooleanCast] **’);
   intToBoolean();
   StringToBoolean();
   ObjectToBoolean();
   nullToBoolean();
  }

  private function intToBoolean():void{
   trace(‘== intToBoolean() ==’);
   trace(‘Boolean(-1): ‘ + Boolean(-1) ); // true
   trace(‘Boolean(0): ‘ + Boolean(0) ); // false
   trace(‘Boolean(1): ‘ + Boolean(1) ); // true
  }

  private function StringToBoolean():void{
   trace(‘== StringToBoolean() ==’);
   var s1:String;
   trace( Boolean(s1) ); // false

   var s2:String = "";
   trace( Boolean(s2) ); // false

   var s3:String = " ";
   trace( Boolean(s3) ); // true

   var s4:String = "A";
   trace( Boolean(s4) ); // true

   var s5:String = "Arick";
   trace( Boolean(s5) ); // true
  }

  private function ObjectToBoolean():void{
   trace(‘== ObjectToBoolean() ==’);
   var o:Object;
   trace( Boolean( o ) ); // false

   o = new Object();
   trace( Boolean( o ) ); // true
  }

  private function nullToBoolean():void{
   trace(‘== nullToBoolean() ==’);
   trace(Boolean( null ) ); // false
  }
 } 
}

3. 轉型為 String
* Array 型別則會將每個元素以 , 分隔串接
* Boolean 型別傳回 "true" 或 "false"
* Date 型別傳回目前表示的日期時間,如:Sun Oct 15 00:00:00 GMT+0800 2006
* null 型別傳回"null"
* int/uint/Number 型別傳回表示的數值字串
* Object 型別如果為 null 回傳 "null" 否則為 "[object Object] "

範例程式(CStringCast.as):
package { /** * 字串轉型測試 * **/ public class CStringCast implements ICast{ public function CStringCast(){ } public function Test():void{ trace('** [CStringCast] **'); BooleanToString(); NumberToString(); ArrayToString(); DateToString(); nullToString(); undefinedToString(); ObjectToString(); } private function BooleanToString():void{ trace('== BooleanToString() =='); trace(String(true)); // true trace(String(false)); // false } private function NumberToString():void{ trace('== NumberToString() =='); trace( String(-1.0) ); // -1 trace( String(0.999999)); // 0.999999 trace( String( 0 ) ); // 0 } private function ArrayToString():void{ trace('== ArrayToString() =='); trace(String(['Arick', 'Mavis', 'Joss'])); // Arick,Mavis,Joss } private function DateToString():void{ trace('== DateToString() =='); trace( String( new Date(2006, 9, 15) ) ); // Sun Oct 15 00:00:00 GMT+0800 2006 } private function nullToString():void{ trace('== nullToString() =='); trace( String( null ) ); // null } private function undefinedToString():void{ trace('== undefinedToString() =='); trace( String( undefined) ); // undefined } private function ObjectToString():void{ trace('== ObjectToString() =='); var o:Object; trace( String( o ) ); // null o = new Object(); trace( String( o ) ); // [object Object] } } }

Read Full Post »

我的第一個 Fortran 程式

昨天答應幫別人看一個我不懂的 Fortran 程式,今天嘗試寫了一個 Hello World 程式,下面就是我的第一個範例:

! 開發環境:
!  1. Windows XP Professional
!  2. Microsoft Fortran PowerStation 4.0 
!
! 設計者:Chui-Wen Chiu(Arick)
!

program HelloWorld ! 指定程式名稱
  implicit none ! 強制變數都要宣告

  integer(2) i ! 整數變數宣告
  character(20) helloStr ! 字串變數宣告

  helloStr="hello world" 
  print *, helloStr ! 列印 hello world
  print *, HelloStr ! 變數不分大小寫

  do i=1,9,1   ! 迴圈,從 1 印到 9
    print *, i 
  end do
end ! 程式結束標記

我覺得基本要素都差不多,只不過結尾 end 我真覺的是多餘的。不過聽說 Fortran 的長處在於科學運算,目前我還不知道他有提供哪些好用的函數。

不過,如果用 Fortran 來寫視窗程式我個人是覺得礙手礙腳,我覺得他對我的用途在於提供數學運算的函數功能,所以把 Fortran 封裝成 DLL 或 Component 是個不錯的方向。另外,Fortran 也有針對 .NET 平台提供對應的編譯器,如下:

1. http://www.lahey.com/lf71/lfnet.htm
2. http://www.silverfrost.com/11/ftn95/overview.asp

未來如果 NET 程式需要特別運算的函數就用 Fortran 先寫好需要的演算法,再封裝成 DLL 或 .NET Component 來使用。

寫到這突然想到我另一個蠻喜歡的語言 MATLAB,這個矩陣運算相當方便的語言,查了一下似乎還沒有提供 .NET Compiler,目前要使用 MATLAB 功能還是只能透過傳統的方式,有興趣者可以參考"1..2..3 ways of integrating MATLAB with the .NET"。

Read Full Post »

兩篇獨孤木的雞肋文章

真是深得我心的兩篇文章,兩天文章依然不改獨孤木的本色

食之無味 棄之可惜
安心啃雞肋還是…?

Read Full Post »

看到 YAHOO 信箱改版新聞感覺介面互動上變的比較好了,也提供許多不錯的功能,可是垃圾信過濾問題不知道有沒有改善,我從申請之後,整個信箱都是垃圾信,即使我設定的黑名單還是一堆廣告信,基本上這個信箱我已經不再使用,只能說是個裝垃圾的信箱。如果信箱收不到你要的信,或是要找到指定信件很困難,基本上這個服務是很差的,其他功能做的再好也沒什麼用… 像 Gmail 就做的相當好,容量大、分類方便、廣告信幾乎沒有…這才叫服務,只能收廣告信的信箱…不知道能做什麼?

Read Full Post »