Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Demon's Souls |#13| Storm King
Demon's Souls |#11| Мaneater part 2
Demon's Souls |#10| Мaneater (part 1)
Demon's Souls |#9| Heart of surprises

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Богатырев А. Весь текст 1009.15 Kb

Хрестоматия по программированию на Си в Unix

Предыдущая страница Следующая страница
1 ... 66 67 68 69 70 71 72  73 74 75 76 77 78 79 ... 87

                            }
                    }
                    printf("Клавиша: %s\n\r", s);

                    if(c == ESC)
                            break;
            }
            tcsetattr(INPUT_CHANNEL, TCSADRAIN, &saved_modes);
    }

    /* Функция настройки на систему команд дисплея */
    void tinit (void) {
        /* static */ char Tbuf[2048];
        /* Tbuf должен сохраняться все время, пока могут вызываться функции tgetstr().
         * Для этого он либо должен быть static, либо вызов функции keyinit()
         * должен находиться внутри tinit(), что и сделано.
         */
        char *tname;
        extern char *getenv();

        if((tname = getenv("TERM")) == NULL){
            printf("TERM не определено: неизвестный тип терминала.\n");
            exit(2);
        }
        printf("Терминал: %s\n", tname);

        /* Прочесть описание терминала в Tbuf */
        switch (tgetent(Tbuf, tname)) {
             case -1:
                printf ("Нет файла TERMCAP (/etc/termcap).\n");
                exit (1);
            case 0:
                printf ("Терминал '%s' не описан.\n", tname);
                exit (2);
            case 1:
                break;              /* OK */
        }
        if(strlen(Tbuf) >= 1024)
    printf("Описание терминала слишком длинное - возможны потери в конце описания\n");

        keyinit();  /* инициализировать строки, пока Tbuf[] доступен */
    }

    void main(void){
            setlocale(LC_ALL, "");
            tinit();
            /* keyinit(); */
            dotest();
            exit(0);
    }

По поводу этого алгоритма надо сказать еще пару слов. Его модификация может с успехом
применяться  для  поиска  слов в таблице (команд, ключей в базе данных, итп.): список
слов превращается в дерево. В таком поисковом алгоритме не требуются таймауты,  необ-
ходимые  при  вводе  с  клавиатуры,  поскольку есть явные терминаторы строк - символы
'\0', которых нет при вводе с клавиатуры.   В  чем  эффективность  такого  алгоритма?
Сравним последовательный перебор при помощи strcmp и поиск в дереве букв:

А. Богатырев, 1992-95                  - 386 -                              Си в UNIX

    "zzzzzzzzzza"
    "zzzzzzzzzzb"
    "zzzzzzzzzzbx"
    "zzzzzzzzzzc"
    "zzzzzzzzzzcx"

Для линейного перебора (даже в отсортированном  массиве)  поиск  строки  zzzzzzzzzzcx
потребует

    zzzzzzzzzza     |       11 сравнений, отказ
    zzzzzzzzzzb     |       11 сравнений, отказ
    zzzzzzzzzzbx    |       12 сравнений, отказ
    zzzzzzzzzzc     |       11 сравнений, отказ
    zzzzzzzzzzcx    V       12 сравнений, успех

Всего: 57 шагов.  Для поиска в дереве:

    __z__z__z__z__z__z__z__z__z__z__a__\0
                                  |_b__\0
                                  |  |_x__\0
                                  |
                                  |_c__\0
                                     |_x__\0

потребуется проход вправо (вниз) на 10 шагов, потом выбор среди 'a','b','c', потом  -
выбор среди '\0' и 'x'.  Всего: 15 шагов. За счет того, что общий "корень" проходится
ровно один раз, а не каждый раз заново. Но это и требует  предварительной  подготовки
данных: превращения строк в дерево!

8.18.  Напишите функцию для  "экранного"  редактирования  вводимой  строки  в  режиме
CBREAK.  Напишите  аналогичную  функцию  на curses-е.  В curses-ной версии надо уметь
отрабатывать: забой (удаление символа перед курсором), отмену всей  строки,  смещение
влево/вправо  по строке, удаление символа над курсором, вставку пробела над курсором,
замену символа, вставку символа, перерисовку экрана.  Учтите, что параллельно с изме-
нением  картинки  в  окне,  вы  должны вносить изменения в некоторый массив (строку),
которая и будет содержать результат. Эта строка должна быть аргументом функции редак-
тирования.
     Забой можно упрощенно эмулировать как

    addstr( "\b \b" );
            или
    addch( '\b' ); delch();

