回答編集履歴

3

ソースコード追記

2021/10/26 20:36

投稿

xebme
xebme

スコア1083

test CHANGED
@@ -89,3 +89,175 @@
89
89
 
90
90
 
91
91
  つまり、初期値を最初に与えるか最後に与えるかによって、リストを左から作るか、右から作るかの違いが生じます。どちらの方法でも再帰処理できるようにしてください。
92
+
93
+
94
+
95
+ ---
96
+
97
+ **補足事項**
98
+
99
+
100
+
101
+ 最後にソースコードを追記します。Java 17(recordを使用)で動作します。併合処理を2種類の再帰で記述しました。
102
+
103
+
104
+
105
+ ```Java
106
+
107
+ import java.util.ArrayList;
108
+
109
+ import java.util.List;
110
+
111
+ import java.util.stream.Stream;
112
+
113
+
114
+
115
+ record Node(String name, Node left, Node right) {}
116
+
117
+
118
+
119
+ @FunctionalInterface
120
+
121
+ interface NodeUtils<U> {
122
+
123
+ List<List<U>> explode(Node source);
124
+
125
+ }
126
+
127
+
128
+
129
+ class NodeUtilsImpl1 implements NodeUtils<String> {
130
+
131
+
132
+
133
+ @Override
134
+
135
+ public List<List<String>> explode(Node source) {
136
+
137
+ if (source == null) return new ArrayList<>();
138
+
139
+ List<List<String>> result = merge(explode(source.left()),explode(source.right()));
140
+
141
+ result.add(0,List.of(source.name())); // 先頭に追加
142
+
143
+ return result;
144
+
145
+ }
146
+
147
+
148
+
149
+ private List<List<String>> merge(List<List<String>> left, List<List<String>> right) {
150
+
151
+ if (left.isEmpty() && right.isEmpty()) return new ArrayList<>();
152
+
153
+ List<String> temp = new ArrayList<>();
154
+
155
+ if (!left.isEmpty()) temp.addAll(left.remove(0));
156
+
157
+ if (!right.isEmpty()) temp.addAll(right.remove(0));
158
+
159
+ List<List<String>> result = merge(left,right);
160
+
161
+ result.add(0,temp); // 先頭に追加
162
+
163
+ return result;
164
+
165
+ }
166
+
167
+
168
+
169
+ }
170
+
171
+
172
+
173
+ class NodeUtilsImpl2 implements NodeUtils<String> {
174
+
175
+
176
+
177
+ @Override
178
+
179
+ public List<List<String>> explode(Node source) {
180
+
181
+ if (source == null) return new ArrayList<>();
182
+
183
+ List<List<String>> result = merge(explode(source.left()), explode(source.right()), new ArrayList<>());
184
+
185
+ result.add(0,List.of(source.name())); // 先頭に追加
186
+
187
+ return result;
188
+
189
+ }
190
+
191
+
192
+
193
+ private List<List<String>> merge(List<List<String>> left, List<List<String>> right, List<List<String>> acc) {
194
+
195
+ if (left.isEmpty() && right.isEmpty()) return acc;
196
+
197
+ List<String> temp = new ArrayList<>();
198
+
199
+ if (!left.isEmpty()) temp.addAll(left.remove(0));
200
+
201
+ if (!right.isEmpty()) temp.addAll(right.remove(0));
202
+
203
+ acc.add(temp); // 末尾に追加
204
+
205
+ return merge(left,right,acc);
206
+
207
+ }
208
+
209
+
210
+
211
+ }
212
+
213
+
214
+
215
+ public class q364810 {
216
+
217
+
218
+
219
+ public static void main(String[] args) {
220
+
221
+ Stream.of(new NodeUtilsImpl1(), new NodeUtilsImpl2()).forEach(q364810::test);
222
+
223
+ }
224
+
225
+
226
+
227
+ static void test(NodeUtils<String> utils) {
228
+
229
+
230
+
231
+ System.out.println();
232
+
233
+
234
+
235
+ Node n7 = new Node("7",null,new Node("8",null,null));
236
+
237
+ Node n2 = new Node("2",new Node("4",null,null),new Node("5",null,null));
238
+
239
+ Node n3 = new Node("3",new Node("6",null,null),n7);
240
+
241
+ Node n1 = new Node("1",n2,n3);
242
+
243
+
244
+
245
+ Stream.of(n1,n2,n3,n7).map(n -> utils.explode(n)).forEach(System.out::println);
246
+
247
+
248
+
249
+ }
250
+
251
+
252
+
253
+ }
254
+
255
+ ```
256
+
257
+
258
+
259
+ 空リスト(初期値)を引数にとる再帰処理は自然な末尾再帰を構成します。末尾再帰はループ構造に置き換えられる場合があります。Java以外の言語で最適化(再帰のループ化)を行うものがあります。
260
+
261
+
262
+
263
+ メソッドは再帰可能ですが、ラムダ式は自身で再帰できません。ラムダ式を使って再帰させるには2つのラムダ式を組み合わせるYコンビネータを定義します。(高難度)

2

畳み込みの方向を追記

2021/10/26 20:36

投稿

xebme
xebme

スコア1083

test CHANGED
@@ -75,3 +75,17 @@
75
75
  }
76
76
 
77
77
  ```
78
+
79
+
80
+
81
+ アキュムレータリストとは結果を格納する入れ物です。空リストは初期値を与えていることになります。引数に空リストを与えるのはNGでした。その場合は、最初の「展開」メソッドのように再帰の終了時に空リストを返します。両者の違いは次のようになります。
82
+
83
+
84
+
85
+ - 再帰メソッドの引数に空リスト(初期値)を与えると、リストの末尾にむかって要素を追加する
86
+
87
+ - 再帰メソッドの終了時に空リスト(初期値)を返すと、リストの先頭に向かって要素を追加する
88
+
89
+
90
+
91
+ つまり、初期値を最初に与えるか最後に与えるかによって、リストを左から作るか、右から作るかの違いが生じます。どちらの方法でも再帰処理できるようにしてください。

1

併合処理について

2021/10/24 05:40

投稿

xebme
xebme

スコア1083

test CHANGED
@@ -33,3 +33,45 @@
33
33
  }
34
34
 
35
35
  ```
36
+
37
+
38
+
39
+ ---
40
+
41
+ **併合処理も再帰で行うなら**
42
+
43
+
44
+
45
+ 併合処理が難しいと思うなら再帰を使ってみます。アキュムレータリストを引数に追加します。アキュムレータリスト引数は空リストを渡します。
46
+
47
+
48
+
49
+ ```Java
50
+
51
+ static リスト 併合処理( 左リスト, 右リスト, アキュムレータリスト) {
52
+
53
+
54
+
55
+ if ( 左リストが空 && 右リストが空) return アキュムレータリスト;
56
+
57
+
58
+
59
+ 作業リスト = new 空リスト;
60
+
61
+ if ( ! 左リストが空) 左リストの先頭要素を削除; 内容を作業リストに追加;
62
+
63
+ if ( ! 右リストが空) 右リストの先頭要素を削除; 内容を作業リストに追加;
64
+
65
+
66
+
67
+ アキュムレータリスト.add(作業リスト);
68
+
69
+
70
+
71
+ return 併合処理( 左リスト, 右リスト, アキュムレータリスト) ;
72
+
73
+
74
+
75
+ }
76
+
77
+ ```