Понимание циклов c благодаря getchar - PullRequest
0 голосов
/ 21 октября 2018

Я прошу помощи для следующей программы, с которой я боролся несколько часов сегодня днем.

Я хотел бы создать функцию, которая получает массив символов (который принадлежит алфавитуA = {a,b,c}) и его dim и return 1, если символ принадлежит языку L, или 0, если нет.

Язык был: a^k b^n c^m, такой что k,m>=0 и n>0.

Я очень старался, и я собираюсь опубликовать то, что я достиг, но мой метод кажется очень длинным (помимо отсутствия работы)

И я хотел бы знать, как я мог думатьс точки зрения улучшения моего кода.

#include <stdio.h>
int array(char v[], int dim) {
   int i, j, k, trovato = 1;
   if (v[0] == 'c') trovato = 0;

   if (v[0] == 'a') {
      for (i = 1; i < dim; i++) {
         while (trovato == 1) {
            if (v[i] == 'c')
               trovato = 0;
            else if (v[i] == 'b') {
               trovato = 1;
               for (j = i + 1; j < dim; j++) {
                  while (trovato == 1) {
                     if (v[j] == 'a') trovato = 0;
                     if (v[j] == 'b')
                        trovato = 1;
                     else if (v[j] == 'c') {
                        trovato = 1;
                        for (k = j + 1; k < dim; k++) {
                           while (trovato == 1) {
                              if (v[k] == 'c')
                                 trovato = 1;
                              else
                                 trovato = 0;
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }

   if (v[0] == 'b') {
      for (i = 1; i < dim; i++) {
         while (trovato == 1) {
            if (v[i] == 'a') trovato = 0;
            if (v[i] == 'b')
               trovato = 1;
            else if (v[i] == 'c') {
               trovato = 1;
               for (j = i; j < dim; j++) {
                  while (trovato == 1) {
                     if (v[j] != 'c')
                        trovato = 0;
                     else
                        trovato = 1;
                  }
               }
            }
         }
      }
   }

   return trovato;
}

int main() {
   char d;
   int DIM, i = 0, k;
   scanf("%d", &DIM);
   char r[DIM];
   scanf("%c", &d);
   d = getchar();
   while (d != '\n') {
      r[i] = d;
      i++;
      scanf("%c", &d);
      d = getchar();
   }
   k = array(r, DIM);
   printf("%d\n", k);
   return 0;
}

Чего я действительно не понимаю, так это того, почему ввод продолжается, как если бы он был в цикле while.

Я думаю, чтопроблема в моем понимании getchar и scanf на символе, поэтому эти строки для примеров:

scanf("%c",&d);
d=getchar();

А как массив векторов должен быть инициализирован?

Что ядействительно обеспокоен эффективностью, я боюсь не улучшать так быстро и правильно, чтоВот почему я спрашиваю даже строгих, но конструктивных критиков в отношении моей работы, которую я пытался выполнить.

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

Альтернативный подход - использовать http://re2c.org/ для его генерации.Взято с их сайта,

Его основная цель - генерировать быстрые лексеры: по крайней мере, так же быстро, как их разумно оптимизированные аналоги, закодированные вручную.

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

static int match(const char *const s) {
    const char *t = s, *marker;
    /*!re2c
        re2c:yyfill:enable = 0;
        re2c:define:YYCTYPE = char;
        re2c:define:YYCURSOR = t;
        re2c:define:YYMARKER = marker;

        pattern = 'a'* 'b'+ 'c'*;
        end = '\n'? "\x00";
        *           { return 0; }
        pattern end { return 1; }
    */
}

int main(void) {
    char a[512];
    while(fgets(a, sizeof a, stdin))
        printf("[%s] %s", match(a) ? "passed" : "reject", a);
    if(errno) return perror("input"), EXIT_FAILURE;
    return EXIT_SUCCESS;
}

Создание re2c a.re > a.c,

/* Generated by re2c 1.0.3 on Sun Oct 21 18:38:31 2018 */
#line 1 "a.re"
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#line 6 "a.re"


static int match(const char *const s) {
    const char *t = s, *marker;

#line 14 "<stdout>"
{
    char yych;
    yych = *t;
    switch (yych) {
    case 'A':
    case 'a':   goto yy4;
    case 'B':
    case 'b':   goto yy5;
    default:    goto yy2;
    }
yy2:
    ++t;
yy3:
#line 18 "a.re"
    { return 0; }
#line 30 "<stdout>"
yy4:
    yych = *(marker = ++t);
    switch (yych) {
    case 'A':
    case 'B':
    case 'a':
    case 'b':   goto yy7;
    default:    goto yy3;
    }
yy5:
    yych = *(marker = ++t);
    switch (yych) {
    case 0x00:
    case '\n':
    case 'B':
    case 'C':
    case 'b':
    case 'c':   goto yy10;
    default:    goto yy3;
    }
yy6:
    yych = *++t;
yy7:
    switch (yych) {
    case 'A':
    case 'a':   goto yy6;
    case 'B':
    case 'b':   goto yy9;
    default:    goto yy8;
    }
yy8:
    t = marker;
    goto yy3;
yy9:
    yych = *++t;
yy10:
    switch (yych) {
    case 0x00:  goto yy11;
    case '\n':  goto yy13;
    case 'B':
    case 'b':   goto yy9;
    case 'C':
    case 'c':   goto yy14;
    default:    goto yy8;
    }
yy11:
    ++t;
#line 19 "a.re"
    { return 1; }
#line 80 "<stdout>"
yy13:
    yych = *++t;
    if (yych <= 0x00) goto yy11;
    goto yy8;
yy14:
    yych = *++t;
    switch (yych) {
    case 0x00:  goto yy11;
    case '\n':  goto yy13;
    case 'C':
    case 'c':   goto yy14;
    default:    goto yy8;
    }
}
#line 20 "a.re"

}

int main(void) {
    char a[512];
    while(fgets(a, sizeof a, stdin))
        printf("[%s] %s", match(a) ? "passed" : "reject", a);
    if(errno) return perror("input"), EXIT_FAILURE;
    return EXIT_SUCCESS;
}

Можно получить регистрозависимый вывод и множество других опций, см. http://re2c.org/manual/options/options.html.

0 голосов
/ 21 октября 2018

Давайте попробуем сделать это проще.Мы вычисляем указатель, который находится за концом массива (v + dim), так что нам не нужно использовать индексирование и индексные переменные, но мы можем изменить сам указатель v.

int matches(const char *v, size_t dim) {
    const char *end = v + dim;
    size_t k = 0, m = 0, n = 0;

    // count consecutive 'a's.
    // for as long as `v` is positioned before the end
    // and points to an 'a', increment `v` and increment `k`.
    while (v < end && *v == 'a') {
        k ++;
        v ++;
    }

    // count consecutive 'b's
    while (v < end && *v == 'b') {
        m ++;
        v ++;
    }

    // count consecutive 'c's
    while (v < end && *v == 'c') {
        n ++;
        v ++;
    }

    // we didn't meet the end yet, something else was seen!
    if (v < end) {
        // not just aaa...bbbbb....cccc...
        return 0;
    }

    // there were only a's, b's, c's in that order
    else {
        check that k, m, n matches the constraints 
        and return a result based on that.
    }
}

Таким образом, первый цикл перемещает указатель вперед до тех пор, пока он указывает на 'a', второй для 'b' и третий для 'c' - тогда мы гарантируем, что мы действительно достигли конца,а не просто случай abca например;мы увеличиваем k, m и n в циклах, поэтому к моменту обращения к предложению else верно, что L = a^k b^m c^n, и вам просто нужно проверить k, m, n.

Никаких вложенных циклов не требуется.


Что касается ввода,

scanf("%c",&d);
d=getchar();

вы читаете символы дважды .Вам нужно только getchar():

int c;  // getchar returns an *int*

while ((c = getchar()) != EOF && c != '\n') {
    r[i ++] = d;
}

- это все, что требуется для чтения ввода до конца файла или новой строки.

...