iT邦幫忙

2023 iThome 鐵人賽

DAY 3
1

今天我們要來談談關於 Dart 的迴圈、判斷式以及函式的語法,馬上開始吧!

條件判斷式

條件判斷式是執行邏輯判斷中重要的程式區塊,在 Dart 中提供了三種條件判斷式的語法

if

if-else 是最常見的條件判斷式

if (weather.now == weather.isRaining) {
  // 如成立執行此區塊
  print('下雨了');
} else if (weather.now == weather.isCloudy) {
  // 當條件 1 不成立時,判斷條件 2,若成立則執行此區塊
  print('刮風了');
} else {
  // 當條件 1, 2 接不成立則執行此區塊
  print('天氣還不錯');
}

if-case

if-caseif 的變形,表是 if 判斷式滿足 case 的情況則執行該區塊

需 Dart version >= 3.0 才支援此語法

if (weather.now case weather.isRaining) {
  print('下雨了');
}

switch

switch 針對單個判斷式進行評估,並且與一系列的 case 進行比對,也可設定 default 來執行預設行為。與其他程式語言比較不同的是,當滿足一個 case 時不需要再 break 離開整個判斷式。

switch (weather.now) {
  case weather.isRaining:
    print('下雨了');
  case weather.isCloudy:
    print('刮風了');
  default:
    print('天氣還不錯');
}

迴圈

迴圈可以將某個區塊的程式碼重複的進行運算,並且可以設定執行條件、次數。

在真正的瞭解迴圈寫法之前我們先來了解兩個關鍵詞 IterableiteratorIterable 表示多元素數據集合,並且這些元素可以按照順序的進行走訪。Iterator class 提供接口給 iterator ,也就是集合中的各元素對象。

在 Dart 中符合 Iterable 的有 ListSetMap,因為都有實現 Iterable 接口。

For 迴圈

最基本的 for 迴圈寫法,想必各位如果有碰過程式都不會太陌生。

List<int> nums = [1, 2, 3, 4, 5];

for (var i = 0; i < 5; i++) {
  print(nums[i]);
}

我們也可以使用迭代的方式 (Iterable ) 來走訪 List。

List<int> nums = [1, 2, 3, 4, 5];

// 這個寫法與上面例子不一樣的點在於,這裡的 num (iterator) 是真的照順序的進行走訪 nums (iterable)
// 但是上面的寫法是直接透過操作 index 指向想要的目標
for (final num in nums) {
  print(num);
}

最後還有一種為 forEach ,可以針對 iterator 執行動作

List<int> nums = [1, 2, 3, 4, 5];

/* 這種寫法跟 js 的 arrow function 很像
nums.forEach((num) => {
  print(num);
}); */
nums.forEach(print);

Whiledo-while

while 在執行迴圈之前會先進行條件判斷;do-while 則是先執行一次迴圈中的行為再進行條件判斷。

List<int> nums = [1, 2, 3, 4, 5];
int index1 = 0;

// 先執行條件判斷
while (index1 < nums.length) {
  print(nums[index1]);
  index1 += 1;
}
int index2 = 0;

// 先執行程式區塊
do {
  print(nums[index2]);
  index2 += 1;
} while(index2 < nums.length);

Breakcontinue

兩者皆為在迴圈中常用的關鍵字,break 可以跳出當前的迴圈;continue 則是跳到下一輪迴圈的狀態繼續執行。

List<int> nums = [1, 2, 3, 4, 5];

for (var i = 0; i < nums.length; i++) {
  if (i == 1) {
    // 當 i 為 1 時 continue 跳到下一輪迴圈
    continue;
  } else if (i == 3) {
    // 當 i 為 3 時跳出迴圈
    break;
  }
  // 有成功執行到這裡的只有當 i 為 0 與 2 的時候,所以只會印出 1 3
  print(nums[i]);
}

函式

函式是將重複的程式邏輯、動作進行封裝並且可重複呼叫的重要角色。
函式與變數相同有各自的型態,對應於此函式將要回傳值的型態。若該函式無回傳值則宣告為 void 型態。

// 定義一個函數名為 isEmpty 回傳值為 bool 型態,並且傳入參數 nums 為 List<int> 型態
// 此函數用於判斷傳入的陣列是否為空,因此當長度為 0 時則回傳 ture,否則為 false
bool isEmpty(List<int> nums) {
  return nums.length == 0;
}

參數 (parameters)

函式的參數是在定義一個函式時需要的變數,像是上述例子的函式中 nums 就是此函式所需要的變數,因此在呼叫函式時需要傳遞相應型態的變數給函式使用。

一個函式可以有無限個參數,並且按照定義的順序來傳遞。但為了讓函式更具彈性,Dart 支援 optional parameters 也就是可以選擇性傳遞的參數,可以以 named parametersoptional positional parameters 兩種方法擇一來使用。

1. Named Parameters

當使用 named parameters 時以 {參數1, 參數2, ...} 格式進行定義。若沒有給定參數的預設值或標示 required 時即表示該參數為 nullable 且預設值為 null 。相對來說,若是標示 required 的參數則是在呼叫該函式時必須夾帶該參數才行。

