是否可以组合对称代码段?
Is it possible combining symmetric code pieces?
我正在为我的学校项目实现 avl 树,发现自己为对称情况编写了两次几乎相同的代码。例如,此函数执行两个节点的旋转以平衡树。 if 子句处理低节点是高节点的左子节点的情况,else 子句处理相反的情况:
void avl<T>::rotate(node<T> *x, node<T> *y)
{
if (x == y->l)
{
x->p = y->p;
if (y->p != nullptr)
if(y->p->l == y)
y->p->l = x;
else
y->p->r = x;
else
this->setHead(x);
y->p = x;
y->l = x->r;
if(x->r != nullptr)
x->r->p = y;
x->r = y;
y->dl = y->calcd('l');
x->dr = x->calcd('r');
if(x->p != nullptr)
if(x->p->l == x)
x->p->dl = x->p->calcd('l');
else
x->p->dr = x->p->calcd('r');
}
else
{
x->p = y->p;
if (y->p != nullptr)
if(y->p->r == y)
y->p->r = x;
else
y->p->l = x;
else
this->setHead(x);
y->p = x;
y->r = x->l;
if(x->l != nullptr)
x->l->p = y;
x->l = y;
y->dl = y->calcd('l');
x->dr = x->calcd('r');
if(x->p != nullptr)
if(x->p->r == x)
x->p->dr = x->p->calcd('r');
else
x->p->dl = x->p->calcd('l');
}
}
如您所见,else 子句与 if 子句完全相似,但交换了 'l' 和 'r'。有没有办法将它们结合起来。我可以做些什么来改进它?我的代码中是否存在设计错误?
All problems in computer science can be solved by another level of
indirection, except of course for the problem of too many
indirections.
(David Wheeler)
你可以这样做(完全未经测试):
node<T>** y_sibling_1 = x == y->l ? &y->p->l : &y->p->r;
node<T>** y_sibling_2 = x == y->l ? &y->p->r : &y->p->l;
node<T>** x_child = x == y->l ? &x->r : &x->l;
node<T>** y_child = x == y->l ? &y->l : &y->r;
x->p = y->p;
if (y->p != nullptr)
if(*y_sibling_1 == y)
*y_sibling_1 = x;
else
*y_sibling_2 = x;
else
this->setHead(x);
y->p = x;
*y_child = *x_child;
if(*x_child != nullptr)
(*x_child)->p = y;
*x_child = y;
y->dl = y->calcd('l');
x->dr = x->calcd('r');
if(x->p != nullptr)
if(x->p->l == x)
x->p->dl = x->p->calcd('l');
else
x->p->dr = x->p->calcd('r');
(请注意,您的最终条件在这两种情况下是等效的。)
这个是不是绕太多了,个人看法问题。
您可能需要此处的模板,因此您需要 calcd<true>()
和 calcd<false>()
。
有了这个改变,你可以写
template<bool xChildy>
void avl<T>::rotateImpl(node<T> *x, node<T> *y);
void avl<T>::rotate(node<T> *x, node<T> *y)
{
if (x == y->l) {
rotateImpl<true>(x,y);
}
else {
rotateImpl<false>(x,y);
}
}
使用指向成员的指针。两个分支之间的唯一区别是您访问的是哪个成员,因此这是抽象出来的简单方法:
using Child = node<T> node<T>::*;
void rotate_impl(node<T>* x, node<T>* y, Child* left, Child* right)
{
x->p = y->p;
if (y->p != nullptr) {
if(y->p->*left == y) {
y->p->*left = x;
}
else {
y->p->*right = x;
}
}
else {
this->setHead(x);
}
y->p = x;
y->*left = x->*right;
if(x->*right != nullptr) {
(x->*right)->p = y;
}
x->*right = y;
y->dl = y->calcd('l');
x->dr = x->calcd('r');
if(x->p != nullptr) {
if(x->p->*left == x) {
x->p->dl = x->p->calcd('l');
}
else {
x->p->dr = x->p->calcd('r');
}
}
}
void avl<T>::rotate(node<T> *x, node<T> *y)
{
if (x == y->l) {
rotate_impl(x, y, &node<T>::l, &node<T>::r);
}
else {
rotate_impl(x, y, &node<T>::r, &node<T>::l);
}
}
我还冒昧地在您的代码中添加了大括号。你可以稍后再感谢我。
我喜欢 Mike 的模板方法。但为了记录,我在这里提出另一种选择。
首先,考虑每个节点有两个children。称他们为"left"和"right"只是一种心态。您也可以将它们放在一个数组中:
template <class T>
class node {
public:
...
enum side{ left,right,last}; // just to make clear my intent here
node *p, *c[last], *d[last]; // no more l and r !!
node* calcd(enum side x) {} // lets use our index here instead of char
...
};
然后你可以分解出依赖于边的代码。我喜欢 lambda,所以这是第一个提议:
template <class T>
void avl<T>::rotate(node<T> *x, node<T> *y)
{
// this lambda works directly with locals of rotate() thanks to [&]
// so put in the side dependent code, and put the side as parameter
// for easy switch. For simplicity I used l and r to facilitate
// transposition, but reafctoring it in a neutral side1 and sinde2
// could help readbility
auto rt = [&](enum node<T>::side l, enum node<T>::side r)->void {
x->p = y->p;
if (y->p != nullptr)
if(y->p->c[l] == y) // l is a parameter here instead of member name
y->p->c[l] = x;
else
y->p->c[r] = x;
else
this->setHead(x);
y->p = x;
y->c[l] = x->c[r];
if (x == y->c[l])
{
if(x->c[r] != nullptr)
x->c[r]->p = y;
x->c[r] = y;
y->d[l] = y->calcd(sd[l]);
x->d[r] = x->calcd(sd[r]);
if(x->p != nullptr)
if(x->p->c[l] == x)
x->p->d[l] = x->p->calcd(sd[l]);
else
x->p->d[r] = x->p->calcd(sd[r]);
}
}; // end of the definition of the lambda
// the code of the function is then reduced to this:
if (x == y->c[node<T>::left])
rt(node<T>::left,node<T>::right);
else
rt(node<T>::right, node<T>::left);
}
我正在为我的学校项目实现 avl 树,发现自己为对称情况编写了两次几乎相同的代码。例如,此函数执行两个节点的旋转以平衡树。 if 子句处理低节点是高节点的左子节点的情况,else 子句处理相反的情况:
void avl<T>::rotate(node<T> *x, node<T> *y)
{
if (x == y->l)
{
x->p = y->p;
if (y->p != nullptr)
if(y->p->l == y)
y->p->l = x;
else
y->p->r = x;
else
this->setHead(x);
y->p = x;
y->l = x->r;
if(x->r != nullptr)
x->r->p = y;
x->r = y;
y->dl = y->calcd('l');
x->dr = x->calcd('r');
if(x->p != nullptr)
if(x->p->l == x)
x->p->dl = x->p->calcd('l');
else
x->p->dr = x->p->calcd('r');
}
else
{
x->p = y->p;
if (y->p != nullptr)
if(y->p->r == y)
y->p->r = x;
else
y->p->l = x;
else
this->setHead(x);
y->p = x;
y->r = x->l;
if(x->l != nullptr)
x->l->p = y;
x->l = y;
y->dl = y->calcd('l');
x->dr = x->calcd('r');
if(x->p != nullptr)
if(x->p->r == x)
x->p->dr = x->p->calcd('r');
else
x->p->dl = x->p->calcd('l');
}
}
如您所见,else 子句与 if 子句完全相似,但交换了 'l' 和 'r'。有没有办法将它们结合起来。我可以做些什么来改进它?我的代码中是否存在设计错误?
All problems in computer science can be solved by another level of indirection, except of course for the problem of too many indirections.
(David Wheeler)
你可以这样做(完全未经测试):
node<T>** y_sibling_1 = x == y->l ? &y->p->l : &y->p->r;
node<T>** y_sibling_2 = x == y->l ? &y->p->r : &y->p->l;
node<T>** x_child = x == y->l ? &x->r : &x->l;
node<T>** y_child = x == y->l ? &y->l : &y->r;
x->p = y->p;
if (y->p != nullptr)
if(*y_sibling_1 == y)
*y_sibling_1 = x;
else
*y_sibling_2 = x;
else
this->setHead(x);
y->p = x;
*y_child = *x_child;
if(*x_child != nullptr)
(*x_child)->p = y;
*x_child = y;
y->dl = y->calcd('l');
x->dr = x->calcd('r');
if(x->p != nullptr)
if(x->p->l == x)
x->p->dl = x->p->calcd('l');
else
x->p->dr = x->p->calcd('r');
(请注意,您的最终条件在这两种情况下是等效的。)
这个是不是绕太多了,个人看法问题。
您可能需要此处的模板,因此您需要 calcd<true>()
和 calcd<false>()
。
有了这个改变,你可以写
template<bool xChildy>
void avl<T>::rotateImpl(node<T> *x, node<T> *y);
void avl<T>::rotate(node<T> *x, node<T> *y)
{
if (x == y->l) {
rotateImpl<true>(x,y);
}
else {
rotateImpl<false>(x,y);
}
}
使用指向成员的指针。两个分支之间的唯一区别是您访问的是哪个成员,因此这是抽象出来的简单方法:
using Child = node<T> node<T>::*;
void rotate_impl(node<T>* x, node<T>* y, Child* left, Child* right)
{
x->p = y->p;
if (y->p != nullptr) {
if(y->p->*left == y) {
y->p->*left = x;
}
else {
y->p->*right = x;
}
}
else {
this->setHead(x);
}
y->p = x;
y->*left = x->*right;
if(x->*right != nullptr) {
(x->*right)->p = y;
}
x->*right = y;
y->dl = y->calcd('l');
x->dr = x->calcd('r');
if(x->p != nullptr) {
if(x->p->*left == x) {
x->p->dl = x->p->calcd('l');
}
else {
x->p->dr = x->p->calcd('r');
}
}
}
void avl<T>::rotate(node<T> *x, node<T> *y)
{
if (x == y->l) {
rotate_impl(x, y, &node<T>::l, &node<T>::r);
}
else {
rotate_impl(x, y, &node<T>::r, &node<T>::l);
}
}
我还冒昧地在您的代码中添加了大括号。你可以稍后再感谢我。
我喜欢 Mike 的模板方法。但为了记录,我在这里提出另一种选择。
首先,考虑每个节点有两个children。称他们为"left"和"right"只是一种心态。您也可以将它们放在一个数组中:
template <class T>
class node {
public:
...
enum side{ left,right,last}; // just to make clear my intent here
node *p, *c[last], *d[last]; // no more l and r !!
node* calcd(enum side x) {} // lets use our index here instead of char
...
};
然后你可以分解出依赖于边的代码。我喜欢 lambda,所以这是第一个提议:
template <class T>
void avl<T>::rotate(node<T> *x, node<T> *y)
{
// this lambda works directly with locals of rotate() thanks to [&]
// so put in the side dependent code, and put the side as parameter
// for easy switch. For simplicity I used l and r to facilitate
// transposition, but reafctoring it in a neutral side1 and sinde2
// could help readbility
auto rt = [&](enum node<T>::side l, enum node<T>::side r)->void {
x->p = y->p;
if (y->p != nullptr)
if(y->p->c[l] == y) // l is a parameter here instead of member name
y->p->c[l] = x;
else
y->p->c[r] = x;
else
this->setHead(x);
y->p = x;
y->c[l] = x->c[r];
if (x == y->c[l])
{
if(x->c[r] != nullptr)
x->c[r]->p = y;
x->c[r] = y;
y->d[l] = y->calcd(sd[l]);
x->d[r] = x->calcd(sd[r]);
if(x->p != nullptr)
if(x->p->c[l] == x)
x->p->d[l] = x->p->calcd(sd[l]);
else
x->p->d[r] = x->p->calcd(sd[r]);
}
}; // end of the definition of the lambda
// the code of the function is then reduced to this:
if (x == y->c[node<T>::left])
rt(node<T>::left,node<T>::right);
else
rt(node<T>::right, node<T>::left);
}