Недостатком этих способов является некорректное поведение в начале строки (при x==0).
Исправьте это!

8.19.  На curses-е напишите функцию редактирования текста  в  окне.   Функция  должна
возвращать  массив  строк с обрезанными концевыми пробелами. Вариант: возвращать одну
строку, в которой строки окна разделяются символами '\n'.

8.20.  Напишите функцию, рисующую прямую линию из точки (x1,y1) в (x2,y2).  Указание:
используйте  алгоритм  Брезенхема  (минимального  отклонения).   Ответ: пусть функция
putpixel(x,y,color) рисует точку в координатах (x,y) цветом color.

    void line(int x1, int y1,   int x2, int y2,
              int color){
        int dx, dy, i1, i2, i, kx, ky;
        register int d; /* "отклонение" */
        register int x, y;
        short /* boolean */ l;

А. Богатырев, 1992-95                  - 387 -                              Си в UNIX

        dy = y2 - y1; dx = x2 - x1;
        if( !dx && !dy ){
                putpixel(x1,y1, color); return;
        }
        kx = 1; /* шаг по x */
        ky = 1; /* шаг по y */
        /* Выбор тактовой оси */
        if( dx < 0 ){ dx = -dx; kx = -1; } /* Y */
        else if( dx == 0 )      kx = 0;    /* X */
        if( dy < 0 ){ dy = -dy; ky = -1; }
        if( dx < dy ){ l = 0; d = dx; dx = dy; dy = d; }
        else           l = 1;

        i1 = dy + dy; d = i1 - dx; i2 = d - dx;
        x = x1; y = y1;

        for( i=0; i < dx; i++ ){
             putpixel( x, y, color );

             if( l ) x += kx; /* шаг по такт. оси   */
             else    y += ky;
             if( d < 0 ) /* горизонтальный шаг      */
                 d += i1;
             else{       /* диагональный шаг        */
                 d += i2;
                 if( l ) y += ky; /* прирост высоты */
                 else    x += kx;
             }
        }
        putpixel(x, y, color);  /* последняя точка  */
    }

8.21.  Составьте программу, которая строит график функции sin(x) на отрезке от  0  до
2*пи.   Учтите  такие вещи: соседние точки графика следует соединять отрезком прямой,
чтобы график выглядел непрерывным; не забывайте приводить double к int, т.к.  коорди-
наты пикселов[*] - целые числа.

8.22.  Напишите функцию, которая заполняет в массиве байт count бит подряд, начиная с
x-ого бита от левого края массива:

        байт 0      |          байт 1
    7 6 5 4 3 2 1 0 | 7 6  5  4  3  2  1  0 : биты в байте
    0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15 : x
        ==========================
            x=2, count=11

