Переключение букв (без строк или функций строк) - PullRequest
0 голосов
/ 30 октября 2018

Я хочу переключать слово «кошка» на «собака» каждый раз, когда оно появляется в тексте. Я не могу использовать строковые или строковые функции.

мой код:

#include <stdio.h>

int main()
{   
    int i; // loop counter 
    int size; // size of arry  
    int input[20];
    printf("enter text here\n");  

    while((input[i] = getchar()) != '\n') // input text to the arry
    {
        if(input[i]=='c' && input[i+1]=='a' && input[i+2]=='t') // switching characters
        {
            input[i]='d'; input[i+1]='o'; input[i+2]='g'; 
        }

        i++; 
        size++;  
    }

    i=0; // reset for next loop

    while(i <= size) // printing the text out ofthe arry 
    {
         putchar(input[i]); 
         i++;
    }

    printf("\n"); 
    return 0;
}

вывод:

enter text here                                                                                                                                  
cat                                                                                                                                              
cat                                                                                                                                              
ȵm�� $$ŵ��p��$���Zտ ��$��M��v���������������������      ������������!��d@8                                                                    $  

�                                                                                                                                                
�����������5_Segmentation fault

Ответы [ 4 ]

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

Эта задача идеально подходит для конечного автомата .

Вот блок-схема того, как конечный автомат может выполнять эту работу: Example finite state machine flowchart

Может быть удивительным, что несоответствующие случаи возвращаются к проверке, соответствует ли последний символ «C», а не просто распечатывают его и получают новый. Причина этого в том, что если мы читаем, скажем, ccat или cacat, мы хотим проверить, соответствует ли нам (более короткий) префикс, в данном случае c.

В качестве другого примера рассмотрим, пытались ли мы сопоставить cocoa, и наш ввод был cococoa. На пятом символе мы читаем c вместо a (уже сопоставив coco, но пока ничего не выводя), поэтому нам нужно вывести co и вернуться к сопоставлению для второго c.

Мы, люди, обычно не создаем такие конечные автоматы вручную. У нас уже есть один для строк, либо в виде библиотеки, либо встроенной в POSIX-совместимые библиотеки C (Linux, Mac, BSD): совпадение регулярного выражения . Если мы используем базовые POSIX, то regcomp() создает эффективный конечный автомат на основе наших спецификаций, а regexec() обрабатывает входные данные на нем.

Этот конкретный случай достаточно прост, чтобы реализовать его вручную. То, что мы хотим сделать, это поместить первое состояние («Get next char») вне цикла, выполнить цикл, который продолжается до тех пор, пока «char = EOF» не соответствует истине, и сделать остальное внутри цикла. Окончательное состояние «Готово» находится после цикла. В псевдокод :

ch = Next char
While ch != EOF:

    If ch != 'c':
        Output ch
        ch = Next char
        Continue
    End If

    # The second "Get next char" in the flowchart:
    ch = Next char

    If ch == EOF:
        Output 'c'
        Break
    Else
    If ch != 'a':
        Output 'c'
        Continue
    End If

    # The third "Get next char" in the flowchart
    ch = Next char

    If ch == EOF:
        Output 'c'
        Output 'a'
        Break
    Else
    If ch != 't':
        Output 'c'
        Output 'a'
        Continue
    End If

    # 'c' 'a' 't' matched (with ch == 't').
    Output 'd'
    Output 'o'
    Output 'g'

    ch = Next char
End While
Done

Программа на C, которая читает стандартный ввод и преобразует каждое вхождение cat в dog с учетом регистра, поэтому может быть записана как

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

void cat_to_dog(FILE *in, FILE *out)
{
    int ch;

    ch = fgetc(in);
    while (ch != EOF) {
        if (ch != 'c') {
            fputc(ch, out);
            ch = fgetc(in);
            continue;
        }

        ch = fgetc(in);
        if (ch == EOF) {
            fputc('c', out);
            break;
        } else
        if (ch != 'a') {
            fputc('c', out);
            continue;
        }

        ch = fgetc(in);
        if (ch == EOF) {
            fputc('c', out);
            fputc('a', out);
            break;
        } else
        if (ch != 't') {
            fputc('c', out);
            fputc('a', out);
            continue;
        }

        fputc('d', out);
        fputc('o', out);
        fputc('g', out);

        ch = fgetc(in);
    }
}

int main(void)
{
    cat_to_dog(stdin, stdout);
    return EXIT_SUCCESS;
}

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

И вот, наконец, мы подошли к сути этого «ответа»: надлежащей документации.

Решение проблемы один раз с использованием тщательно созданного, чрезвычайно жесткого и эффективного кода ничего не стоит, если нет способа изменить или исправить ошибки в коде. (Даже самый лучший программист в мире допускает ошибки и ошибки на разных уровнях сложности. Если кто-то утверждает, что он этого не делает, он лжет.)

Мы могли бы задокументировать конечный автомат, объяснив его в комментариях, перемеженных с приведенным выше кодом. Это было бы хорошо; комментарии всегда должны объяснять намерение программиста, цель или задачу, для выполнения которой предназначен фрагмент кода. Вместо этого мы часто пишем комментарии, в которых рассказывается о том, что делает код, что является менее чем полезным, потому что мы можем легко прочитать код, чтобы увидеть, что он делает. Чего мы не знаем, так это то, соответствует ли код намерениям программистов, или намерение программистов является верным решением основной проблемы!

