2017-09-02 37 views
-1

摇排序向量: 程序工作,但是:摇排序使用MACRO

我试图用泡起来,泡相同功能下的抖动排序(冒泡MAX值获得正确的并向下冒泡以获得最小值到左侧)。为了做到这一点,我尝试使用下面的宏不编译:

标志是“+”和Oper是被“>”泡

符号“ - ”和Oper是被“<”气泡向下

对于冒泡 -

开始是迭代器I(迭代向量索引)

端为n-1-i的;

泡下来 - 交换起始和结束值

#define bubble_up_down(var_t, pVector, _Is_swp, start, end, sign, oper)\ 
{\ 
    var_t current_index;\ 
    var_t current_val;\ 
    var_t next_val;\    
    for (current_index = *(start) ; current_index (oper) *(end) ; (sign)(sign)current_index){\ 
    {\ 
     VectorGet((pVector), current_index, &current_val);\ 
     VectorGet((pVector), current_index(sign)1, &next_val);\ 
     if(current_val (oper) next_val)\ 
     {\ 
      VectorSet((pVector), current_index, next_val);\ 
      VectorSet((pVector), current_index(sign)1, current_val);\ 
      *(_Is_swp) = 1;\ 
     }\ 
    }\ 
} 

需要你的意见来解决这个宏。

+1

有什么错误? – leyanpan

+0

开发更长的代码,因为宏是邪恶的。顺便说一下,你的思维方式在C++模板中有非常好的视角 –

+0

在这里使用宏有什么意义?在执行向上/向下的气泡排序和摇动排序时,您是否只想替换冗余代码?如果'Get' /'SetValue'显然不是类型无关的,那类型是什么?你如何调用你的宏? –

回答

2

这不是很清楚为什么你想在这里使用宏。你想避免重复代码?还是你想让你的排序例程类型独立?

总之,您的宏有几个误区:

  • 你可能阅读,你应该警惕宏参数用括号。这通常是很好的建议,因为宏是文本替换;例如臭名昭​​着的SQ(x + 1)将解析为x + 1*x + 1。就你而言,建议是错误的。您的代码中会出现句法错误的“运算符”,例如(-)(<)。只需使用signoper即可。
  • 即便如此,sign sign将解析为- -+ +,这不是你想要的。您可以将i++重写为同样有效的i = i + 1,或者您可以使用令牌粘贴操作符sign##sign,这会生成--++
  • 宏不是函数。你可能会在一个函数内调用你的宏。你调用宏的范围内的所有局部变量也在宏的范围内。这意味着可能不需要定义所有这些指针。
  • 为什么要传递数组元素类型var_t?我认为SetVectorGetVector不是宏,所以类型独立性下降。
  • 如果var_t是数组元素的类型,那么您的索引不一定是相同的类型;它应该是一个整数类型。 (您的元素必须与<运算符相媲美,所以它是算术类型之一,但是如果您有一个长度超过256个元素的char阵列,会发生什么情况?)
  • 如果您的元素是算术类型,可能不需要GetValueSetValue调用。您可以使用=运算符分配值。

这一切都让我觉得你并不知道自己在做什么。这加上已知的宏陷阱和缺点是一个很好的理由这里不使用任何宏


附录  在评论中,PO曾表示,宏应该做到两点:应该避免重复的代码,它应该使分拣独立类型的数组元素组成。这是两件不同的事情。

编写短的本地宏以避免重复代码可能是一种有用的技术,特别是如果代码需要保持变量在几个地方同步。你的情况有用吗?

所以,你有你的向上冒泡代码:

int done = 0; 

while (!done) { 
    done = 1; 

    for (int i = 1; i < n; i++) { 
     if (a[i - 1] > a[i]) { 
      swap(a, i - 1, i); 
      done = 0; 
     } 
    } 
} 

(这将使用swap函数交换两个数组元素它比你的版本的更多直接的,因为它不使用get/。set访问功能)现在你写的向下冒泡对应:

while (!done) { 
    done = 1; 

    for (int i = n - 1; i > 0; i--) { 
     if (a[i - 1] > a[i]) { 
      swap(a, i - 1, i); 
      done = 0; 
     } 
    } 
} 

这两个片段在闭环控制而不同。都访问从1到n - 1的所有指数。所以你的宏需要传递开始和结束值。但是它也需要知道比较结果–小于或大于–以及是否递增或递减索引。这是四个简单循环的数据。

您可以尝试摆脱比较并在两个方向上使用!=。但是如果数组为空,那么你的循环将失败。

当您使用无符号整数作为索引时,上面的向后循环将在空数组上失败。正向和反向的lops与C不对称,因为下限和上限也是不对称的:下限总是包含性的,上限总是排他性的。这种正向循环:

for (unsigned int i = 0; i < n; i++) ... 

有以下落后相当于:

for (unsigned int i = n; i-- > 0;) ... 

这里,减量发生在条件和更新的部分是空的。优点是,它使用完全相同的边界,即逐字符号0n,但通过在进入循环体之前递减,访问相同的有效数字范围0n - 1。它适用于无符号整数,这是循环变量的自然选择。要长话短说:正向和反向循环在C中是不对称的,因此为它们编写宏是不容易的。 C的语法比for i = 1 to n更详细,但事实就是如此。通过选择合适的索引名称来接受它,并减轻打字痛苦:它是i,而不是current_index

如果没有宏,你可以减少代码的冗余吗?当然:你可以写冒泡两种功能,下一次:

static int bubble_up(int a[], int n) 
{ 
    int done = 1; 

    for (int i = 1; i < n; i++) { 
     if (a[i - 1] > a[i]) { 
      swap(a, i - 1, i); 
      done = 0; 
     } 
    } 

    return done; 
} 

static int bubble_down(int a[], int n) 
{ 
    int done = 1; 

    for (int i = n; i-- > 1;) { 
     if (a[i - 1] > a[i]) { 
      swap(a, i - 1, i); 
      done = 0; 
     } 
    } 

    return done; 
} 

(这些功能static,即私有的,当前编译单元)现在你的实际排序的功能是这样的:

void sort_bubble_up(int a[], int n) 
{ 
    int done = 0; 

    while (!done) { 
     done = bubble_down(a, n); 
    } 
} 

void sort_bubble_down(int a[], int n) 
{ 
    int done = 0; 

    while (!done) { 
     done = bubble_down(a, n); 
    } 
} 

void sort_shaker(int a[], int n) 
{ 
    int done = 0; 

    while (!done) { 
     done = bubble_up(a, n) || bubble_down(a, n); 
    } 
} 

如果你不怕空循环体,你甚至可以让他们到:

void sort_bubble_up(int a[], int n) 
{ 
    while (bubble_down(a, n)) { } 
} 

void sort_bubble_down(int a[], int n) 
{ 
    while (bubble_down(a, n)) { } 
} 

void sort_shaker(int a[], int n) 
{ 
    while (bubble_up(a, n) || bubble_down(a, n)) { } 
} 

所有这些代码仅适用于int一但是。标准库的接近类型独立性的方法是通过void *指针和用户定义的比较函数在字节级别上工作。例如,排序功能qsort这样做。

C++和其他语言有模板,您可以在其中编写几种类型的算法。当你“实例化”一个模板时,编译器为这个类型创建一个函数,然后调用它。

你可以用宏模拟这个。如果你只是想调用的函数体中宏,你可以定义:

#define BUBBLE_SORT(ARRAY, N, TYPE) do {   \ 
     int done = 0;       \ 
     int i;         \ 
               \ 
     while (!done) {       \ 
      done = 1;       \ 
               \ 
      for (i = 1; i < N; i++) {   \ 
       if (ARRAY[i - 1] > ARRAY[i]) { \ 
        TYPE sawp = ARRAY[i];  \ 
               \ 
        ARRAY[i] = ARRAY[i - 1];  \ 
        ARRAY[i - 1] = swap;   \ 
        done = 0;     \ 
       }        \ 
      }         \ 
     }          \ 
    } while (0) 

