C47. 方形 — nested loop(巢狀迴圈) 練習
練習題目
根本益智遊戲啊XD
練習
for與in之間何時用 _ ,什麼時候是要給一個名稱呢?
for-in
迴圈可以用來迭代一個集合的所有元素。你可以使用一個特定的變數名稱來存儲每次迭代中的元素值,或者使用_
來忽略該值。
- 使用一個名稱:當你需要在迴圈體中使用每次迭代的元素值時,應該給出一個名稱。例如:
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
次。因此,我們使用_
來忽略每一個元素值。
總的來說,選擇使用_
還是一個名稱取決於你是否需要在迴圈體中使用每次迭代的元素值。
判斷使用場景
決定使用元素值名稱還是_
主要取決於你是否需要在迴圈中使用每次迭代的元素值。以下是一些具體的場景來說明這個概念:
- 使用元素值名稱的場景:
- 計算總和:如果你有一個數字陣列並且你想計算其總和,你需要在每次迴圈中訪問每個數字,因此你應該使用元素值名稱。
- 操作元素:如果你要對陣列的每個元素進行某種操作(例如 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
。 - 在內層迴圈中,重複添加
emoji
到content
字符串中,每一行中的表情符號數量與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
參考