Панграмма на языке Си с использованием функций

#c #c-strings #counting #function-definition #pangram

Вопрос:

Когда я ввожу данные The quick brown fox jumps over the lazy dog , выводится следующая программа not a pangram . Тем не менее, я ожидаю s , что мне будет 26 лет, и printf("pangram") меня казнят. Что я делаю не так?

 #include <ctype.h>
#include <stdio.h>
#include <string.h>

char findpan(char arr[]) {
    int i, j, count = 0;
    for (i = 0; i < strlen(arr); i  ) {
        if (isalpha(arr[i]))
            count  ;
    }
    for (i = 0; i < strlen(arr); i  ) {
        for (j = i   1; j < strlen(arr); j  ) {
            if (arr[i] == arr[j])
                count--;
        }
    }
    return (count);
}

int main() {
    int s;
    char str[60];
    fgets(str, 60, stdin);
    s = findpan(str);
    if (s == 26)
        printf("pangram");
    else
        printf("not a pangram");
    return 0;
}
 

Комментарии:

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

2. s является -5 . Проблема в алгоритме. Должны ли вы пройти тест на 0 ? и имейте в виду, что если в тексте есть 4 «о», вы вычитаете 6, а не 3.

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

4. Обратите внимание, что в вашем алгоритме вы «отсчитываете» любые дублированные символы, а не только альфа, поэтому дублирование пробелов count также приводит к уменьшению.

Ответ №1:

Если я понял, что вы пытаетесь сделать, то эти вложенные циклы

 for (i = 0; i < strlen(arr); i  ) {
    for (j = i   1; j < strlen(arr); j  ) {
        if (arr[i] == arr[j])
            count--;
    }
}
 

неверны. Давайте предположим, что у вас есть строка «AAA». Таким образом, после предыдущего цикла количество будет равно 3.

Теперь после этих вложенных циклов количество будет равно 0 вместо 1. То есть, когда i = 0, тогда для j = 1 и j = 2 arr[j] равно arr[i]. Таким образом, количество будет уменьшено в два раза. Когда i = 1, тогда для j = 2 снова arr[j] = arr[i], и количество будет уменьшено еще раз.

Также, похоже, вам следует игнорировать случаи букв.

Я могу предложить следующую реализацию функции, как показано в демонстрационной программе ниже.

 #include <stdio.h>
#include <ctype.h>

size_t findpan( const char *s )
{
    size_t count = 0;
    
    for ( const char *p = s; *p;   p )
    {
        if ( isalpha( ( unsigned char ) *p ) )
        {
            char c = tolower( ( unsigned char )*p );
            
            const char *q = s;
            while ( q != p amp;amp; c != tolower( ( unsigned char )*q ) )   q;
            
            if ( q == p )    count;
        }
    }
    
    return count;
}

int main(void) 
{
    printf( "%zun", findpan( "The quick brown fox jumps over the lazy dog" ) );
    
    return 0;
}
 

Вывод программы является

 26
 

Без использования указателей функция может выглядеть следующим образом

 size_t findpan( const char *s )
{
    size_t count = 0;
    
    for ( size_t i = 0; s[i] != ''; i   )
    {
        if ( isalpha( ( unsigned char ) s[i] ) )
        {
            char c = tolower( ( unsigned char )s[i] );
            
            size_t j = 0;
            while ( j != i amp;amp; c != tolower( ( unsigned char )s[j] ) )   j;
            
            if ( j == i )   count;
        }
    }
    
    return count;
}
 

Комментарии:

1. Большое вам спасибо, я понял свою ошибку. Однако я еще не знаком с указателями и т. Д., Поэтому я не смогу реализовать предложенный вами код, но я определенно попытаюсь изменить логику и решить проблему с помощью другого подхода. Еще раз спасибо

2. @ACHALKAMBOJ Вообще нет.:) Я обновил свой ответ реализацией функции, которая не использует указатели.

3. Еще раз спасибо. Переполнение стека-это потрясающе :)))))))))))))))))

Ответ №2:

Простое Решение?

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

 #include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

bool findpan(char arr[]) {
    int i,j;
    for (i = 'a'; i < 'z';   i) { // goes through the alphabet
        for (j = strlen(arr); j > 0; j--) // goes through the arr[] 
            if (tolower(arr[j]) == i) // checks if the letter exists
                break; // breaks the inner for-loop if letter found
          
        if (j == 0) // if letter not found
            return false;  
    }
    return true;
}

