10.7 Другое приложение — sort |
Предыдущая Содержание Следующая |
Давайте напишем маленькую программу сортировки текста, чтобы проверить, действительно ли Filter можно использовать многократно, чтобы увидеть, как обрабатываются параметры командной строки, и по достоинству оценить, что делегат может принадлежать произвольному классу. Фильтр сортировки должен собрать все текстовые строки, отсортировать весь набор, и наконец записать их. Раздел 7.7 представил List на основе динамического кругового буфера, который мы можем использовать, чтобы собрать строки до того, как добавим метод сортировки. В разделе 2.5 мы реализовали простой класс String; если мы интегрируем его в нашу иерархию классов, то сможем использовать его для хранения всех строк в List. Давайте начнём с главной программы, которая просто создаёт фильтр с его делегатом:
int main (int argc, char * argv []) { void * filter = new(Filter(), new(Sort(), 0));
return mainLoop(filter, argv); }
Поскольку мы можем подключить методы обратного вызова для любого класса, то можно создать делегата непосредственно в подклассе List:
% SortClass: ListClass Sort: List { char rflag; %— void flags (_self, Object @ filter, char flag); int line (_self, const Object @ filter, const char * fnm, \ char * buf); int quit (_self, const Object @ filter); %}
Чтобы продемонстрировать обработку опции, мы распознаём -r как просьбу отсортировать в обратном порядке. Все другие флаги отбрасываются методом flags(), у которого есть тэг flag для respondsTo():
% flag: Sort flags { %casts assert((flagM) flags == flags);
if (flag == ’r’) self -> rflag = 1; else fprintf(stderr, "usage: %s [-r] [file...]\n", progname(filter)), exit(1); }
Имея String и List сбор линий тривиален:
% Sort line { %casts assert((lineM) line == line);
addLast(self, new(String(), buf)); return 0; }
Как только все строки собраны, обратный вызов quit заботится о сортировке и записи. Если есть хотя бы одна строка, мы позволяем новому виду sort() позаботиться о сортировке списка, а затем удаляем все строки поочередно и позволяем объекту String самому вывести себя на экран. Мы можем отсортировать в обратном порядке просто удаляя строки с конца списка:
% Sort quit { %casts assert((quitM) quit == quit);
if (count(self)) { sort(self); do puto(self -> rflag ? takeLast(self) : takeFirst(self), stdout); while (count(self)); } return 0; }
Что можно сказать о sort()? ANSI-C для сортировки произвольных массивов на основе функции сравнения определяет библиотечную функцию qsort(). К счастью List реализован как круговой буфер в массиве, то есть если мы захотим реализовать sort() как метод List, то должны испытать очень мало затруднений:
static int cmp (const void * a, const void * b) { return differ(* (void **) a, * (void **) b); }
% List sort { %casts if (self -> count) { while (self -> begin + self -> count > self -> dim) addFirst(self, takeLast(self)); qsort(self -> buf + self -> begin, self -> count, sizeof self -> buf[0], cmp); } }
Если список содержит какие-либо элементы, мы прокручиваем список до тех пор, пока он не будет занимать слитную область буфера, а затем передаём список в qsort(). Функция сравнения отсылает differ() прямо к элементам списка — String_differ была основана на strcmp() и поэтому можно (зло-)употребить её в качестве функции сравнения.
|
Предыдущая Содержание Следующая |