// 此函式定義參數
//   - name 有標註 required,表示呼叫此函數時需夾帶 name 參數
//   - age 有給預設值,因此呼叫此參數若有給 age 使用夾帶值,否則帶入 0
//   - isMale 為 nullable,因此可選擇性夾帶
void addPerson({required String name, int age = 0, bool? isMale}) { ... }

void main() {
  // 在呼叫函式時,就可以具名的給定參數
  addPerson(name: 'Micro Jordan', age: 25, isMale: true);
  addPerson(name: 'Amy', age: 25);
  addPerson(name: 'Mike');
}

2. Optional positional parameters

將參數用 [] 包住就可以表示該參數為可選擇性的夾帶,若未給預設值的參數,則表示該參數為 nullable 且預設值為 null

// 此函數定義參數
//  - name 因為未用 [] 包起來,需擺放在第一個參數位置
//  - age 被包在 [] 中因此可選擇性夾帶,因有給預設值所以未使用時預設帶入 0
//  - isMale 則可選擇性的夾帶
void addPerson(String name, [int age = 0, bool? isMale]) { ... }

void main() {
  addPerson('Micro Jordan', 25, true);
  addPerson('Amy', 25);
  addPerson('Mike');
}

補充:main() 函式為整個程式的進入點,當我們在使用該程式時也可以給定執行時的參數,讓程式支援多項指令的功能。

舉例來說:在 linux command 中有提供 ls 這個指令,用於列出當前資料夾底下的檔案結構。若是 ls -l 則能列出這些檔案結構各字詳細的資訊、ls -a 可列出隱藏檔等等。這個實現方式便是將 -l-a 等等的當作執行參數傳入程式中,讓程式進行對應的操作。

void main(List<String> arguments) {
  // 參數會以 List of String 的形式傳入程式當中
  print(arguments);
}

匿名函式

我們剛剛看到的函式都有明確的名稱,但 Dart 也支援匿名函式,用於簡單的操作上並可讓程式碼更簡潔。匿名函式使用 => 來定義,我們用例子來說明

// 我們定義了匿名函式 () => ... 並且將 function 指派給 add 變數
var add = (int x, int y) => x + y;
// 因此可執行
print(add(3, 5));

泛型

泛型可以讓定義的程式碼能兼容多種型態,也能有效的避免程式碼間只是因為型態不同卻要覆寫很多份的難題。其實我們在目前接觸到的如 ListSetMap 都是運用到泛型來定義並且實作的。
我們從簡單的例子來看看

// 定義取得傳入 List 的第一個元素
int getFirstItem(List<int> list) {
  return list[0];
}

上面的這個例子我們傳入了整數型態的 List,並因為整數 List 中第一個元素一定也是整數,所以再回傳 int 型態,目前為止都沒什麼問題。
但當我們今天需要實現 String 型態的實作方法呢?我們則必須再寫一次一模一樣的程式碼,僅僅只是型態不同而已。

// 再寫一次幾乎一模一樣的程式
String getFirstItem(List<String> list) {
  return list[0];
}

因此這時候就可以讓泛型派上用場,當我們在查看 Dart 文件時應該時常看到型態是用 TE 等等的單一字母來標註型態,這其實也就是泛行的寫法。由於我們傳入的為單一型態設為T 的 List ,因函式的實作便是回傳 List 中第一個元素,因此回傳型態也為 T 。函式名字後的<T> 則用於標示於該函式中我們定義了 T 這一個泛型。

T getFirstItem<T>(List<T> list) {
  return list[0];
}

當今天有多個型態的泛型需要實現時如Map 的定義為 Map<K, V> ,就在<> 多宣告即可。

今日總結

  • 條件判斷式:條件判斷式是用於執行邏輯判斷的重要程式區塊,Dart 提供了三種條件判斷式的語法:
    1. if:使用最常見的 if-else 來執行不同條件下的程式。
    2. if-case:透過 if-case 在特定條件下執行程式。
    3. switch:使用 switch 針對單一判斷式進行評估,比對多個 case,也可設定 default 來執行預設行為。
  • 迴圈:
    1. for 迴圈:基本的 for 迴圈,用於指定次數的迭代。
    2. whiledo-whilewhile 在迴圈前進行條件判斷,而 do-while 先執行一次迴圈後再進行條件判斷。
    3. breakcontinue:用於控制迴圈的流程,break 可以跳出迴圈,continue 可以跳過目前迴圈的狀態繼續執行下一輪迴圈。
  • 函式:函式用於封裝重複的程式邏輯,並且可以重複呼叫。Dart 函式可以有參數,參數可以是必要的或選擇性的。另外,Dart 支援匿名函式,用於簡單的操作。函式的參數可以使用 named parametersoptional positional parameters 兩種方式來定義。
  • 泛型:泛型使得程式碼可以兼容多種型態,並且避免因型態不同而重複撰寫程式碼。在 Dart 中,泛型使用單一字母標註型態,如 T,可以應用於函式、類別等地方。這提高了程式碼的彈性和重用性。

今天介紹的概念我們都從比較簡單的角度進行切入,希望有幫助到大家對於這個語言的初步認識。明天我們會談談類別、錯誤處理相關的內容。明天見囉~


上一篇
[Day 02] Dart 基礎語法 Part 1
下一篇
[Day 04] Dart 基礎語法 Part 3
系列文
Flutter 從零到實戰 - 30 天の學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言