C 中的嵌套 for 和 if 循环
Nested for and if loops in C
我一直在学习哈佛的 CS50 课程,我有一个关于嵌套 for 和 if 循环的设计和使用的问题。我已经提交了一个问题集,现在我只是在进行“post-mortem”,看看我是否可以更有效地编写它。特别是,我有一个接受 2 个参数的函数:用户的一段文本和一个 26 字母的密码密钥。然后通过用相应的密码密钥值替换每个字符,将明文转换为密文。
我想知道我在这里写的方式是否会被认为是糟糕的设计?多层的 for 和 if 循环对我来说似乎很笨重。 (特别是为每个字符遍历我的字母字符串,然后将该字符重新分配给相应的密码密钥,看起来很复杂?)
谢谢,
// function for converting plain text to cipher text
string substitution(string text, string cipher)
{
string alphabet = ("abcdefghijklmnopqrstuvwxyz");
// for each character:
for (int i = 0, n = strlen(text); i < n; i++)
{
// check if character is in the alphabet:
if ((text[i] >= 'a' && text[i] <= 'z') || (text[i] >= 'A' && text[i] <= 'Z'))
{
// find position in alphabet by index, then convert to same index in the cipher string
for (int j = 0; j < 26; j++)
{
if (text[i] == alphabet[j])
{
text[i] = cipher[j];
break;
}
if (text[i] + 32 == alphabet[j])
{
text[i] = cipher[j] - 32;
break;
}
}
}
}
return text;
}
你用它来检查 text[i]
是否是字母表而不是 isalpha()
,所以我假设你将程序的目标环境限制为字母表的字符代码是连续的(如 ASCII) .
if ((text[i] >= 'a' && text[i] <= 'z') || (text[i] >= 'A' && text[i] <= 'Z'))
在此限制下,您可以简单地减去'a'
或'A'
来获得字符的索引,而无需使用循环来查找字符。
这表示内循环部分
for (int j = 0; j < 26; j++)
{
if (text[i] == alphabet[j])
{
text[i] = cipher[j];
break;
}
if (text[i] + 32 == alphabet[j])
{
text[i] = cipher[j] - 32;
break;
}
}
可以写成
if (text[i] >= 'a' && text[i] <= 'z')
{
text[i] = cipher[text[i] - 'a'];
}
else
{
text[i] = cipher[text[i] - 'A'] - 32;
}
您可以使用 strchr
函数压缩字符搜索,它...与您做的一样。那里的效率提高不多。
要计算索引,您可以使用直接减法:
// check if character is in the alphabet:
if ((text[i] >= 'a' && text[i] <= 'z') {
j = text[i]-'a';
text[i] = cipher[j];
} else if ((text[i] >= 'A' && text[i] <= 'Z') {
j = text[i]-'A';
text[i] = cipher[j] + 32;
}
您还可以预先计算所有 255 个可能的 char 值的 table(排除零),如果不能对字符进行加密,则将值设置为零,否则设置为密码值。
然后:
for (i = 0; text[i]; i++) {
if (table[text[i]]) {
text[i] = table[text[i]];
}
}
如果您需要对长文本进行加密,这当然是有意义的,否则您在计算 table 上花费的时间比使用它要多。
此外,由于在许多体系结构上分支比简单分配更昂贵,请考虑 MikeCAT 的出色建议,即“非密码”字符被自己替换,因此 text[i] = table[text[i]]
实际上什么都不做:
for (i = 0; text[i]; i++) {
text[i] = table[text[i]];
}
这样做应该更容易一些
string substitution(string text, string cipher)
{
// for each character:
for (int i = 0, n = strlen(text); i < n; i++)
{
// check if character is in the alphabet:
if (text[i] >= 'a' && text[i] <= 'z')
{
text[i] = cipher[text[i] - 97]; // index of 'a' in alphabet is 97
}
else if (text[i] >= 'A' && text[i] <= 'Z'))
{
text[i] = cipher[text[i] - 65] - 32; // index of 'A' in alphabet is 65
}
}
return text;
}
如果您要对字符代码顺序和关系(例如 upper/lowercase 转换)做出假设,那么 alphabet
数组就没有什么用处。它的唯一目的可能是将代码与平台定义的字符集排序分离。
在实践中,您的假设对于您可能遇到的任何平台都有效可能是一个公平的赌注,但如果您要定义 alphabet
那么您也许应该保持一致并使其余的独立于平台的代码 character-set。为此,您应该使用 ctype.h
header。下面的代码做到了这一点,还消除了一些其他问题,例如“幻数”。
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
// function for converting plain text to cipher text
string substitution(string text, string cipher)
{
static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
static const size_t ALPHABET_LEN = sizeof(alphabet) / sizeof(*alphabet) ;
// for each character:
size_t text_len = strlen( text ) ;
for( int i = 0; i < text_len; i++ )
{
// check if character is in the alphabet:
if( isalpha( text[i] ) )
{
// find position in alphabet by index, then convert to same index in the cipher string
for (int j = 0; j < ALPHABET_LEN; j++)
{
bool is_upper = isupper( text[i] ) ;
if( !is_upper && text[i] == alphabet[j])
{
text[i] = cipher[j];
break;
}
else if( is_upper && tolower(text[i]) == alphabet[j])
{
text[i] = toupper( cipher[j] ) ;
break;
}
}
}
}
return text;
}
这也许是一个见仁见智的问题,但许多编码标准只允许 break
作为 switch-case 分隔符。 break
和 continue
用于中止循环会产生糟糕且混乱的结构和控制流,并且会使调试变得更加复杂,因为控制流可以“跳过”break-points。避免使用 break
:
bool match = false ;
for (int j = 0; !match && j < ALPHABET_LEN; j++)
{
bool is_upper = isupper( text[i] ) ;
if( !is_upper && text[i] == alphabet[j])
{
text[i] = cipher[j];
match = true ;
}
else if( is_upper && tolower(text[i]) == alphabet[j] )
{
text[i] = toupper( cipher[j] ) ;
match = true ;
}
}
我一直在学习哈佛的 CS50 课程,我有一个关于嵌套 for 和 if 循环的设计和使用的问题。我已经提交了一个问题集,现在我只是在进行“post-mortem”,看看我是否可以更有效地编写它。特别是,我有一个接受 2 个参数的函数:用户的一段文本和一个 26 字母的密码密钥。然后通过用相应的密码密钥值替换每个字符,将明文转换为密文。
我想知道我在这里写的方式是否会被认为是糟糕的设计?多层的 for 和 if 循环对我来说似乎很笨重。 (特别是为每个字符遍历我的字母字符串,然后将该字符重新分配给相应的密码密钥,看起来很复杂?)
谢谢,
// function for converting plain text to cipher text
string substitution(string text, string cipher)
{
string alphabet = ("abcdefghijklmnopqrstuvwxyz");
// for each character:
for (int i = 0, n = strlen(text); i < n; i++)
{
// check if character is in the alphabet:
if ((text[i] >= 'a' && text[i] <= 'z') || (text[i] >= 'A' && text[i] <= 'Z'))
{
// find position in alphabet by index, then convert to same index in the cipher string
for (int j = 0; j < 26; j++)
{
if (text[i] == alphabet[j])
{
text[i] = cipher[j];
break;
}
if (text[i] + 32 == alphabet[j])
{
text[i] = cipher[j] - 32;
break;
}
}
}
}
return text;
}
你用它来检查 text[i]
是否是字母表而不是 isalpha()
,所以我假设你将程序的目标环境限制为字母表的字符代码是连续的(如 ASCII) .
if ((text[i] >= 'a' && text[i] <= 'z') || (text[i] >= 'A' && text[i] <= 'Z'))
在此限制下,您可以简单地减去'a'
或'A'
来获得字符的索引,而无需使用循环来查找字符。
这表示内循环部分
for (int j = 0; j < 26; j++)
{
if (text[i] == alphabet[j])
{
text[i] = cipher[j];
break;
}
if (text[i] + 32 == alphabet[j])
{
text[i] = cipher[j] - 32;
break;
}
}
可以写成
if (text[i] >= 'a' && text[i] <= 'z')
{
text[i] = cipher[text[i] - 'a'];
}
else
{
text[i] = cipher[text[i] - 'A'] - 32;
}
您可以使用 strchr
函数压缩字符搜索,它...与您做的一样。那里的效率提高不多。
要计算索引,您可以使用直接减法:
// check if character is in the alphabet:
if ((text[i] >= 'a' && text[i] <= 'z') {
j = text[i]-'a';
text[i] = cipher[j];
} else if ((text[i] >= 'A' && text[i] <= 'Z') {
j = text[i]-'A';
text[i] = cipher[j] + 32;
}
您还可以预先计算所有 255 个可能的 char 值的 table(排除零),如果不能对字符进行加密,则将值设置为零,否则设置为密码值。
然后:
for (i = 0; text[i]; i++) {
if (table[text[i]]) {
text[i] = table[text[i]];
}
}
如果您需要对长文本进行加密,这当然是有意义的,否则您在计算 table 上花费的时间比使用它要多。
此外,由于在许多体系结构上分支比简单分配更昂贵,请考虑 MikeCAT 的出色建议,即“非密码”字符被自己替换,因此 text[i] = table[text[i]]
实际上什么都不做:
for (i = 0; text[i]; i++) {
text[i] = table[text[i]];
}
这样做应该更容易一些
string substitution(string text, string cipher)
{
// for each character:
for (int i = 0, n = strlen(text); i < n; i++)
{
// check if character is in the alphabet:
if (text[i] >= 'a' && text[i] <= 'z')
{
text[i] = cipher[text[i] - 97]; // index of 'a' in alphabet is 97
}
else if (text[i] >= 'A' && text[i] <= 'Z'))
{
text[i] = cipher[text[i] - 65] - 32; // index of 'A' in alphabet is 65
}
}
return text;
}
如果您要对字符代码顺序和关系(例如 upper/lowercase 转换)做出假设,那么 alphabet
数组就没有什么用处。它的唯一目的可能是将代码与平台定义的字符集排序分离。
在实践中,您的假设对于您可能遇到的任何平台都有效可能是一个公平的赌注,但如果您要定义 alphabet
那么您也许应该保持一致并使其余的独立于平台的代码 character-set。为此,您应该使用 ctype.h
header。下面的代码做到了这一点,还消除了一些其他问题,例如“幻数”。
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
// function for converting plain text to cipher text
string substitution(string text, string cipher)
{
static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
static const size_t ALPHABET_LEN = sizeof(alphabet) / sizeof(*alphabet) ;
// for each character:
size_t text_len = strlen( text ) ;
for( int i = 0; i < text_len; i++ )
{
// check if character is in the alphabet:
if( isalpha( text[i] ) )
{
// find position in alphabet by index, then convert to same index in the cipher string
for (int j = 0; j < ALPHABET_LEN; j++)
{
bool is_upper = isupper( text[i] ) ;
if( !is_upper && text[i] == alphabet[j])
{
text[i] = cipher[j];
break;
}
else if( is_upper && tolower(text[i]) == alphabet[j])
{
text[i] = toupper( cipher[j] ) ;
break;
}
}
}
}
return text;
}
这也许是一个见仁见智的问题,但许多编码标准只允许 break
作为 switch-case 分隔符。 break
和 continue
用于中止循环会产生糟糕且混乱的结构和控制流,并且会使调试变得更加复杂,因为控制流可以“跳过”break-points。避免使用 break
:
bool match = false ;
for (int j = 0; !match && j < ALPHABET_LEN; j++)
{
bool is_upper = isupper( text[i] ) ;
if( !is_upper && text[i] == alphabet[j])
{
text[i] = cipher[j];
match = true ;
}
else if( is_upper && tolower(text[i]) == alphabet[j] )
{
text[i] = toupper( cipher[j] ) ;
match = true ;
}
}