未知深度的巢狀迴圈
引入: 自定義
有一個小孩子,她能夠把吃下去的東西消化一部分,另一部分吐回來,完好如初。有一天,她想要知道心臟長什麼樣子,但是沒有人願意告訴她。她知道自己也有心臟,於是,她把自己吃下去,吐回來自己缺了右腿,她高興了,深信如此重複必有一次能夠看到自己可望了解的心臟──即使多少次是未知。她缺了左手,然後她缺了左腿,接著她缺了小腸,最後,終於吐出裸露的心臟,她笑了──失去下顎的上唇沒有顫抖,失去左鄰的右眼沒有淚,失去外衣的軟腦內沒有痛楚,失去肉體的靈魂沒有遺憾。
巢狀迴圈指的是多個以內外關係存在的迴圈交織作用,這經常用在多個變數求所有變化與結果。以下程式實現輸入三個數值,把1 到該數且三者串連的所有可能列出。
]Function test(ByVal test1 As Integer, ByVal test2 As Integer, ByVal test3 As Integer)
]For i1 As Integer = 1 To test1 Step 1
]For i2 As Integer = 1 To test2 Step 1
]For i3 As Integer = 1 To test3 Step 1
]MsgBox(i1 & ", " & i2 & ", " & i3)
]Next i3
]Next i2
]Next i1
]End Function
]
]test(1, 2, 2) //MsgBox 依序顯示"1, 1, 1"、"1, 1, 2"、"1, 2, 1"、"1, 2, 2"。
初學者經常使用上面這種定型巢狀迴圈,以上面為例,最多帶三個變數,想要第四個變數一起參與,沒門。
這是一個對巢狀迴圈的誤解。確實在上例,這些For 語法組織成就如同鳥巢一般(如果經過縮排美化),簡單易懂。但是這種定型巢狀迴圈不是萬能,真正的巢狀迴圈多半如同文章開頭那個故事裡的小女孩──那可不是閑閑沒事寫來嚇人的──把從自己身上吐出來的東西再分解一次。
這通常使用到一個大迴圈,用來監看應該使用幾層,在每一層之內,把自己的參數處理後送入自己體內。
]Function test(ByVal testInput As Array, Optional ByVal testKnow As Array = Nothing)
]If (UBound(testInput) - LBound(testInput) + 1 = 1) Then
]For i1 As Integer = 1 To testInput(LBound(testInput)) Step 1
]MsgBox(Reline(testKnow, ", ") & ", " & i1)
]Next
]Else
]For i1Number As Integer = 1 To testInput(LBound(testInput)) Step 1
]test(ResSteal(testInput, LBound(testInput)), ResPush(testKnow, i1Number))
]Next
]End If
]End Function
]
]test(New Integer() {1, 2, 2}) //MsgBox 依序顯示"1, 1, 1"、"1, 2, 2"、"1, 2, 1"、"1, 2, 2"。
]test(New Integer() {1, 2, 2, 2}) //MsgBox 依序顯示"1, 1, 1, 1"、"1, 1, 1, 2"、"1, 1, 2, 1"、"1, 1, 2, 2"、"1, 2, 1, 1"、"1, 2, 1, 2"、"1, 2, 2, 1"、"1, 2, 2, 2"。
如果巢狀迴圈總是被定型,就會處處被限制,小女孩只會失手失腳,在看到心臟前抑鬱而終。喜劇就因為定型巢狀迴圈的限制而降華為悲劇。
也許妳注意到了,一個不定型巢狀迴圈通常寫成一個函式,這樣才能不斷吞吐自己於自己,並且在讀入參數時判斷參數的數量,如果到了最後一到防線,那麼就執行主要程序。如果還沒,則切出某一個,並把其他的再吞回自己之中處理。在這樣的結構中,通常出現ResSteal 函式來刪除陣列中的元素。
如果想到無限巢狀迴圈(也就是不定型巢狀迴圈),那麼肯定想到文字排列。在數學中,給予一組成員,則能夠在成員不重複的條件下將該些成員排成多種順序。比如給予"ABC" 便能夠回傳"ABC"、"ACB"、"BAC"、"BCA"、"CAB"、"CBA"。
]Function Permutation(ByVal Elements As Array) As Array
]If (Elements Is Nothing) Then
]Return Nothing
]End If
]If (UBound(Elements) - LBound(Elements) + 1 < 2) Then
]Return Elements(LBound(Elements))
]End If
]Dim Result As Array = Nothing
]If (UBound(Elements) - LBound(Elements) + 1 = 2) Then
]Dim First As Array = Array.CreateInstance(ArrayType(Elements), 2)
]First(LBound(First)) = Elements(LBound(Elements))
]First(UBound(First)) = Elements(UBound(Elements))
]Dim Second As Array = Array.CreateInstance(ArrayType(Elements), 2)
]Second(LBound(Second)) = Elements(UBound(Elements))
]Second(UBound(Second)) = Elements(LBound(Elements))
]Result = Array.CreateInstance(Elements.GetType, 2)
]Result(LBound(Result)) = First
]Result(UBound(Result)) = Second
]Else
]For i1Elements As Integer = LBound(Elements) To UBound(Elements) Step 1
]Dim ThePermutation As Array = Permutation(ResSteal(Elements, i1Elements))
]For i1Permutation As Integer = LBound(ThePermutation) To UBound(ThePermutation) Step 1
]ThePermutation(i1Permutation) = ResPush(ThePermutation(i1Permutation), Elements(i1Elements), 0)
]Next
]Result = ResHang(Result, ThePermutation)
]Next
]End If
]Return Result
]End Function
]
]Dim test As Array = Permutation(New String() {"A", "B", "C"})
]For i1 As Integer = LBound(test) To UBound(test) Step 1
]MsgBox(Reline(test(i1), Nothing))
]Next
