C47. 方形 — nested loop(巢狀迴圈) 練習

練習題目

根本益智遊戲啊XD

練習

for與in之間何時用 _ ,什麼時候是要給一個名稱呢?

for-in迴圈可以用來迭代一個集合的所有元素。你可以使用一個特定的變數名稱來存儲每次迭代中的元素值,或者使用_來忽略該值。

  1. 使用一個名稱:當你需要在迴圈體中使用每次迭代的元素值時,應該給出一個名稱。例如:
let numbers = [1, 2, 3, 4, 5]
for number in numbers {
print(number)
}
  • 在上述例子中,給出了名稱number來代表numbers陣列中的每一個元素,並在迴圈體中印出每一個數字。

2. 使用_:當你不需要在迴圈體中使用每次迭代的元素值時,可以使用_來忽略該值。例如:

let numbers = [1, 2, 3, 4, 5]
for _ in numbers {
print("Hello, world!")
}
  • 在上述例子中,並不關心numbers陣列中的每一個元素是什麼,我們只是想重複印出"Hello, world!"的訊息numbers.count次。因此,我們使用_來忽略每一個元素值。

總的來說,選擇使用_還是一個名稱取決於你是否需要在迴圈體中使用每次迭代的元素值。

判斷使用場景

決定使用元素值名稱還是_主要取決於你是否需要在迴圈中使用每次迭代的元素值。以下是一些具體的場景來說明這個概念:

  1. 使用元素值名稱的場景:
  • 計算總和:如果你有一個數字陣列並且你想計算其總和,你需要在每次迴圈中訪問每個數字,因此你應該使用元素值名稱。
  • 操作元素:如果你要對陣列的每個元素進行某種操作(例如 print,修改,或應用某種函數),你需要使用元素值名稱。

2. 使用_的場景:

  • 單純的迭代:如果你只是想迴圈固定的次數,但並不需要知道每一次迴圈的元素值,那麼你可以使用_。例如,你想打印"Hello, World!"10次,但你並不在乎當前是第幾次迴圈
  • 忽略元素:如果你遍歷一個陣列但實際上不需要使用其中的元素,例如你只是想知道陣列有多少元素,那麼你可以使用_

總的來說,當你需要在迴圈中操作或者訪問每個元素時,應該使用元素值名稱。反之,如果你不需要使用元素值,則可以使用_

第一行與其他行不同

  • 判斷行數 i 是否等於 1,若是則在 content 字串中加入 "🐦" 符號,表示該位置位於第一行
  • 否則加入 "🐤" 符號,表示該位置不在第一行。
    // 第一行:第一行"🐦",其他行"🐤"
