[D语言] qsort的尴尬

本文讨论了Phobos标准库中qsort函数的使用限制及其改进方案。介绍了如何通过委托参数来提高qsort的灵活性,并提供了一个新的qsort实现示例。
phobos里面在stc.c.stdlib里提供了qsort,这是个传统的qsort:
[code]
void qsort(void *base, size_t nelems, size_t elemsize,
int (*compare)(void *elem1, void *elem2));
[/code]
它接受的比较函数是个函数指针,如果我们想使用委托就比较麻烦了,委托是对象指针和函数指针的绑定。

phobos/internal/qsort2.d里实现了一个数组排序方法:
[code]
extern (C) long _adSort(Array a, TypeInfo ti)
{
synchronized
{
tiglobal = ti;
std.c.stdlib.qsort(a.ptr, a.length, cast(size_t)ti.tsize(), &cmp);
}
return *cast(long*)(&a);
}
[/code]
当调用array.sort时就会使用它。它使用了一个全局变量,在比较函数里调用这个全局变量,所以能够知道是哪个ClassInfo对象,间接完成了委托功能。由于使用了全局变量,为了防止多个线程同时修改使用tiglobal,它增加了synchronized区块,代价是多个线程对多个数组排序将是顺序执行的。

当然可以避免使用这种临界区,或者是避免长时间锁住,有两种方法。

方法1是在_adSort里锁住临界区,赋值然后调用qsort,在qsort里复制全局的tiglobal以函数执行栈上,然后释放临界区,可以提高效率,也就是避免长时间锁住。带来的问题是qsort变成一个“不干净”的版本,它脏了,而且效率也比较低,临界区的开销不小。如果这样还不如给qsort和它的排序函数加一个参数呢:
[code]
void qsort(void *base, size_t nelems, size_t elemsize,
int (*compare)(void *elem1, void *elem2, void* arg), void* arg);
[/code]
稍干净点,一样难看。

方法2是使用线程专有存储(TSS),我在phobos里面没有看到它使用,所以也比较麻烦,因为需要初始化和释放,修改Thread类?感觉不好。

搜索到一个帖子:
http://www.digitalmars.com/d/archives/137.html

看上去很美,不过没有实现亚,真是麻烦。。自己写线程类吧。。就为了这个接受委托的qsort。。。好像还是重写个qsort更简单一些。

phobos/internal/qsort.d提供了数组排序的不加锁版本,不过是专用的。

以上是打算调用std.c.stdlib.qsort来编写使用委托参数的qsort时遇到的麻烦。感觉还是写一个比较简单:
[code]
void qsort(T)(T* arr, size_t n, int delegate(T,T) dg) {
if (!n) return;

int i=0, k=n-1;
T t = arr[k>>1];

do {
while(dg(arr[i], t) < 0)
i++;
while(dg(t, arr[k]) < 0)
k--;
if (i>k)
break;
if (i!= k) {
T tmp = arr[i];
arr[i] = arr[k];
arr[k] = tmp;
}
k--;
i++;
}while(i<=k);

if (i<n)
qsort!(T)(arr+i, n-i, dg);

if (k)
qsort!(T)(arr, k+1, dg);
}

import std.stdio;
import std.perf;

void main() {
int cmp(int a, int b) {
return a - b;
}

PerformanceCounter counter = new PerformanceCounter;
counter.start();

for(int i=0; i<1000000; ++i) {
int[] arr = [5,2,4,1,3,8,5,9,7];
qsort(arr.ptr, arr.length, &cmp);
}
counter.stop();
writefln(counter.periodCount());
counter.start();

for(int i=0; i<1000000; ++i) {
int[] arr = [5,2,4,1,3,8,5,9,7];
arr.sort;
}
counter.stop();
writefln(counter.periodCount());
}
[/code]
由于没有优化,所以效率比array.sort要低一些。
### C语言 `qsort` 函数的使用方法 #### 头文件引入 在使用 `qsort` 函数之前,需要包含标准库头文件 `<stdlib.h>`[^3]。 #### 参数说明 `qsort` 函数有四个参数: 1. **`void *base`**: 表示待排序数组的第一个元素的地址。 2. **`size_t num`**: 表示数组中的元素个数。 3. **`size_t size`**: 表示单个数组元素的大小(以字节为单位)。 4. **`int (*comparator)(const void *, const void *)`**: 这是一个函数指针,用于定义比较逻辑。它接收两个指向常量的 `void*` 类型参数,并返回一个整数值来指示它们之间的关系[^4]。 #### 返回值 `qsort` 不会返回任何值(即返回类型为 `void`),因此它是通过修改原数组来进行排序的[^1]。 #### 比较函数的设计 为了实现不同类型的排序,需要设计合适的比较函数。例如对于基本数据类型(如 `int`, `float` 等),可以直接进行数值比较;而对于复杂的数据结构(如字符串或自定义结构体),则需编写特定的比较逻辑[^2]。 以下是几个具体的例子: --- #### 示例 1: 对整型数组进行升序排列 ```c #include <stdio.h> #include <stdlib.h> // 定义比较函数 int compare_int(const void *a, const void *b) { return (*(int*)a - *(int*)b); // 升序排列 } int main() { int arr[] = {5, 3, 8, 6, 7}; int n = sizeof(arr)/sizeof(arr[0]); qsort(arr, n, sizeof(int), compare_int); printf("Sorted array:\n"); for (int i = 0; i < n; ++i) printf("%d ", arr[i]); return 0; } ``` 上述代码实现了对整型数组按升序排序的功能。 --- #### 示例 2: 对字符数组(字符串)降序排列 ```c #include <stdio.h> #include <string.h> #include <stdlib.h> // 定义比较函数 int compare_str(const void *a, const void *b) { return strcmp(*(char **)b, *(char **)a); // 字符串降序排列 } int main() { char *arr[] = {"apple", "orange", "banana", "grape"}; int n = sizeof(arr)/sizeof(arr[0]); qsort(arr, n, sizeof(char *), compare_str); printf("Sorted strings:\n"); for (int i = 0; i < n; ++i) printf("%s\n", arr[i]); return 0; } ``` 此程序展示了如何利用 `strcmp` 来完成字符串的降序排序操作。 --- #### 示例 3: 自定义结构体排序 假设有一个学生记录表,其中包含学生的姓名和成绩,我们希望按照分数从高到低对学生列表进行排序。 ```c #include <stdio.h> #include <stdlib.h> typedef struct Student { char name[50]; float score; } Student; // 定义比较函数 int compare_student(const void *a, const void *b) { return ((Student *)b)->score - ((Student *)a)->score; // 成绩降序排列 } int main() { Student students[] = { {"Alice", 90}, {"Bob", 85}, {"Charlie", 95}, {"David", 88} }; int n = sizeof(students)/sizeof(students[0]); qsort(students, n, sizeof(Student), compare_student); printf("Students sorted by scores descending:\n"); for (int i = 0; i < n; ++i) printf("%s %.1f\n", students[i].name, students[i].score); return 0; } ``` 这段代码演示了针对更复杂的用户定义类型执行排序的过程。 --- ### 总结 以上介绍了三种常见的场景下应用 `qsort` 函数的方法:简单数据类型、字符串以及自定义结构体。每种情况都需要精心设计对应的比较函数以便满足实际需求^。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值