2022年7月20日 星期三

複習 Dart 語法

dart 語法跟 c++ 非常類似, 習慣 c++ 語言應該很容易進入狀況, 參考官方文件: https://dart.dev/guides/language/language-tour


1. 常用類型: bool, int, double, String, List, Set, Map, BigInt
    BigInt: 大整數, 無限整數類型
    List 陣列, 用 [value, ... ] 可用中括號表列陣列值, 有序, 從 0 開始
    Set  集合, 用 {value, ... } 可用大括號列舉集合值, 無序
    Map  字典, 用 {key:value, ...} 可用大括號列舉成對 key:value, HashMap 無序, LinkedHashMap 依插入順序為序, SplayTreeMap 則排序過, 若無特別指定 Map 則用 LinkedHashMap 來實現. Map 以 [key] 當索引鍵, 存取內容時, 操作起來就像是陣列.
    整數 (char, int, int64, long, long long) 統一用 int, 浮點數(float, double) 統一用 double
    String: 可用成對單引號 '這是字串' 或 成對雙引號 "This 'is' string", 單雙引號混用時要注意成對的先後次序, 不得有穿插的情形
    Note:
        Sting: 使用 String.fromCharCodes([ ]) 將整數陣列轉為字串, 使用 .codeUnitAt(0) 將字元轉為整數, dart 沒有 char 類型, 字元可使用 int 或 String 來處理, 例如:
        int c = 'c'.codeUnitAt(0);
        String str = String.fromCharCodes([97]);
        字串串接, 在引號內使用 ${ } 當作串接位置(place holder), 例如: '... ${變數 或 運算式} ...'
    宣告關鍵字:
        const   固定值, 類型, 物件內容, 成員都不能改變
        final   最終類型的物件, 類型物件不能再變, 但物件裡的成員可改變
        var  固定的類型, 類型無法再變, 可重新指定為同類型的物件
        dynamic 變動的類型(類型, 物件, 內容全都能重新指定)
        
2. 類型無須使用 new, 直接呼叫新類型建構式就能實例化物件, 可使用 is 來檢驗物件類型, dart 不用指標, 只有物件, 使用 . 取用物件的成員

3. 基本運算
    四則運算: +, -, *, /, %, ~/, ++, --
    邏輯運算: !, &&, ||
    比較運算: >, <. >=, <=, ==, !=
    位元運算: ~, &, |, ^, >>, <<
    條件運算: 條件式 ? 正值 : 負值;
    
4. 標準輸出/輸入函式:
    ptint(""); // 自動輸出換行
    stdout.write(""); // 輸出無換行,  須 import "dart:io"
    stdin.readLineSync();// 輸入,  須 import "dart:io"
    
5. 引入程式庫 import, 引出程式庫 export, 專案底下目錄 lib 是專案程式庫的起始點(project library root)

6.透過 pubspec.yaml 指引, 程式庫相關性用命令 pub get 來管理

7. static 只能用在 class { }內, 不能用於 function( ){ } 內, c 的 static 實際上也是放在 global 區, 用符號連結過去而已, 若有必要, 可將變數宣告在 global 區在 function(){ } 內共享.

8. 函式內的預設參數(default parameter), 可以用中括號 [val, ...] 宣告預設位置參數, 或是大括號{key:value, ...} 宣告預設帶名參數, 兩者僅能擇其一, 不能同時使用. 預設參數宣告時, 必須放在無預設值後面, 但呼叫時, 除了位置參數需遵循宣告位置的順序外, 帶名參數可以放在任何位置, SDK 2.17 版之後, 不需擺在位置參數之後. 帶名參數前若使用 required 修飾, 呼叫時就必須給定該帶名參數的數值

9. 透過 FFI 可以用 C 語言直接與作業系統相互溝通

10.  理解 async/await 與 Isolate.spawn( ) 的運作邏輯:

await 一個 Future<T>(未來物件)必須放在 async 區塊內, 只要將函式區塊宣告成 async 變成非同步函式, 就可以用來 await 未來物件, 同時也可以在其它 async (非同步)區塊內被 await. 呼叫 async 函式, 意味著當執行到 await 時, 後續程序會安排到排程運行. 後續可以註冊(then)一個回調函式(callback function), 一旦在排程內執行完畢, 除了呼叫此回調函式外, 同時將結果一起傳回來. 因此呼叫 async 函式並不會阻塞程式的運行, 而是將非同步函式硬是拆成兩段(以 await 為分界點, 若無 await 就一直執行程式到最後), 前段執行完, 返回一個未來物件, 繼續往下運行, 後段將在排程中運行, 簡單來說就是非同步程序拆成現在與未來(先後)執行順序