int main() {
    bool isPangram;
    char str[60];
    
    fgets(str, 60, stdin);
    isPangram = findpan(str);
    
    if (isPangram)
        printf("pangram");
    else
        printf("not a pangram");
    return 0;
}
 

Объяснение

«a»«z» представляют диапазон строчных букв в десятичных числах в таблице ASCII:

 for (i = 'a'; i < 'z';   i) 
 

tolower преобразует arr[j] символ в нижний регистр, а затем сравнивает его с i:

 if (tolower(arr[j]) == i)
 

stdbool.h вводится для использования bool aka bool ean:

 #include <stdbool.h>
 

Комментарии:

1. Привет , большое спасибо за помощь. У меня было одно сомнение , где находится заголовочный файл stdbool.h, используемый в программе. И разве нам не нужно использовать if(isPanagram=true) ??? Еще раз спасибо. :))

2. @ACHALKAMBOJ я думаю, что теперь я более четко изложил это в разделе пояснений 😀

3. Спасибо тебе ооочень много.

4. Пожалуйста, старайтесь избегать магических чисел .

5. @GiorgosXou Поскольку вы ограничиваетесь строчными буквами ASCII, почему бы не использовать сами символы, как в for (i = 'a'; i < 'z'; i) ?

Ответ №3:

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

Как только вы закончите с входной строкой, вы пройдете по массиву, увеличите счетчик для каждого ненулевого значения и вернете его.

Возможно, что-то вроде этого:

 #include <stdio.h>
#include <ctype.h>

int main(void)
{
    char input[512];

    if (!fgets(input, sizeof input, stdin))
        return 1;  // Failed to read input

    int letters[26] = { 0 };  // 26 letters in the English alphabet

    for (unsigned i = 0; input[i] != '';   i)
    {
        if (isalpha(input[i]))
        {
            // Limiting myself to e.g. plain ASCII here
              letters[tolower(input[i]) - 'a'];
        }
    }

    // Count the number of non-zero elements in the letters array
    unsigned counter = 0;
    for (unsigned i = 0; i < 26;   i)
    {
        counter  = letters[i] != 0;
    }

    // Print result
    printf("Counter = %dn", counter);
}
 

С помощью вашего примера ввода ( The quick brown fox jumps over the lazy dog ) он выводит

Счетчик = 26

Это делает только один проход по входной строке, а затем один проход по letters массиву. Никакого вложенного цикла, никаких многократных проходов по входной строке.

Комментарии:

1. если вы идете на оптимизацию процессора, то почему бы вам просто не if(letters[tolower(input[i]) - 'a'] == 0){counter ; letters[tolower(input[i]) - 'a'];}

2. @GiorgosXou я знаю, но делать что-то подобное Counter = letters[tolower(input[i]) - 'a'] == 1; -значит поднимать это на уровень, который даже мне не нравится.

Ответ №4:

Если мы примем 8-битные символы и сможем временно выделить 256 байт в стеке, то это будет читабельно, компактно и довольно эффективно:

 bool is_pangram (const char* str)
{
  char used [256]={0};
  for(; *str!=''; str  )
  {
    used[*str]=1;
  }
  return memchr(amp;used['a'], 0, 26)==NULL; // 26 letters in the alphabet
}
 

Обнуление 256 байтов может показаться неэффективным, но основные компиляторы x86 выполняют это в 16 инструкциях. Эта функция также не предполагает смежности 'a' to 'z' . Чтобы добавить поддержку верхнего регистра, просто сделайте used[tolower(*str)]=1; это, хотя это может привести к большому разветвлению.

Тестовый код:

 #include <stdio.h>
#include <stdbool.h>
#include <string.h>

bool is_pangram (const char* str)
{
  char used [256]={0};
  for(; *str!=''; str  )
  {
    used[*str]=1;
  }
  return memchr(amp;used['a'], 0, 26)==NULL;
}

int main (void) 
{
  const char* test_cases[] = 
  {
    "",
    "hello, world!",
    "the quick brown fox jumps over the lazy dog",
    "the quick brown cat jumps over the lazy dog",
    "junk mtv quiz graced by fox whelps",
    "public junk dwarves hug my quartz fox",
  };

  for(size_t i=0; i<sizeof test_cases/sizeof *test_cases; i  )
  {
    printf(""%s" is %sa pangramn", test_cases[i], is_pangram(test_cases[i])?"":"not ");
  }

  return 0;
}