1つずつ回答します。
huskiesさんの理解について
例えば、まずif tree.Left != nil
を通してnodeを1まで進め(リーフノード)、そのループ時はnilとなるのでappend
される
不明瞭なので、書き直すと下記の通りです。
if tree.Left != nil
を通して、10, 5, 2のノードにおいてtree.Left.InOrderTraverse(array)
が呼び出され、最終的に1のノードでtree.Left
がnilになるので、array = append(array, tree.Value)
が実行される。ここで重要なのは、10, 5, 2のノードでの呼び出しはリターンされず、スタックに積まれたままである(実行中)ということです。そのため、1のノードの実行が終了してもその後2のノードが引き続き実行されます。
したがって、その後の記述で、
関数が終了してしまうのでは
と書かれていますが、1のノードに生えているメソッドInOrderTraverseについて、この理解は正しいです。
(本質ではないですが、InOrderTraverseはレシーバに生えているので関数ではなくメソッドです。)
huskiesさんの仮説について
1のリーフノードに届くまで3回再帰しています。例えば初回の再帰が呼ばれると、ノード自体は5に移動すると思いますが、この時点では tree.Left != nil のif文に入るため、appendまでは行かず、一度ここで止まり(?)処理待ちの状態となる
正しいです。
そして, 1に到達すると、if文には入らないため、append, 次のif分を処理し、一度return(?)する
正しいです。
その後、処理が止まっていた再帰の処理が新しい順から始まる
「新しい順から始まる」という言葉を明確にすると、「2のノードのarray = tree.Left.InOrderTraverse(array)
の代入が実行される」です。
huskiesさんの質問について
下記の加算結果を代入するコードが実行される時を考えます。
go
1func main() {
2 sum := 1 + 2
3 fmt.Println(sum)
4}
詳細に書くと長くなるので、概要だけ掻い摘むと
1 + 2
が評価される
1 + 2
の評価した結果をsumに代入する
という2つの過程に分けられます。
ここで、1 + 2
の評価が終わるまで代入が為されないことが分かると思います。
次に、下記の関数呼び出し結果を代入するコードが実行される時を考えます。
go
1func One() int {
2 return 1
3}
4
5func main() {
6 result := One()
7 fmt.Println(result)
8}
この場合も同様に、
One()
が評価される
One()
を評価した結果をresultに代入する
という2つの過程に分けられます。
ここで、One()
の評価が終わるまで代入が為されないことが分かります。
これをInOrderTraverse(array)
に置き換えて考えてください。
もう説明は要らないでしょう。
補足
実際に実行される順番を見るために、Playgroundのコードを開いて、Runして得られた結果を見てください。すると、呼び出される順番が分かります。
このように、実行順序を知るために各行で結果を出力するのは良く使う手法です。他には、gdbなどのデバッガで処理手順を知るのも1つの手段だと思います。詳しくはググってください。
一応ソースコードと結果を載せておきます。
go
1package main
2
3import "fmt"
4
5type BSTNode struct {
6 Value int
7
8 Left *BSTNode
9 Right *BSTNode
10}
11
12func (tree *BSTNode) Traverse(array []int) []int {
13 fmt.Printf("[Called ] array: %v, now: %d\n", array, tree.Value)
14 // 左のノードがあれば、そちらのノードで更にトラバースする
15 if tree.Left != nil {
16 fmt.Printf("[CallLeft ] array: %v, now: %d\n", array, tree.Value)
17 array = tree.Left.Traverse(array)
18 fmt.Printf("[CalledLeft ] array: %v, now: %d\n", array, tree.Value)
19 }
20
21 // arrayに今見ているノードの値を追加する
22 array = append(array, tree.Value)
23 // 右にノードがあれば、そちらのノードで更にトラバースする
24 if tree.Right != nil {
25 fmt.Printf("[CallRight ] array: %v, now: %d\n", array, tree.Value)
26 array = tree.Right.Traverse(array)
27 fmt.Printf("[CalledRight] array: %v, now: %d\n", array, tree.Value)
28 }
29 fmt.Printf("[Return ] array: %v, now: %d\n", array, tree.Value)
30 // トラバースした結果を返す
31 return array
32}
33
34// ※バックスラッシュが出せなかったので、\で代用しています
35// 10
36// / \
37// 5 15
38// / \ \
39// 2 6 22
40// /
41// 1
42
43func main() {
44 // ノードを作る
45 node1 := &BSTNode{Value: 1, Left: nil, Right: nil}
46 node2 := &BSTNode{Value: 2, Left: node1, Right: nil} // node1というノードを左の子として保持している(以下略)
47 node6 := &BSTNode{Value: 6, Left: nil, Right: nil}
48 node5 := &BSTNode{Value: 5, Left: node2, Right: node6}
49 node22 := &BSTNode{Value: 22, Left: nil, Right: nil}
50 node15 := &BSTNode{Value: 15, Left: nil, Right: node22}
51 node10 := &BSTNode{Value: 10, Left: node5, Right: node15}
52
53 // 根からトラバースし始める
54 fmt.Println(node10.Traverse([]int{}))
55
56 // BITの格納順序が壊れていた場合は、最小値~最大値に出力されない
57 // この場合は5と6を反転させている
58 node5 = &BSTNode{Value: 5, Left: nil, Right: nil}
59 node6 = &BSTNode{Value: 6, Left: node2, Right: node5}
60 node10 = &BSTNode{Value: 10, Left: node6, Right: node15}
61 // 根からトラバースし始める
62 fmt.Println(node10.Traverse([]int{}))
63
64}
65
66
67// Output:
68// [Called ] array: [], now: 10
69// [CallLeft ] array: [], now: 10
70// [Called ] array: [], now: 5
71// [CallLeft ] array: [], now: 5
72// [Called ] array: [], now: 2
73// [CallLeft ] array: [], now: 2
74// [Called ] array: [], now: 1
75// [Return ] array: [1], now: 1
76// [CalledLeft ] array: [1], now: 2
77// [Return ] array: [1 2], now: 2
78// [CalledLeft ] array: [1 2], now: 5
79// [CallRight ] array: [1 2 5], now: 5
80// [Called ] array: [1 2 5], now: 6
81// [Return ] array: [1 2 5 6], now: 6
82// [CalledRight] array: [1 2 5 6], now: 5
83// [Return ] array: [1 2 5 6], now: 5
84// [CalledLeft ] array: [1 2 5 6], now: 10
85// [CallRight ] array: [1 2 5 6 10], now: 10
86// [Called ] array: [1 2 5 6 10], now: 15
87// [CallRight ] array: [1 2 5 6 10 15], now: 15
88// [Called ] array: [1 2 5 6 10 15], now: 22
89// [Return ] array: [1 2 5 6 10 15 22], now: 22
90// [CalledRight] array: [1 2 5 6 10 15 22], now: 15
91// [Return ] array: [1 2 5 6 10 15 22], now: 15
92// [CalledRight] array: [1 2 5 6 10 15 22], now: 10
93// [Return ] array: [1 2 5 6 10 15 22], now: 10
94[1 2 5 6 10 15 22]
95// [Called ] array: [], now: 10
96// [CallLeft ] array: [], now: 10
97// [Called ] array: [], now: 6
98// [CallLeft ] array: [], now: 6
99// [Called ] array: [], now: 2
100// [CallLeft ] array: [], now: 2
101// [Called ] array: [], now: 1
102// [Return ] array: [1], now: 1
103// [CalledLeft ] array: [1], now: 2
104// [Return ] array: [1 2], now: 2
105// [CalledLeft ] array: [1 2], now: 6
106// [CallRight ] array: [1 2 6], now: 6
107// [Called ] array: [1 2 6], now: 5
108// [Return ] array: [1 2 6 5], now: 5
109// [CalledRight] array: [1 2 6 5], now: 6
110// [Return ] array: [1 2 6 5], now: 6
111// [CalledLeft ] array: [1 2 6 5], now: 10
112// [CallRight ] array: [1 2 6 5 10], now: 10
113// [Called ] array: [1 2 6 5 10], now: 15
114// [CallRight ] array: [1 2 6 5 10 15], now: 15
115// [Called ] array: [1 2 6 5 10 15], now: 22
116// [Return ] array: [1 2 6 5 10 15 22], now: 22
117// [CalledRight] array: [1 2 6 5 10 15 22], now: 15
118// [Return ] array: [1 2 6 5 10 15 22], now: 15
119// [CalledRight] array: [1 2 6 5 10 15 22], now: 10
120// [Return ] array: [1 2 6 5 10 15 22], now: 10
121// [1 2 6 5 10 15 22]
要点だけを話したので、更に詳しく知りたい場合は「関数 呼び出し 仕組み」や「再帰 スタックメモリ」などで調べると良いと思います。ここら辺はコンピュータサイエンスの分野になるので、「実行できれば良い」というスタンスなら別に知る必要はないと思います。
長くなりましたが、プログラムが実行される時にどのように実行されるかを理解するのは非常に重要で為になります。引き続き頑張ってください。