本课有一些建议,关于如何使设计和调试你的程序变得更容易。你做编程的时间越长,在避免和修复错误上就会变得更好。但在这里,我们会尝试给你一些非常常用及有用的建议。我们会讲解以下技巧:
- 一次建立一个部分
- 动手来解决一些例子
- 在你开始写代码之前先计划好你的代码
- 编写代码
- 动手测试你解决的例子
- 测试额外的例子包括 “边界”和一些随机的情况
这个神奇的公式不会自动解决一切,但它可以节省你的时间,并减少你处理后的问题的数量。
当你遇到一个问题,一些帮助调试它的策略有 使用诊断打印语句 (我们将在以后的课中使用), 使用可视化工具, 仔细阅读代码, 以及编写好的测试。 如果你 在家中运行Python, 你可以使用断点和分步的工具来更好的理解错误。 |
一个 运算法则 意味着一系列的指令。 而计算机程序需要使用特定的语言编写,如Python和JavaScript。algorithm这个词意味着一步一步的指示,可以用语言或图表示。例如,一个甜甜圈食谱是一个算法,其中“量出3杯面粉”一步,“让面团发酵两小时”是另一步。我们会在这节课中开发一个简单的算法。 然后我们会落实这意味着将它转换成一个可以运行的计算机程序。
问题:甜甜圈计算器
Tim Horton's, 一家在加拿大的咖啡连锁店, 销售 timbits, 一种好吃的甜甜圈球:
你必须在接下来的几周里组织几次聚会,你会对每一个聚会订购不同数量的timbits。所以为了你的预算计划,你想做一个程序来计算任意数量timbits的价格。在你当地的分店,都是下面这些价格:
数量 | 价格 |
---|---|
1 | $0.20 |
10 (小盒) | $1.99 |
20 (中盒) | $3.39 |
40 (大盒) | $6.19 |
一次建立一个部分
最终,你想要编写一个程序,它的输入是 在聚会上的人数——P,它的输出是总共的含税支出。在最高的层面上,这个程序有三个部分:
- 计算当有P个人时,你一共需要买多少(T)个甜甜圈球?
- 计算买T个甜甜圈球的价格
- 计算税和总共的花销。
事实上,你可以一个接一个的完成这三个部分。同样,我们也建议你如此进行。在你每进入下一步骤之前,记得测试和修改你已完成的部分,因为越大的程序,越难发现和解决每个错误。在组合成一个完整的程序之前,三个部分分开进行会让你更容易开发和测试。对这节课的其余部分,我们会将重点放在步骤2:计算购买T个timbits的价格。
动手来解决一些例子
为了能够告诉电脑做某件事,你首先需要能够自己完成它。让我们通过一个例子来练习一下:购买 45 个timbits的价格是多少? 让我们看下表格,我们可以买大盒装(40个),需要$6.19。这让我们还需要单独买 45 – 40 = 5 个timbits, 它们需要额外的 5 * $0.20 = $1.00。总价会是 $6.19 + $1.00 = $7.19.
在你开始写代码之前先计划好你的代码
如果我们想买4个或456个 timbits而不是45个呢?总的来说,我们应该买尽可能多的大盒装,然后再换成中盒装或小盒装,最后才是购买单独的 timbits 来得到正确的总数。
这种算法真的是最优的吗?是的! 你可以看到,买两个中盒装(2*$3.39 = $6.78) 是一个坏主意,因为我们可以更便宜的在一个大盒装中买到 2*20=40 timbits。同样的比较可以在单个与小盒、小盒与中盒之间进行,这些比较可以用来证明这个算法是最优的。 如果价格或者大小不一样,我们可能需要更小心的考虑。 |
我们之前说过,你应该在开始编写代码前,做好计划。你可以:
- 用中文写出一个简短的算法分步描述
- 使用图表或者流程图来显示所有的步骤
- 当下,你应该把重点放在整体框架上,而不是细节。
在我们的例子中, 我们会给出一个文字描述。 我们没有给你显示这个特定程序的流程图,因为它只是从步骤1到步骤2之间的直线流动。带有循环的程序,就像我们随后会看到的,会从流程图中获得更多的效益。
计算出任意数量的timbits价格的算法
- 获取timbits输入的数量。
- 从零开始,跟踪总价格。
- 尽可能的购买大盒装。
- 计算还需要多少timbits。
- 更新总价格。
- 如果可以,购买一个中盒装,并重复步骤A和B。
- 如果可以,购买一个小盒装,并重复步骤A和B。
- 购买单独的timbits并重复步骤B。
- 输出总价
我们把这叫做 伪代码,因为它是一个串联的一步一步的说明,但却没有使用任何真正的编程语言。
编写代码
重申我们的第一条建议, 每次只建立一个部分, 你也可以考虑在从1到7的每一步中写一个单独的代码块。此外,这里还有一件事,我们需要考虑所有的步骤:我们要用什么样的变量?他们的名字,类型和初始值是什么?
让我们用 timbitsLeft
来代表我们还需要购买timbits的数量,同时我们用totalCost
来代表目前为止的总价。我们不需要事先告诉Python他们的类型,但我们可以先计划好: that timbitsLeft
将会是 int
类型, 同时 totalCost
会是 float
类型。 (所以一分钱是 0.01
)。在每一步中,我们还需要一些额外的临时变量,它们只有在我们需要时才会使用到。
第一步和第二部可以各用一行代码来完成:
timbitsLeft = int(input()) # 步骤1: 获取输入 totalCost = 0 # 步骤2: 初始化总价格在步骤3中, 我们需要计算最多可以买多少个大盒装。我们怎样才能计算它呢?因为每一个大盒装有40个timbits,数字
bigBoxes
应该是 timbitsLeft
除以 40
的整数商。 持续记录这些运算成分,使我们有了下面这段代码:
# 步骤3: 尽可能的购买大盒装 bigBoxes = timbitsLeft / 40 totalCost = totalCost + bigBoxes * 6.19 # 更新总价 timbitsLeft = timbitsLeft - 40 * bigBoxes # 计算剩余还需要的timbits数量事实上,我们在这已经犯了一个错误。 捕获错误的最好方法是尽早测试你的代码,并经常测试你的代码!让我们乐观地说,我们现在已经测试了部分代码。我们只需要添加一些打印语句来观察它目前的行为。(除了使用打印语句,你也可以使用我们的可视化工具来检查每一步。)
这并不是我们想要的结果!问题在哪里? 哪一行代码使我们的程序发生了改变? 试着在打开下一段之前找到它。记着,我们在前面自己动手完成了这个问题。
bigBoxes = timbitsLeft / 40
, 因为 bigBoxes
出来的结果是 1.125
,而我们想要它等于 1
。你不可能买一盒的一小部分。一个可行的解决方法(你会在课程7A中看到),就是在整数除法中使用 //
来替代十进制除法的/
。另一个基于我们在第4课中看到的方法是转换 timbitsLeft / 40
为一个 int
,这将删除这个数的小数部分。我们将使用这种方法:于是这行代码变成了 bigBoxes = int(timbitsLeft / 40)
。步骤4和5与步骤3十分相似。另外,因为你会买最多一个中型盒子或一个小盒子,我们可以使用一个if
语句:
if timbitsLeft >= 20: # 步骤4,我们能买一个中盒装么? totalCost = totalCost + 3.39 timbitsLeft = timbitsLeft - 20 if timbitsLeft >= 10: # 步骤5, 我们可以买一个小盒装么? totalCost = totalCost + 1.99 timbitsLeft = timbitsLeft - 20最后,我们需要付20分给每个额外单独购买的timbits并输出最终答案。
totalCost = totalCost + timbitsLeft * 20 # 步骤6 print(totalCost) # 步骤7
测试你完成的例子
这里是整体的程序。我们会做的第一件事是测试当输入为45 时,它是否正确运行。
这里肯定存在一个错误,因为这45个timbits竟然需要100多元!你的最后一份工作就是找到并消除这个错误,并修复隐藏在代码中的另一个错误。它将在下一个部分中被测试。
这里有一个关于Tim Horton的价格的小趣事:有可能你买 T+1个 timbits会比买 T 个timbits更便宜。例如,购买19个timbits需要$3.79, 但购买20个timbits却只需要$3.39。不管怎样, 我们目前只关心怎样最便宜的购买正好 T个timbits, 因此之前的算法是完全正确的。所以你的程序应该输出3.79 当输入是 19 时。 |
测试额外的例子
在计算机科学圈里,我们尝试智能自动化的测试你的代码,以确保你已经正确地解决了这个问题。但在一个真正的编程环境中,应该由你来彻底测试自己的代码。这里有一些有用的测试指南。
- 你可以检查你程序的 每一行代码 来确认这个程序是正常工作的。在timbit 这个问题中,使用输入
10
可以帮助检查我们是否正确的处理了小盒的情况。同样的,输入20
和40
可以帮我们检查中盒和大盒的情况。最后,你应该检查单个timbit的情况(例如使用输入1
)。- 这个可以帮助我们检查像$3.39和$1.99这样的值是否键入正确。
- 记得检查会超出你程序限制的 "边界线" 情况。例如,这个程序可能的最小输入是
0
, 因为你不可能买负数个timbits。我们的程序并没有最大输入值,但你可以尝试一些值,例如39
和41
,他们都非常接近刚够买一大盒的界限。- 这个可以帮助检查你是否不小心打入了
>
当你真正需要打入的是>=
。
- 这个可以帮助检查你是否不小心打入了
- 在一些情况下你可以自动检查每一个 输入,或者检查很多随机的输入来看你的程序是否正常工作。在此网站的内部测试中,我们使用了大量的随机输入。使用
import random
在你程序的开头,接着例如你使用代码random.randint(10, 100)
,它就会产生一个在10
至100
之间的随机整数。
你能够找到所有的错误并通过所有的测试吗? 尝试一下吧!
在Python中,一些计算会产生一些小的错误:
令人惊讶的是,上面这行代码并不会给我们整好0.35 !这是由于Python存储信息的方式造成的。你不用担心这些极小的错误:打分软件会自动无视他们。我们会在课程7B中更详细的说明这件事。 |
饿了吗?你已经准备好进入下节课了!