100 Days of Code-day33-输出模式切换(顺逆序按位“rn (让你) 双重奏”)
在day32的基础上,我们对其进行修改,使它能够处理-r标记。该标记表明,以逆序(递减)方式排序,不过要保证-r和-n能够组合在一起使用。
个人思路:
修改1:
if (argc > 2 && mystrcmp(argv[1], "-n") == 0 && mystrcmp(argv[2], "-r") == 0)numeric = 1;
增加了一个参数-r,所以对于主函数中判断排序模式语句,我对于if语句的条件进行了修改。需要判断捕获到的第三个参数是否为-r
修改2:
既然是逆序方式排序,我希望自己在改变排序算法的前提下,只对进行比较操作的函数进行修改,也就是当目标值大于基准值时,返回负数。此时myqsort函数接收到传递的值为负,所以此时进行交换操作。所以,之前是将小的元素尽量往前放,而现在是将较大的元素尽量往前放。
int numcmp(char *s1, char *s2){double value1, value2;value1 = atof(s1);value2 = atof(s2);if (value1 > value2)return -1;else if (value1 < value2)return 1;elsereturn 0;}
K&R的思路:
static char option = 0;//默认情况下为字符串排序,且为按升序排序int main(int argc, char *argv[]){int nlines;char *lineptr[MaxLines];int c, rc = 0;//rc作为所输入的命令字符是否正确的标志char **temp = argv;scanf("%d\n", &argc);for (int i = 1; i < argc; i++){argv[i] = (char *)malloc(MaxLen * sizeof(char));scanf("%s", argv[i]);}getchar();while(--argc > 0 && (*++temp)[0] == '-')while(c = *++temp[0])switch (c){case 'n':option |= Numeric;break;case 'r':option |= Decr;break;default:printf("sort: illegal option %c\n", c);argc = 1;//一旦输入的命令字符非'rn',就表明输入的是无效命令rc = -1;break;}if (argc)printf("Usage: sort -nr\n");elseif ((nlines = readline(lineptr, MaxLines)) >= 0)//这里并不意味着nlines=0的时候需要参与排序,而是说这里为了方便操作//将无输入内容一并归入有输入内容中,也就是将其当做一种正常情况。否则要多设置一个返回标志。{if(option & Numeric)myqsort((void **)lineptr, 0, nlines - 1, (int(*)(void*, void*))numcmp);//为什么是nlines-1呢//原因很简单,计算行数是从序号0开始的,0到nlines-1 一共nlines行elsemyqsort((void **)lineptr, 0, nlines - 1, (int(*)(void*, void*))strcmp);writeline(lineptr, nlines, option & Decr);}else {//NOTE:readline函数在输入行数超出限制范围或者没有足够的空间在放入输入内容的情况下,会返回负数。//对于这种情况需要在显示屏上显示相应的标志printf("input too big to sort\n");rc = -1;}return rc;}
本题目要求对字符串或数字排序,同时按升序或者降序排序。那么就一共有2*2=4种情况。如何才能较为巧妙地将这四种情况区分开来。进行位运算就是一个不错的选择。
排序程序的具体功能有静态字符变量option的二进制位取值决定
具体思路如下:(假设一个数字用8位二进制位来表示)
00000000 从右往左,编号由低到高
第0位=0 对字符串排序
第0位=1 对数字排序
第1位=0 按升序排序
第1位=1按降序排序
修改1:
相对应的程序段如下所示,可以说下面这段程序是本程序当中的第一个精妙之处。
while(c = *++temp[0])switch (c){case 'n':option |= Numeric;break;case 'r':option |= Decr;break;default:printf("sort: illegal option %c\n", c);argc = 1;//一旦输入的命令字符非'rn',就表明输入的是无效命令rc = -1;break;}
- note1:
option |= Numeric;
Numeric=1,即二进制表示形式为00000001。任何数与其进行按位或运算第0位都将成为1,则程序进行对数字的排序。
- note2:
option |= Decr;
Decr=2,即二进制表示形式为00000010。任何数其进行按位或运算第1位都将成为1,则程序按照降序进行排序。
- 那么对于其余两种情况,就是在第0位和第1位的二进制位为0时便可以表示。
总结起来就是,通过位运算,我们可以通过option这一个变量就可以表示多种情况,在这里我们用到了两个二进制位表示了4种情况。
修改2:
这第二个精妙之处便是下面这段程序。虽然之前我们将四种情况通过命令指令表示出来了。但是,没有将命令与相对应的操作联系在一起。这里的按位与运算很好的将这两者联系在一起。
if(option & Numeric)myqsort((void **)lineptr, 0, nlines - 1, (int(*)(void*, void*))numcmp);elsemyqsort((void **)lineptr, 0, nlines - 1, (int(*)(void*, void*))strcmp);writeline(lineptr, nlines, option & Decr);
- 在判断该执行数值排序还是字符串排序时,我们可以看到两者命令的不同在于第0位的不同。所以将option与Numeric(等于1)进行逻辑与运算,得到的数字的第0个二进制位是否为1,由此就可以判断该用哪个函数进行交换操作。
- 同里writelines函数中的修改也是如此。
输出行writelines函数
增加了一个参数decr就是为了判断升降序输出的问题。也就是看第1位二进制数是否为1,由此可以判断是否应该逆序输出。具体如下所示。
if(decr)while(--nlines)printf("%s\n", lineptr[nlines]);elsefor (i = 0;i < nlines; i++){printf("%s\n", lineptr[i]);}