10.7 Другое приложение — sort

Предыдущая  Содержание  Следующая V*D*V

Давайте напишем маленькую программу сортировки текста, чтобы проверить, действительно ли 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() и поэтому можно (зло-)употребить её в качестве функции сравнения.

 

Предыдущая  Содержание  Следующая