【问题标题】:Pangram in C using functions在 C 中使用函数的 Pangram
【发布时间】:2021-09-12 22:49:38
【问题描述】:

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

【问题讨论】:

  • 绝对有问题的一件事是您将大写和小写字母视为不同的字母。
  • s-5。问题是算法。你应该测试0 吗?请注意,如果文本中有 4 个 o,则减去 6,而不是 3。
  • 也许是时候学习如何使用 调试器 逐句执行代码,同时监控变量及其值。
  • 请注意,在您的算法中,您“不计算”任何重复的字符,而不仅仅是 alpha,因此重复的空格字符也会导致 count 减少。

标签: c c-strings counting function-definition pangram


【解决方案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 && 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] != '\0'; 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;
}

【讨论】:

  • 非常感谢,我明白我的错误了。但是我还不熟悉指针等,所以我无法实现你建议的代码,但我肯定会尝试改变逻辑并用不同的方法解决问题。再次感谢
  • @ACHALKAMBOJ 完全没有。:) 我用不使用指针的函数实现更新了我的答案。
  • 再次感谢。堆栈溢出是惊人的:)))))))))))))))))
【解决方案2】:

简单解决方案?

这是一个简单的解决方案,我猜你可能只是想知道它是还是不是一个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>

【讨论】:

  • 嗨,非常感谢您的帮助。我有一个疑问,程序中使用的头文件 stdbool.h 在哪里。而且我们不必使用 if(isPanagram=true) ???再次感谢。 :))
  • @ACHALKAMBOJ 我想我现在在“解释”部分下已经说得更清楚了:D
  • 非常感谢。
  • 请尽量避开magic numbers
  • @GiorgosXou 既然您将自己限制为小写 ASCII,为什么不使用字符本身,例如 for (i = 'a'; i &lt; '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] != '\0'; ++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) 输出

计数器 = 26

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

【讨论】:

  • 如果你要进行 CPU 优化,那你为什么不只是 if(letters[tolower(input[i]) - 'a'] == 0){counter ++; ++letters[tolower(input[i]) - 'a'];}
  • @GiorgosXou 我知道,但是像Counter += ++letters[tolower(input[i]) - 'a'] == 1; 这样的操作将它提升到一个水平,即使我并不满意。
【解决方案4】:

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

bool is_pangram (const char* str)
{
  char used [256]={0};
  for(; *str!='\0'; 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!='\0'; 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-11
    • 1970-01-01
    • 1970-01-01
    • 2011-06-05
    • 2021-07-15
    相关资源
    最近更新 更多