子代孵化 Isolate.spawn(void Function(SendPort), SendPort) 則是強迫程序分裂成兩條路徑並行(另類的 multithread, 但相互獨立的執行空間), 新孵化的子代 isolate 運作後便一去不返, 父子代 isolate 只能透過 sendPort 與 receivePort 單向交流(sendPort 負責發送訊息, receivePort 用於聆聽訊息), 若父子代要雙向傳輸, 必須在子代程序內另外產生一個 receivePort, 把成員 sendPort 經由 Isolate.spawn 所附帶的 sendPort 傳給父代, 父代透過所收到的 sendPort 就能向子代發出訊息, 雙向溝通管道才建立完成. 父子代都是名副其實的隔離物件, 無法透過全域或區域變數共享任何資源, 測試程式:

import "dart:async";
import "dart:isolate";
void main( ) {
  print("Main thread isoloate: ${Isolate.current.debugName.toString()}");
 
  final bgIsolate = (SendPort port) {
        print("${DateTime.now()}:new  ${Isolate.current.debugName.toString()}");       
        Isolate.exit(port, "Finish");
  };
 
  final port = ReceivePort( );
  final child = Isolate.spawn(bgIsolate, port.sendPort, debugName: "childIsolate");

  child.whenComplete(() {//注意:並不是指 bgIsolate 程序執行完成,而是當 isoloate 孵化完成!
    print("${DateTime.now()}: spawn complete.");
  });

  port.first.then((value){
        print("${DateTime.now()}: childIsolate return $value");
        port.close( );
  });
 
  Timer(Duration(milliseconds: 3000), ( ) {
    print("${DateTime.now()}: time is up.");
  });
}
開啟終端機執行  dart   main.dart  看結果:
Main thread isoloate: main
2022-07-23 16:32:51.726479:new  childIsolate
2022-07-23 16:32:51.747431: spawn complete.
2022-07-23 16:32:51.753271: childIsolate return Finish
2022-07-23 16:32:54.740046: time is up

11.  dart 不需 compile 直接用 dart VM 執行:  dart  main.dart ,  或是用  dart compile exe main.dart 翻譯成原生系統上可執行的獨立執行檔 main.exe

12. 透過 dart compile 事先編譯, 將 .dart 原始碼轉換成不同平台上可執行的的代碼(exe/aot-snapshot/jit-snapshot/kernel/js)
    a. dart compile js main.dart -o main.js 用來將 dart 語法轉換成 javascript 語法, 用 nodejs main.js 就能執行
    b. dart VM 內含 dart runtime 可執行 kernel snapshot 及 jit-snapshot 代碼.
    c. kernel snapshot 是與硬體系統(x86, arm, powerPC, mips)無關的 dart VM 代碼(dart byte code), dart VM 仍要對他二次解譯(compile), 轉成機械碼(machine code), 才能執行.
    d. 所轉成的 jit-snappsot 代碼可以讓 dart VM 加速翻譯成 machin code 並執行, 因此作業系統上仍必需有相對應的 dart VM, 用來分配記憶體/解譯成 machine code/執行.
    e. aot-snapsot 是作業系統上(windows, ios, linux)可執行的機械碼(machine code), 無需再解譯(compile), 透過 dartaotruntime 執行該 aot-snapshot, 不用透過 dart VM, 就能呼叫作業系統的 runtime 來執行
    f. exe 執行檔是把 aot-snapshot 及 dart runtime 全包在一起, 在作業系統上用 dart compile exe main.dart -o main 轉成獨立執行檔 main, 在 shell 底下 ./main 就能執行
    g. 理論上執行速度: kernel  < jit-snapshot < aot-snapshot < exe

沒有留言:

張貼留言

使用 pcie 轉接器連接 nvme SSD

之前 AM4 主機板使用 pcie ssd, 但主機板故障了沒辦法上網, 只好翻出以前買的 FM2 舊主機板, 想辦法讓老主機復活, 但舊主機板沒有 nvme 的界面, 因此上網買了 pcie 轉接器用來連接 nvme ssd, 遺憾的是 grub2 bootloader 無法識...