Такой алгоритм используется в растровой машинной графике для рисования горизонтальных
прямых  линий  (тогда  массив  - это видеопамять компьютера, каждый бит соответствует
пикселу на экране).
     Ответ (причем мы заполняем биты не просто единицами, а "узором" pattern):

    void horizLine(char *addr,int x,int count,char pattern){
      static char masks[8] = {
           0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
      /* индекс в этом массиве равен числу 0-битов слева */
____________________
   [*] Пиксел (pixel, pel) - picture element, в машинной графике - точка растра на  эк-
ране.

А. Богатырев, 1992-95                  - 388 -                              Си в UNIX

      register i;
      char mask;
      short lbits, rbits; /* число битов слева и справа */
      short onebyte;      /* единственный байт ? */

      addr += x/8;        /* в байте 8 бит */
      mask = masks[ lbits = x & 7 ];   /* x % 8 */
      if( count >= (rbits = 8 - lbits)){
          count -= rbits; onebyte = 0;
      }else{
          mask &= ~masks[ lbits = (x+count) & 7 ];
          onebyte = 1;
      }
    /* Первый байт */
      *addr = (*addr & ~mask) | (pattern & mask);
      addr++;
      /* Для pattern==0xFF можно просто
       *     *addr++ |= mask;
       * поскольку (a &~m)|(0xFF & m) = (a &~m) | m =
       *   (a|m) & (~m|m) = (a|m) & 0xFF = a | m
       * Почему здесь нельзя написать *addr++ = (*addr...) ?
       * Потому, что ++ может быть сделан ДО вычисления
       * правой части присваивания!
       */
      if(onebyte) return;
    /* Средние байты */
      for(i = count/8; i > 0; --i)
          *addr++ = pattern;  /* mask==0xFF */
    /* Последний байт */
      if((lbits = count & 7) == 0) return;
      /* последний байт был полным */
      mask = ~masks[lbits];
      *addr = (*addr & ~mask) | (pattern & mask);
    }

Заметим, что для быстродействия подобные алгоритмы обычно пишутся на ассемблере.

8.23.  Напишите при помощи curses-а "электронные часы",  отображающие  текущее  время
большими  цифрами  (например, размером 8x8 обычных символов) каждые 5 секунд. Исполь-
зуйте alarm(), pause().

8.24.  Составьте программу, реализующую простой диалоговый интерфейс,  основанный  на
меню. Меню хранятся в текстовых файлах вида:

А. Богатырев, 1992-95                  - 389 -                              Си в UNIX

            файл menu2_12
    -----------------------------------------------
    ЗАГОЛОВОК_МЕНЮ
    +команда_выполняемая_при_входе_в_меню
    -команда_выполняемая_при_выходе_из_меню
    альтернатива_1
            команда1_1
            команда1_2
    альтернатива_2
            команда2_1
            команда2_2  #комментарий
            команда2_3
    альтернатива_3
           >menu2_2     #это переход в другое меню
    альтернатива_4
           >>menu3_7    #хранимое в файле menu3_7
    ...
            ...
    -----------------------------------------------

Программа должна обеспечивать: возврат к предыдущему меню по клавише Esc  (для  этого
следует  хранить "историю" вызовов меню друг из друга, например в виде "полного имени
меню":

    .rootmenu.menu1_2.menu2_4.menu3_1

где menuI_J - имена файлов с меню), обеспечить выход из программы по клавишам  'q'  и
ESC,  выдачу подсказки по F1, выдачу полного имени меню по F2.  Вызов меню при помощи
>> означает замещение текущего меню новым, что соответствует замене  последней  компо-
ненты  в  полном  имени  меню.  Вызов  >>>> означает вызов меню как функции, т.е. после
выбора в новом меню и выполнения нужных действий автоматически должно быть выдано  то
меню, из которого произошел вызов (такой вызов соответствует удлинению полного имени,
а возврат из вызова - отсечению последней компоненты).  Этот вызов может быть показан
на  экране  как  появление нового "выскакивающего" окна поверх окна с предыдущим меню
(окно возникает чуть сдвинутым - скажем, на y=1 и x=-2), а возврат - как исчезновение
этого окна.  Заголовок меню должен высвечиваться в верхней строке меню:

               |-------------------
            |--ЗАГОЛОВОК_МЕНЮ---- |
            |  альтернатива_1   | |
            |  альтернатива_2   | |
            | *альтернатива_3   | |
            |  альтернатива_4   |--
            ---------------------

Сначала реализуйте версию, в которой каждой "альтернативе" соответствует единственная
строка   "команда".    Команды  следует  запускать  при  помощи  стандартной  функции
system(команда).
     Усложните функцию выбора в меню так, чтобы альтернативы можно было  выбирать  по
первой букве при помощи нажатия кнопки с этой буквой (в любом регистре):

            Compile
            Edit
            Run program

8.25.  Напишите на curses-е функцию, реализующую выбор в меню  -  прямоугольной  таб-
лице:

А. Богатырев, 1992-95                  - 390 -                              Си в UNIX

            слово1   слово4   слово7
            слово2  *слово5   слово8
            слово3   слово6

Строки - элементы меню - передаются в функцию выбора в  виде  массива  строк.   Число
элементов  меню  заранее  неизвестно и должно подсчитываться внутри функции.  Учтите,
что все строки могут не поместиться в таблице, поэтому надо предусмотреть  "прокручи-
вание"  строк  через  таблицу  при  достижении  края меню (т.е. таблица служит как бы
Предыдущая страница Следующая страница
1 ... 66 67 68 69 70 71 72  73 74 75 76 77 78 79 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама