一题多解×2(流的概念+递归)

目录

题目一:

解法一:

解法二:

题目二:

解法一:

解法二:


题目一:

编写一个程序,在遇到EOF之前,把输入作为字符流读取。该程序要报告平均每个单词的字母数。不要把空白统计为单词的字母。实际上,标点符号也不应该统计,但是现在暂时不同考虑这么多(如果你比较在意这点,考虑使用ctype.h系列中的ispunct()函数)。

 这是《C Prime Plus》一书中的题8-4,做题前,我们有必要了解一下EOF是什么意思。 

EOF是end of file的缩写,表示"文字流"(stream)的结尾。这里的"文字流",可以是文件(file),也

可以是标准输入(stdin)。

EOF在<stdio.h>中是被定义的一个常量:

#define  EOF (-1)

 更详尽的讲解:1.百科,2.博友.

而第二个概念,字符流IO流的一种,计算机通过IO流来实现对硬盘文件的读和写操作;但我们的

输入一般是通过键盘键入缓冲区存放的,实现的是对缓冲区的写入操作

流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出。

 “把输入作为字符流读取”,这里只是利用一种概念上的相似性完成程序。

现在讲回题目,一般两个词之间我们用一个空格隔开,但最后一个单词我们一般用标点并不用空格

作为结尾,也就是说,单词数=空格数+1,空格数可以用变量sp_iNum表示,即:

int sp_iNum=0;//int sp_iNum={0};

。然后,我们需要记录得到的字母总数,定义一个变量:

int iNum=0;//int iNum={0};

{0}是一个可以给很多种变量初始化的东西,

例如:

char cChar={0};

char cChar[10]={0};

char *file={0};//即char *file=NULL;

接下来,我们定义一个用于接收并暂存字符供检验的容器(一次检验一个字符):

char cChar;

现在我们不将标点统计在总字母数iNum里,那我们就要用到<ctype.h>中的ispunct()函数,

返回值为非零(真)表示c是标点符号,返回值为零(假)表示c不是标点符号。

#include<ctype.h>
#include<stdio.h>
int main()
{
	char cChar;
	cChar = '?';
	int iChar = ispunct(cChar);
	printf("%d", iChar);
	return 0;
}

 结果为:

解法一:

#include<stdio.h>//8-4
#include<ctype.h>
int main()
{
	char cChar = 0;
	int iNum = 0;
	int sp_iNum = 0 ;
	while ((cChar = getchar()) != EOF)
	{
		if (cChar == ' ')
			sp_iNum++;
		else if (ispunct(cChar))
			;
		else if(cChar != ' '&&!ispunct(cChar)&& cChar != '\n')
			iNum++;
		else if (cChar == '\n')
		{
			printf("%.2f\n", iNum*1.0 / (sp_iNum + 1));
			iNum = 0;
			sp_iNum = 0;
		}
	}
}

 Where there is a will,there is a way!

这句话共有28个字母,空格数有7个,故每个单词平均有28/8=3.50个。在我看来,难理解的应

该是我输入了一整句话,为什么不是只有第一个字母'W'才能被赋给cChar呢?

getchar()是stdio.h中的库函数,它的作用是从stdin流中读入一个字符,也就是说,如果stdin有数据的话不用输入它就可以直接读取了,第一次调用getchar()时,确实需要人工的输入,但是如果你输了多个字符,以后的getchar()再执行时就会直接从缓冲区中读取了

以后的getchar()再执行时就会直接从缓冲区中读取了”,这句话就是原因啦。这段话也可以这么理

解:我提前将要装进容器cChar的字符一个个在缓冲区先放好,在第一个字母'W'被装进去并检验后,再将'W'倒掉,然后是'h',接着是'e'······直到最后一个字符。

解法二:

#include <stdio.h>
#include <ctype.h>
int main(void)
{
	int ch, i = 0, j = 0;
	float sum;

	printf("Please enter a word the inspection of the average number of letters and words several letters\n");
	printf("After input press CTRL + Z to calculate:\n");

	while ((ch = getchar()) != EOF)
	{
		if (ch >= 'a'&&ch <= 'z' || ch >= 'A'&&ch <= 'Z')
			i++;
		else if (ch == 32)
			j++;
	}
	sum = i*1.0 / (j + 1);
	printf("Letters have %d\naverage number of letters have %0.2f\n", i, sum);
	return 0;
}

