关于C#:在编译时解决河内塔

Solving the Towers of Hanoi at compile-time

我正在尝试在编译时解决河内塔楼,但是我发现了一个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template<int src, int dst>
struct move_disc
{
    // member access will print src and dst
};

template<int n, int src, int tmp, int dst>
struct hanoi
{
    hanoi<n-1, src, dst, tmp> before;
    typename move_disc<src, dst>::lol disc;
    hanoi<n-1, tmp, src, dst> after;
};

template<int src, int tmp, int dst>
struct hanoi<0, src, tmp, dst>
{
    // recursive base case
};

hanoi<3, 1, 2, 3> go;

不幸的是,上述元程序仅打印了六个动作,而不是七个动作:

1
2
3
4
5
6
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<1, 3>
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<1, 2>
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<3, 2>
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<1, 3>
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<2, 1>
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<2, 3>

缺少从1到3的最后移动。这是为什么?问题可以解决吗?


我认为这是因为hanoi<1, 1, 2, 3>已经被实例化(产生第一个错误),并且在模板实例化过程中后来遇到" "时没有被再次实例化。

[编辑:为了使它更清晰,这是递归模板实例化的"图"(有错误):

  • hanoi<3, 1, 2, 3>

    • 1:hanoi<2, 1, 3, 2>

      • 1.1:hanoi<1, 1, 2, 3>

        • 1.1.1:hanoi<0, 1, 3, 2>
        • (move_disc<1, 3>)
        • 1.1.2:hanoi<0, 2, 1, 3>
      • (move_disc<1, 2>)
      • 1.2:hanoi<1, 3, 1, 2>

        • 1.2.1:hanoi<0, 3, 2, 1>
        • (move_disc<3, 2>)
        • 1.2.2:hanoi<0, 1, 3, 2>
    • (move_disc<1, 3>)
    • 2:hanoi<2, 2, 1, 3>

      • 2.1:hanoi<1, 2, 3, 1>

        • 2.1.1:hanoi<0, 2, 1, 3>
        • (move_disc<2, 1>)
        • 2.1.2:hanoi<0, 3, 2, 1>
      • (move_disc<2, 3>)
      • 2.2:hanoi<1, 1, 2, 3>:已在1.1实例化(move_disc<1, 3>错误不再重复)。

-结束编辑。]

我可以想到的一个" fix "是使每个专业化都是唯一的,例如通过添加" id "模板参数(并在递归实例化期间生成唯一的新值):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<int n, int src, int tmp, int dst, int id>
struct hanoi
{
    hanoi<n-1, src, dst, tmp, id*2> before;
    typename move_disc<src, dst>::lol disc;
    hanoi<n-1, tmp, src, dst, id*2+1> after;
};

template<int src, int tmp, int dst, int id>
struct hanoi<0, src, tmp, dst, id>
{
    // recursive base case
};

hanoi<3, 1, 2, 3, 1> go;

实时:http://ideone.com/0lQaXs


我没有直接回答您的问题,但这将是我的版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#include <type_traits>

using namespace std;

template <typename T> class TD;

struct NullType {};

template <int N, typename S>
struct Stack {
    static const int Head = N;
    typedef S Tail;
};

#define STACK_0() NullType
#define STACK_1(D1) Stack<D1, NullType>
#define STACK_2(D1, D2) Stack<D1, STACK_1(D2)>
#define STACK_3(D1, D2, D3) Stack<D1, STACK_2(D2, D3)>
#define STACK_4(D1, D2, D3, D4) Stack<D1, STACK_3(D2, D3, D4)>

template <typename S>
struct Pop;

template <int N, typename R>
struct Pop<Stack<N, R>> {
    typedef Stack<N, typename Pop<R>::result> result;
};

template<int N>
struct Pop<Stack<N, NullType>> {
    typedef NullType result;
};

template <typename S>
struct Top;

template <int N, typename R>
struct Top<Stack<N, R>> {
    static const int result = Top<R>::result;
};

template <int N>
struct Top<Stack<N, NullType>> {
    static const int result = N;
};

template <typename S, int D>
struct Push;

template <int N, typename S, int D>
struct Push<Stack<N, S>, D> {
    typedef Stack<N, typename Push<S, D>::result> result;
};

template <int M>
struct Push<NullType, M> {
    typedef Stack<M, NullType> result;
};

template <typename S>
struct SizeOf;

template <int N, typename R>
struct SizeOf<Stack<N, R>> {
    static const int value = SizeOf<R>::value + 1;
};

template <>
struct SizeOf<NullType> {
    static const int value = 0;
};

template <typename S1, typename S2, typename S3>
struct Hanoi {
    typedef S1 Stack1;
    typedef S2 Stack2;
    typedef S3 Stack3;

    template <int I, int J, int unused=0>
    struct MoveDisc;

