C中的Pangram使用函数

Pangram in C using functions

当我输入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;
}

如果我理解了您要执行的操作,那么这些嵌套循环

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 && c != tolower( ( unsigned char )*q ) ) ++q;
            
            if ( q == p ) ++ count;
        }
    }
    
    return count;
}

int main(void) 
{
    printf( "%zu\n", 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] != '[=13=]'; i++ )
    {
        if ( isalpha( ( unsigned char ) s[i] ) )
        {
            char c = tolower( ( unsigned char )s[i] );
            
            size_t j = 0;
            while ( j != i && c != tolower( ( unsigned char )s[j] ) ) ++j;
            
            if ( j == i ) ++count;
        }
    }
    
    return count;
}

简单的解决方案?

这是一个简单的解决方案,我猜你可能只想知道它是还是不是 pangram,所以我将你的函数更改为 boolean 一个:

#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'表示Dec中小写字母的范围小数,在ASCII table:

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

tolowerarr[j] character 转换为小写 and 然后将其与 i:

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

stdbool.h 被引入用于 bool aka boolean:

#include <stdbool.h>

将我自己限制为纯 ASCII,您可以创建一个简单的数组,每个字母一个元素,每个元素初始化为零。然后循环遍历字符串,并将每个字母转换为数组的索引并增加相应的元素值。

完成输入字符串后,您将遍历数组,并为每个非零值增加一个计数器,return。

也许是这样的:

#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] != '[=10=]'; ++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 = %d\n", counter);
}

使用您的示例输入 (The quick brown fox jumps over the lazy dog) 它输出

Counter = 26

这只会对输入字符串进行一次传递,然后对 letters 数组进行一次传递。没有嵌套循环,没有多次遍历输入字符串。

如果我们假设 8 位字符并且可以在堆栈上临时分配 256 个字节,那么这既可读、紧凑又相当高效:

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

256 字节的清零看起来效率不高,但主流的 x86 编译器 运行 在 16 条指令中。此函数也不假设 'a''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!='[=11=]'; str++)
  {
    used[*str]=1;
  }
  return memchr(&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 pangram\n", test_cases[i], is_pangram(test_cases[i])?"":"not ");
  }

  return 0;
}