Другой возможностью будет включение диаграммы, возможно, нумерация действий (овалы) и тестов (параллелограммы), и добавление комментариев в коде, ссылающемся на диаграмму. Это проще, но не так легко следовать (потому что вам нужно постоянно ссылаться на код и диаграмму).

К сожалению, очень часто опускается часть документации ( "Я сделаю это позже, когда у меня будет больше времени" ), и просто проверяю, работает ли код для правильного ввода . Часто невозможно полностью протестировать код для всех возможных входных данных (хотя этот код настолько прост, что может быть), поэтому многие ошибки остаются незамеченными. Без документации, чтобы человек мог проверить код и попытаться оценить, является ли он логически корректным (то есть, что «блок-схема» или конечный автомат, который он реализует, является правильной), и является ли Код соответствует рабочей модели или нет, ошибки обнаруживаются только при укушении ими на практике. Что противно.

Конечные автоматы являются ярким примером того, насколько важны комментарии (и документация), но они действительно применимы ко всему написанному вами коду. Научитесь пытаться описывать свои аргументы (модель решения) и намерения (то, что вы хотите, чтобы код выполнял) в своих комментариях и писать множество комментариев с самого начала. Это очень трудно привыкнуть позже; Я лично все еще борюсь с этим, после десятилетий программирования. Если впоследствии выясняется, что комментарий не нужен, его удаление занимает менее доли секунды; но если он объясняет важную причуду или сложную деталь, которую мы, люди, обычно не замечаем, это может сэкономить часы, дни или даже недели времени разработчика позже.

Это также означает, что практика комментирования неиспользуемого кода не очень полезна, потому что реальные комментарии очень быстро расходятся с кодом, который видит компилятор (или интерпретатор). Вместо этого научитесь использовать систему контроля версий для своих источников; Я рекомендую git . Он доступен практически для всех операционных систем (см. здесь ), и вы можете использовать его как локально на вашем компьютере, так и для распределенных проектов, если вы хотите поместить свой код на GitHub или аналогичный сервисы (или даже настроить свой собственный сервер git). Таким образом, вы можете синхронизировать код и комментарии; и при изменении вашего кода вы можете отдельно описать причины этих изменений (changesets). После того, как вы освоите его, вы обнаружите, что это не обременительно, но фактически ускоряет разработку кода!

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

Мало проблем здесь.

  1. Неинициализированные локальные переменные.

    int i = 0; // loop counter int size = 0; // size of array

  2. Вы проверяете a и t символы, которые еще не прочитаны.

    Следовательно, проверьте наличие t в текущем введенном символе, если оно совпадает, затем проверьте a и c в предыдущих введенных символах, как показано ниже.

    if(i>=2 && input[i-2]=='c' && input[i-1]=='a' && input[i]=='t') // switching characters
    {
        input[i]='g'; input[i-1]='o'; input[i-2]='d';
    }
    
0 голосов
/ 30 октября 2018

Киран упоминает в своем ответе, что «Вы проверяете символы a и t, которые еще не прочитаны» . Вы можете обойти это, используя argc и argv.

Это моя версия вашей программы с использованием argc и argv. Вы заметите, что это также предотвращает ограничение входного буфера (т. Е. Нет input[20]).

#include <stdio.h>

int main(int argc, char **argv)
{
    int i = 0, j = 0;

    for(i=1; i<argc; i++)
    {
      while(argv[i][j] != '\0')
      {
        if(argv[i][j] == 'c')
        {
          if(((argv[i][j+1]) == 'a') && (argv[i][j+2] == 't'))
          {
            argv[i][j] = 'd';
            argv[i][j+1] = 'o';
            argv[i][j+2] = 'g';
          }
        }
        printf("%c", argv[i][j]);
        j++;
      }
      j=0;
      printf(" ");
    }
    printf("\n");
    return 0;
}

Пример ввода и вывода:

$ ./test my favourite cartoon when i was a kid was catdog
my favourite cartoon when i was a kid was dogdog 

$ ./test i used to love cats but now i love dogs
i used to love dogs but now i love dogs 

PS: На случай, если вы родились слишком поздно или не смотрели много мультфильмов по телевизору; вот ссылка: https://en.wikipedia.org/wiki/CatDog.

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

Вы пытаетесь получить доступ к input[i], input[i + 1] и input[i + 2].
Использование:

while (input[i + 2] && input[i + 2] != '\n')


В вашем случае:

#include <stdio.h>

int main()
{
    int i = 0; // loop counter
    int size = 0; // size of array  
    int input[20];
    printf("enter text here\n");  

    while((input[i] = getchar()) != '\n' && i < 19) // input text to the array
    {
/*        if(input[i]=='c' && input[i+1]=='a' && input[i+2]=='t') // switching characters
        {
            input[i]='d'; input[i+1]='o'; input[i+2]='g'; 
        }
*/

        i++; 
        size++;  
    }
    input[i] = 0;//ALWAYS null-terminate arrays.
    if (i >= 2);
        while (input[i + 2]) {
            if (input[i] == 'c' && input[i + 1] == 'a' && input[i + 2] == 't') {
                input[i] = 'd';
                input[i + 1] = 'o';
                input[i + 2] = 'g';
            }
        }
    }

    i=0; // reset for next loop

    while(i < size) // printing the text out ofthe arry 
    {
        putchar(input[i]); 
        i++;
    }

    printf("\n"); 
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...