,然后使用像这样的宏:

char c[] = "Mississippi"; 

BUBBLE_SORT(c, strlen(c), char); 

(即do { ... } while (0)事情周围thze宏使得宏的行为就像函数调用一样,循环体的新范围允许局部变量)

这里的问题是这样的多行宏很难调试。当主体出现错误时,您只需在错误消息中获取调用宏的行的编号。 (但是你可以使用-E与大多数编译器看到预处理器如何解决了宏)。

结论:

  • 宏是有用的,但你要知道自己在做什么。一般来说,尽量避免使用它们,因为它们很难调试,而且往往难以理解。 (另外这个人可能会在半年后。)
  • 如果你必须使用宏,尽量让它看起来尽可能的自然。通过运营商如>+应该让你保持警惕。
  • 对通用代码使用函数而不是宏。
  • 拥抱C处理不同类型的方法。如果学习qsort的工作方式,而不是为泡泡排序实现提供宏,它将会更有用(如果不那么有趣)。
  • 如果你真的需要写很多类型无关的代码,你可能不应该使用C.
+0

感谢您的详细解答。关于你的第一个评论 - 是的,我想避免重复的代码,这就是为什么我选择使用MACRO。你有任何其他建议如何使用相同的函数泡MAX值正确和最小值向量的左侧(用于摇动排序的向量)?你认为使用函数参数作为指向另一个函数的指针可以解决它吗? – user8180105

+0

另外 - 插入var_t是为了将此函数用于其他矢量 - 其他类型的变量(不只是整数)。在这种情况下,我也可以使用指针void。 – user8180105