func firstRowPattern() {

let count = Int(emojiSliderControl.value) // 將slider值轉為整數。
countValueLabel.text = "\(count)" // 將 count 的值做當前數值顯示。
var content = "" // 用於存儲生成的圖案內容。

for i in 1...count { // 遍歷行數

for _ in 1...count { // 遍歷列數

if i == 1 { // 判斷是否在第一行
content += "🐦"
} else {
content += "🐤"
}
}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

// 將內容加到Label
displayEmojiLabel.text = content
}

隔行生成(奇數行、偶數行)

  • 檢查當前的行數 i 是否為奇數,若是則在 content 字串中加入 "🐦" 符號,表示奇數行
  • 否則加入 "🐤" 符號,表示偶數行。
    // 間隔行:每隔一行交互產生: 🐦行 以及 🐤行。
func spacingPattern() {

let count = Int(emojiSliderControl.value) // 將slider值轉為整數。
countValueLabel.text = "\(count)" // 將 count 的值做當前數值顯示。
var content = "" // 用於存儲生成的圖案內容。

for i in 1...count { // 遍歷行數

for _ in 1...count { // 遍歷列數

if i % 2 == 1 { // 檢查當前的行數 i 是否為奇數。
content += "🐦"
} else {
content += "🐤"
}
}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

// 將內容加到Label
displayEmojiLabel.text = content
}

對角線

  • 在生成的正方形矩陣中,對角線上的元素會形成一條從左上到右下的對角線。
  • i == j 條件,若為真則在 content 字串中加入 "🐦" 符號,表示該位置位於對角線上。
    // 對角一:行數位置加一,列數位置也會加一(行數=列數)
func diagonalPattern() {

let count = Int(emojiSliderControl.value) // 轉整數
countValueLabel.text = "\(count)" // 當前數值顯示
var content = "" // 添加的內容

for i in 1...count { // 遍歷行數

for j in 1...count { // 遍歷列數

if i == j { // 行數與列數相等時
content += "🐦"
} else { // 當行數與列數不相等時
content += "🐤"
}
}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

// 將內容加到Label
displayEmojiLabel.text = content
}

i == j

  • 對角線條件 i == j 是因為在一個正方形矩陣中,對角線上的元素具有相同的行數和列數。當行數 i 與列數 j 相等時,即滿足 i == j 條件,這代表該位置位於對角線上。
  • 假設有一個 3x3 的正方形矩陣,該矩陣的每個元素可以用 (i, j) 表示,其中 i 為行數,j 為列數。以下是該矩陣的索引表示方式:
// 正方形矩陣座標

(1, 1) (1, 2) (1, 3)
(2, 1) (2, 2) (2, 3)
(3, 1) (3, 2) (3, 3)
  • 例子中的對角線元素為 (1, 1)(2, 2)(3, 3)。這些元素的行數 i 與列數 j 相等,即 i == j
  • 因此,當在程式碼中使用 if i == j 條件時,它會判斷當前的行數 i 是否等於列數 j,如果相等則表示該位置位於對角線上,可以進行特定的處理或標記。

反對角線

沒想到換個方向的對角線,觀察的角度就不同了XD

  • 判斷行數與列數之和 (i+j) 是否等於矩陣的邊長加1 (count + 1),則在 content 字串中加入 "🐦" 符號,表示該位置位於反對角線上
  • 否則加入 "🐤" 符號,表示該位置不在反對角線上。
    // 對角二:
func antiDiagonalPattern() {

let count = Int(emojiSliderControl.value) // 轉整數
countValueLabel.text = "\(count)" // 當前數值顯示
var content = "" // 添加的內容

for i in 1...count { // 遍歷行數

for j in 1...count { // 遍歷列數

if (i+j) == count { // 判斷是否在反對角線上
content += "🐦"
} else {
content += "🐤"
}
}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

// 將內容加到Label
displayEmojiLabel.text = content
}

規律:反對角線上的每個位置的行數和列數之和都是相同的。

  • 在一個正方形矩陣自身中,反對角線由左上角到右下角的斜線。例如,對於一個3x3的矩陣,反對角線上的元素分別是 (1, 3),(2, 2) 和 (3, 1)。
例如:
(1, 3) 的行數和列數之和是 1+3=4。
(2, 2) 的行數和列數之和是 2+2=4。
(3, 1) 的行數和列數之和也是 3+1=4。

( i + j ) == count + 1

  • 正方形矩陣的邊長是相同的,所以總行數和總列數之和就是矩陣的邊長加1。例如,對於一個3x3的矩陣,邊長是3,所以總行數和總列數之和是 3+3=6。
  • 因此,可以使用 (i+j) == count + 1 來判斷是否在反對角線上,其中 i 是行數,j 是列數,count 是矩陣的邊長。
    // 對角二:
func antiDiagonalPattern() {

let count = Int(emojiSliderControl.value) // 轉整數
countValueLabel.text = "\(count)" // 當前數值顯示
var content = "" // 添加的內容

for i in 1...count {

for j in 1...count {

if (i+j) == count + 1 {
content += "🐦"
} else {
content += "🐤"
}
}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

// 將內容加到Label
displayEmojiLabel.text = content
}
  • 如果是 (i+j) == count
錯誤的

X形狀(值必須為奇數)

  • 只能用於奇數大小的圖案,因為只有奇數大小的圖案才能形成 X 形狀的交叉部分。
  • 判斷條件 i == j || (i+j) == count + 1 用來判斷該位置是否應該是 "🐦" 符號。
  • 當行數等於列數 i == j 或行數與列數的和等於 count + 1 時,表示該位置位於 X 形狀的交叉部分,因此將 "🐦" 加入。
    // X 形狀 (count只能是奇數)
func xShapePattern() {

let count = Int(emojiSliderControl.value) // 取得滑桿的值,轉換為整數
countValueLabel.text = "\(count)" // 將 count 的值顯示在畫面上
var content = "" // 用於存儲生成的圖案內容

for i in 1...count { // 遍歷行數

for j in 1...count { // 遍歷列數

if i == j || (i+j) == count + 1 { // 判斷是否位於 X 形狀的交叉部分
content += "🐦"
} else {
content += "🐤"
}
}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

// 將內容加到Label
displayEmojiLabel.text = content
}

如果是偶數的話(以8為例)

  • 如果 count 是偶數,X 形狀的圖案在正中間的位置可能會缺少一個交叉點,因為無法找到一個正確的位置使得兩條對角線相交。
偶數狀態

十字(值必須為奇數)

可以同時觀察值在奇數/偶數的差異
  • 若該位置的行數 = (count + 1) / 2列數 = (count + 1) / 2 ,則代表該位置應填入 “🐦”。
  • 起始點是被視為矩陣中的中央點。在生成矩陣時,我們以 crossCenter 為中心點,並將該位置及其所在行和列視為十字形狀的一部分。
   // 十字 (奇數)
func crossPatteern() {

let count = Int(emojiSliderControl.value) // 取得滑桿的值,轉換為整數
countValueLabel.text = "\(count)" // 將 count 的值顯示在畫面上
var content = "" // 用於存儲生成的圖案內容

let crossCenter = (count + 1) / 2 // 計算出十字形狀中心所在的行數和列數。

for i in 1...count { // 遍歷行數

for j in 1...count { // 遍歷列數

if i == crossCenter || j == crossCenter { // 當行數等於十字形狀中心的行數,或者列數等於十字形狀中心的列數時
content += "🐦"
} else {
content += "🐤"
}
}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

// 將內容加到Label
displayEmojiLabel.text = content
}

(count + 1) / 2 是為了計算出十字形狀中心的行數和列數。

  • 假設 count 是奇數,例如 5,那麼 (count + 1) / 2 就等於 (5 + 1) / 2,結果為 3。這代表了十字形狀在矩陣中的中心位置應該是第 3 行和第 3 列,即矩陣的正中心。
// 當 count 為 5 時,矩陣的索引座標如下:

(1, 1) (1, 2) (1, 3) (1, 4) (1, 5)
(2, 1) (2, 2) (2, 3) (2, 4) (2, 5)
(3, 1) (3, 2) (3, 3) (3, 4) (3, 5) <-- 十字形狀中心位置(3,3)
(4, 1) (4, 2) (4, 3) (4, 4) (4, 5)
(5, 1) (5, 2) (5, 3) (5, 4) (5, 5)
  • 可以觀察到,在奇數的情況下, (count + 1) / 2 的結果會是中心位置的索引值,因此在程式中使用 crossCenter 來表示十字形狀的中心行數和列數。

如果count是偶數

  • 在偶數大小的矩陣中,無法找到一個確切的中心位置。

X交叉,中央點為不一樣的Emoji (count 是奇數)

  • 在這個矩陣中,中間的位置會顯示 “🐣”,交叉的位置會顯示 “🐦”,其他位置則會顯示 “🐤”。
  • 中心點是由 (count + 1) / 2 計算得出的,表示矩陣的中央行數和列數。當行數和列數都等於中心點時,在該位置放入 "🐣"。
  • 行數等於列數,或行數和列數之和等於矩陣邊長加1時,在該位置填入 "🐦"。其他位置則填入 "🐤"。
    // 畫 X,count 只能是奇數,交叉的地方顯示特別的圖案
func xCenterSharpPattern() {

let count = Int(emojiSliderControl.value) // 取得滑桿的值,轉換為整數
countValueLabel.text = "\(count)" // 將 count 的值顯示在畫面上
var content = "" // 用於存儲生成的圖案內容

let crossCenter = (count + 1) / 2 // 計算出十字形狀中心所在的行數和列數。

for i in 1...count {

for j in 1...count {

if i == crossCenter, j == crossCenter { // 當行數和列數都等於十字形狀的中心點時
content += "🐣"
} else if i == j || i + j == count + 1 { // 當行數等於列數 或 行數和列數之和等於矩陣邊長加1時
content += "🐦"
} else {
content += "🐤"
}
}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

// 將內容加到Label
displayEmojiLabel.text = content
}

  • 檢查當前的行數 i 和列數 j 是否在方框的邊界位置,即第一行、第一列、最後一行或最後一列。
  • 如果是在方框的邊界位置,則將 "🐦" 加入 content 字串中表示方框的邊界;否則,將 "🐤" 加入 content 字串中表示方框的內部。
    // 畫框框
func squarePattern() {

let count = Int(emojiSliderControl.value) // 取得滑桿的值,轉換為整數
countValueLabel.text = "\(count)" // 將 count 的值顯示在畫面上
var content = "" // 用於存儲生成的圖案內容

for i in 1...count {

for j in 1...count {

if i == 1 || i == count || j == 1 || j == count { // 方框的邊界位置
content += "🐦"
} else { // 方框的內部位置
content += "🐤"
}
}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

// 將內容加到Label
displayEmojiLabel.text = content
}

梅花座

  • 行+列 = 偶數,將 “🐦” 加入 content 字串中。表示 奇數行(奇數列),或是 偶數行 (偶數列)。
  • 行+列 = 奇數,將 "🐤" 加入 content 字串中。表示 偶數行(奇數列),或是 奇數行(偶數列)。
  • 也可簡化成行與列的和是否可以被偶數2整除。
    // 奇數行(奇數列變化🐦),偶數行(偶數列變化🐦)(行數與列數的和為偶數)
func alternatePattern() {

let count = Int(emojiSliderControl.value) // 轉整數
countValueLabel.text = "\(count)" // 當前數值顯示
var content = "" // 添加的內容

for i in 1...count { // 遍歷行數

for j in 1...count { // 遍歷列數

if (i+j) % 2 == 0 { // 判斷行數與列數的和是否為偶數
content += "🐦" // 當行數與列數的和為偶數時,將 "🐦" 加入內容中
} else {
content += "🐤" // 當行數與列數的和為奇數時,將 "🐤" 加入內容中
}

}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

// 將內容加到Label
displayEmojiLabel.text = content
}
  • 我原本的寫法…搞得很繞….XD,當初是針對🐦去思考的。
// 奇數行(奇數列變化🐦),偶數行(偶數列變化🐦)(梅花做另一個寫法)

for i in 1...count { // 遍歷行數

for j in 1...count { // 遍歷列數

if i % 2 == 1 && j % 2 == 1 { // 奇數行(奇數列變化🐦)
content += "🐦"
} else if i % 2 == 0 && j % 2 == 0 { // 偶數行(偶數列變化🐦)
content += "🐦"
} else {
content += "🐤"
}
}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

每排顯示不同圖案 (Array)

進行兩層迴圈:外層迴圈根據 count 的值遍歷每一行,內層迴圈根據 count 的值在每一行中重複添加表情符號。

  • 在每一行的開始,先計算出當前行所使用的表情符號的索引 emojiIndex。該索引是當前行的序號對表情符號陣列的長度取模得到的,這樣可以確保在遍歷完所有表情符號後,能循環地使用表情符號。
  • 根據 emojiIndex 從陣列 emojis 中取出對應的 emoji
  • 在內層迴圈中,重複添加 emojicontent 字符串中,每一行中的表情符號數量與 count 的值相等。
    // 每行顯示不同圖案
func differentRowPattern() {
let count = Int(emojiSliderControl.value) // 取得滑桿的值,轉換為整數
countValueLabel.text = "\(count)" // 將 count 的值顯示在畫面上
var content = "" // 添加的內容

let emojis = ["🐦", "🐧", "🐔", "🐤"] // 用於顯示的圖案陣列

for row in 0..<count {
let emojiIndex = row % emojis.count // 計算出目前 row 所使用的圖案索引,以達到循環選取的效果
let emoji = emojis[emojiIndex] // 根據索引從圖案陣列中取得對應的圖案

for _ in 0..<count {
content += emoji // 在目前 row 中重複添加對應的圖案
}
content += "\n" // 在每一行的結尾加入換行符號,用來分隔行數
}

// 將內容加到Label
displayEmojiLabel.text = content
}

補充:

當初在滑軌值與陣列索引間轉不過來,因為我的滑軌最小值為1,而索引是從0開始XD。

  • for row in 0..<count 中,範圍 0..<count 表示從 0 開始,但不包括 count 本身。
  • 這是因為索引通常是從 0 開始計算的,在這種情況下,我們想要遍歷的行數就是從 0 到 count-1,所以使用這種範圍來控制迴圈的執行次數。
  • 例子:如果滑桿的最大值為 9,則在此情況下,count 的值將為 9。而在 for row in 0..<count 的迴圈中,將會遍歷從 0 到 8 共計 9 個值,代表會有 9 個 row。
  • 這是因為範圍 0..<count 不包含最大值,所以迴圈會遍歷從 0 到 8 的範圍。因此,對於 count 的值為 9 的情況下,會有 9 個 row。

GitHub

參考

--

--

wei Tsao 學習紀錄
彼得潘的 Swift iOS / Flutter App 開發教室

Hi ! 我是wei , 先前未接觸過程式開發設計,想藉此來記錄自己的學習歷程,以利培養自己的程式邏輯 :)