原文为CSDN博主「一线生」的原创文章,上面的代码较原文有改动,原文链接

原文代码有些小错误,如:

if (ch >= 'A'&&ch <= 'z' || ch >= 'A'&&ch <= 'Z')

//应为if (ch >= 'a'&&ch <= 'z' || ch >= 'A'&&ch <= 'Z')

sum = i / (j + 1);//可为sum = i *1.0/ (j + 1);

否则在等式右边的式子自动取整后,才会将值拷贝给sum转换为float类型。

若不改动,设i=11,j=2,则sum=3,而不是6.67

Ctrl+z是将任务中断,挂起的状态。

 具体作用建议看这篇博文:Ctrl+z跟Ctrl+c的区别_绮梦寒宵的博客-CSDN博客_ctrl+z和ctrl+c

题目二:

求n的阶乘。

题目很容易理解,n的阶乘N=1*2*······*(n-1)*n(n>=1)。

解法一:

#include<stdio.h>
int main()
{
	int n,fact=1;
	scanf_s("%d", &n);//Dev-C++:scanf("%d", &n);
	while (n > 1) 
    fact *= n--;
	printf("%d", fact);
	return 0;
}

下面讲解一下这个程序:fact用来存储最终的结果,n即要用来计算阶乘的数字,其中fact要赋予初值为1。

while (n > 1) //while (n >=1)也可以 
    fact *= n--;

fact与未进行自减操作的n相乘,并将结果拷贝到fact中,取代fact中原先的值。

结束的临界条件为n=2,执行完n=2这次后不再执行 fact *= n--;

即fact=2*······*(n-1)*n,若你为n赋值为1,将直接打印出fact的初始值1。

那么我们再想一下如何利用递归函数来解决这个问题:

解法二:

#include<stdio.h>
int facto(int n)
{
	int fact = 1;
	if (n > 1)
		fact = facto(n - 1)*n;
	return fact;
}
int main()
{
	int n,fact;
	scanf_s("%d", &n);//Dev-C++:scanf("%d", &n);
	fact = facto(n);
	printf("%d", fact);
	return 0;
}

难点在于这个facto()函数,其中的语句:

if (n > 1)
        fact = facto(n - 1)*n;

正是facto()递归地调用自身。

没学过递归的同学看这个可能有点懵,不要紧,让我细细道来,帮你开个头。 

在上面的语句中,facto(n-1)意即将n-1(n-1>0的情况下)这个值通过这个facto()函数继续过一遍,

这样的套娃操作会在n=1时结束,届时,会从最小的那个娃娃开始反套娃。

最小的套娃是那个呢?是这个:

facto(1)
{
    int fact = 1;
    if (n > 1)//n=1不执行这两行
        fact = facto(n - 1)*n;
    return fact=1;
}

倒数第二小的是:

facto(2)
{
    int fact = 1;
    if (n > 1)
        fact = facto(1)*2=1*2;//注意,代码不能这么写,这样写只是方便讲解。
    return fact=1*2;
}

倒数第三层是:

facto(3)
{
    int fact = 1;
    if (n > 1)
        fact = facto(2)*3=1*2*3;
    return fact=1*2*3;
}

倒数第四层:

facto(4)
{
    int fact = 1;
    if (n > 1)
        fact = facto(3)*4=1*2*3*4;
    return fact=1*2*3*4;
}

就这样,我们一层层套到最大的娃:

facto(n)
{
    int fact = 1;
    if (n > 1)
        fact = facto(n - 1)*n=1*2*3*4*······*(n-1)*n;
    return fact=1*2*3*4*······*(n-1)*n;
}

然后我们将用下面的返回语句将值拷贝给main()函数中的变量fact

return fact=1*2*3*4*······*(n-1)*n;

最终套娃结束,随着打印出fact的值,程序结束。

注:由于两个fact变量定义的地方不同,所以它们的作用域也不同,不用担心它们会起冲突

通过这个例子,我们可以归结出递归的一般思想:通过不断地求取某种极限,我们可以得到最底层

返回的条件,最终一层层倒退回来。

当然,这只是个人理解,不妥还请读者指正。


我的上一篇博客:一题多解×1

我的下一篇博客:结构体×共用体×枚举类型

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔走的月光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值