    template <int unused>
    struct MoveDisc<1, 2, unused> {
        typedef Hanoi<
            typename Pop<Stack1>::result,
            typename Push<Stack2, Top<Stack1>::result>::result,
            Stack3
        > result;
    };

    template <int unused>
    struct MoveDisc<2, 1, unused> {
        typedef Hanoi<
            typename Push<Stack1, Top<Stack2>::result>::result,
            typename Pop<Stack2>::result,
            Stack3
        > result;
    };

    template <int unused>
    struct MoveDisc<1, 3, unused> {
        typedef Hanoi<
            typename Pop<Stack1>::result,
            Stack2,
            typename Push<Stack3, Top<Stack1>::result>::result
        > result;
    };

    template <int unused>
    struct MoveDisc<3, 1, unused> {
        typedef Hanoi<
            typename Push<Stack1, Top<Stack3>::result>::result,
            Stack2,
            typename Pop<Stack3>::result
        > result;
    };

    template <int unused>
    struct MoveDisc<2, 3, unused> {
        typedef Hanoi<
            Stack1,
            typename Pop<Stack2>::result,
            typename Push<Stack3, Top<Stack2>::result>::result
        > result;
    };

    template <int unused>
    struct MoveDisc<3, 2, unused> {
        typedef Hanoi<
            Stack1,
            typename Push<Stack2, Top<Stack3>::result>::result,
            typename Pop<Stack3>::result
        > result;
    };
};

static_assert(
    is_same<
        Pop<STACK_1(1)>::result,
        NullType
    >::value,"Pop<> not working."
);

static_assert(Top<STACK_2(2, 1)>::result == 1,"Top<> not working.");

static_assert(
    is_same<
        Push<STACK_1(2), 1>::result,
        STACK_2(2, 1)
    >::value,"Push<> not working."
);

static_assert(
    SizeOf<STACK_4(4, 3, 2, 1)>::value == 4,"SizeOf<> not working."
);

static_assert(
    is_same<
        typename Hanoi<
            STACK_4(4, 3, 2, 1),
            STACK_0(),
            STACK_0()>::MoveDisc<1,2>::result,
        Hanoi<
            STACK_3(4, 3, 2),
            STACK_1(1),
            STACK_0()>
    >::value,"MoveDisc<1,2> not working."
);

static_assert(
    is_same<
        typename Hanoi<
            STACK_3(4, 3, 2),
            STACK_1(1),
            STACK_0()>::MoveDisc<2,1>::result,
        Hanoi<
            STACK_4(4, 3, 2, 1),
            STACK_0(),
            STACK_0()>
    >::value,"MoveDisc<2,1> not working."
);

static_assert(
    is_same<
        typename Hanoi<
            STACK_1(4),
            STACK_0(),
            STACK_0()>::MoveDisc<1,3>::result,
        Hanoi<
            STACK_0(),
            STACK_0(),
            STACK_1(4)>
    >::value,"MoveDisc<1,3> not working."
);

static_assert(
    is_same<
        typename Hanoi<
            STACK_2(3, 2),
            STACK_0(),
            STACK_2(4, 1)>::MoveDisc<3,1>::result,
        Hanoi<
            STACK_3(3, 2, 1),
            STACK_0(),
            STACK_1(4)>
    >::value,"MoveDisc<3,1> not working."
);

static_assert(
    is_same<
        typename Hanoi<
            STACK_1(1),
            STACK_2(3, 2),
            STACK_1(4)>::MoveDisc<2,3>::result,
        Hanoi<
            STACK_1(1),
            STACK_1(3),
            STACK_2(4, 2)>
    >::value,"MoveDisc<2,3> not working."
);

template <typename H, int D, int F, int T, int V>
struct Solve {
    typedef typename Solve<
        typename Solve<H, D-1, F, V, T>::result::template MoveDisc<F, T>::result, D-1, V, T, F
    >::result result;
};

template <typename H, int F, int T, int V>
struct Solve<H, 1, F, T, V> {
    typedef typename H::template MoveDisc<F, T>::result result;
};

template <typename H>
struct Solution {
    typedef typename Solve<H, SizeOf<typename H::Stack1>::value, 1, 3, 2>::result result;
};

static_assert(
    is_same<
        Solution<
            Hanoi<
                STACK_4(4, 3, 2, 1),
                STACK_0(),
                STACK_0()
            >
        >::result,
        Hanoi<
            STACK_0(),
            STACK_0(),
            STACK_4(4, 3, 2, 1)
        >
    >::value,"Solution<> is not working."
);

int main() {

}

堆栈类模板实际上是Andrei Alexandrescu撰写的《 Modern C Design》一书中的TypeList \\


您的程序是正确的,但是您依赖于编译器错误消息(由实现定义),这种情况下它不会第二次显示错误。但是在实际代码中,TMP必须做一些导致运行时行为的事情。如果仅在move_disc构造函数中输出(带有std::cout)srcdst并使move_disc成为hanoi的成员,则强制执行编译时错误,则一切正常。