唠唠python(4) -- 简化工作

胜兵先胜而后求战,败兵先战而后求胜。 – 孙子兵法

遇到问题

我的女朋友最近生病了,周三下了班就回了家,顺带着还把在公司的一部分工作带了回来。

等我回到家,她已经卧在床上睡一觉了。

过了一会她睡醒了,坐起来跟我说:“工作没做完,你帮我做一下吧。很简单,就是把这张表格里的数字,以10000为单位,上或下取整,填到它下面的格子里。”说罢,就又睡了过去。

怎么计算

我粗略地看了一眼,这是一个excel表格文件,就是下面的这张表格。

待处理表格

简单查看了一下excel的基础知识。这一个一个的格子,叫作“单元格”,哈哈,形象吧。暂时我们只需要这个概念。

这里面有黑色内容的单元格和红色内容的单元格。红色内容的单元格是计算上面的黑色单元格的内容(物料的待收数据)得到的结果。

我们的目标是,输入这个文件,然后让计算机自己经过一系列的计算过程,将表格中的还未计算过的数据按给定的规则计算,将计算结果写到下面一个单元格,并帮我们把处理后表格保存下来。

这个目标实在激动人心!我跟我女朋友确认过,以这次的数据量,如果人工来做,可能需要花费3-4个小时的时间。而对计算机来说,在一分钟内就可以完成,而且可以确保结果的准确性。需要的知识也浅显易懂,如果对相关知识熟练,完全可以在半小时内将我们想要做的事情清楚地告诉计算机。

当然,学习是循序渐进的。在这篇文章中,我们先把目标降低一些:告诉计算机一个待收数量,和一条规则(以10000为单位上取整),让它帮我们计算实际的代收数量。这是我们完成整个目标的第一步,这个技巧也完全可以用在其它类似的工作上。

获取输入

首先,我们需要告诉计算机待收数量。这当然很简单,我们在前面已经说过了。假设待收数量是1234:

1
2
3
>>> 1234
1234
>>>

但是,这样会显得很仓促:1234代表什么呢?我们现在当然知道,它就是我们要求值的数据。但当我们写入到一个文件时,如何把1234传进去呢?

显然,我们可以直接写到文件,比如:

1
print 1234

而此时,如果再重新输入一个数字时,我们就需要打开文件,然后把1234改为新的数字,保存文件,然后运行。修改代码文件很繁琐,而且是很危险的,这还只是一行简单的代码,想想,如果一个代码文件有上百行,我们如何再找到这个数值的位置?会不会小手一抖,把其它行的数据也修改了而自己并没有意识到?繁琐的操作也必然会降低我们的效率。

这时,我们就自然有这么一个需求。

计算机:您好,我最亲爱的某某某,您的待收数量是多少,我来为您计算实际待收数量。

亲爱的某某某:嗯,1234。

计算机:您实际需要待收的数量是10000。

嗯,这就是人们所说的交互,其实就是让人跟计算机的沟通像人与人的沟通一样顺畅。

这时,我们需要知道这么一个东西:

1
2
3
4
>>> int(raw_input("请输入您的待收数量:"))
请输入您的待收数量:1234
1234
>>>

注意,里面的第一个1234是我们自己输入的,后面还跟了一个回车。

int(raw_input("请输入您的待收数量:")),这么大一坨?没关系,先死记下,在之后的文章我们会进一步了解。我们只需要知道,它让计算机询问我们待收数量。

如何计算取整后的值

我们现在用的python是个死脑筋。为什么这么说呢?如果你使用的python是之前文章中链接的python或是系统默认安装的,试试下面这个:

1
2
3
>>> 5 / 2
2
>>>

我们在学校学到的可是5 ÷ 2 = 2.5,现在却得到个2。谁的问题?

语言的问题。语言是有局限的,而当我们输入5 / 2时,python解释给计算机说:“请帮我们计算一下5 ÷ 2的值,取整就可以了,小数点我们就不要了。

语言是在发展的,文章中用的python版本是python2,此时,默认情况下,整数除法的结果是整数,而且是下取整。

现在回到我们的问题:给一个数字,以10000为单位,上取整或下取整。我们假设是上取整,想想应该怎么做呢?

我们把单位缩小,先来看一下以3为单位上取整,我们来取一批数值观察一下。

我们来分别观察一下 待收数量实收数量 的值和它们之间的相互关系格式:

  • 待收数量:1,实收数量:3,待收数量 ÷ 3后下取整:0
  • 待收数量:2,实收数量:3,待收数量 ÷ 3后下取整:0
  • 待收数量:3,实收数量:3,待收数量 ÷ 3后下取整:1
  • 待收数量:4,实收数量:6,待收数量 ÷ 3后下取整:1
  • 待收数量:5,实收数量:6,待收数量 ÷ 3后下取整:1
  • 待收数量:6,实收数量:6,待收数量 ÷ 3后下取整:2
  • 待收数量:7,实收数量:9,待收数量 ÷ 3后下取整:2
  • 待收数量:8,实收数量:9,待收数量 ÷ 3后下取整:2
  • 待收数量:9,实收数量:9,待收数量 ÷ 3后下取整:3

我们发现,待收数量 ÷ 3后下取整 的值会随着 实收数量 增长,但并不完全一致,而是相差 2。如果在 ÷ 3 前先将 待收数量 +2,则结果刚好变成了3倍的关系。如下:

  • 待收数量:1,实收数量:3,(待收数量 + 2) ÷ 3后下取整:1
  • 待收数量:2,实收数量:3,(待收数量 + 2) ÷ 3后下取整:1
  • 待收数量:3,实收数量:3,(待收数量 + 2) ÷ 3后下取整:1
  • 待收数量:4,实收数量:6,(待收数量 + 2) ÷ 3后下取整:2
  • 待收数量:5,实收数量:6,(待收数量 + 2) ÷ 3后下取整:2
  • 待收数量:6,实收数量:6,(待收数量 + 2) ÷ 3后下取整:2
  • 待收数量:7,实收数量:9,(待收数量 + 2) ÷ 3后下取整:3
  • 待收数量:8,实收数量:9,(待收数量 + 2) ÷ 3后下取整:3
  • 待收数量:9,实收数量:9,(待收数量 + 2) ÷ 3后下取整:3

也就是说,实收数量 刚好是 (待收数量 + 2) ÷ 3后下取整的3倍,其中的 2 实际上就是由 3 - 1 得来的。我们用数学公式整理一下现在的这个想法:

1
2
3
实收数量
= (待收数量 + 2) / 3 * 3
= (待收数量 + (3 - 1)) / 3 * 3

注意,其中的 / 符号即我们python语言里的除法,它是下取整的。

类比到10000时的情形,我们的公式会变成下面这个样子:

1
实收数量 = (待收数量 + (10000 - 1)) / 3 * 3

实际上,如果我们可以让计算机整数相除时上取整(而不是下取整),公式就会变得异常简单:n 上取整除以 10000 * 10000,在我们学习了后面的更多知识后就可以做到。现在这个公式(n + (10000 - 1)) / 10000 * 10000虽繁琐,但已是我们通过自己仅有的一点计算机知识所能学到的了。

马儿跑起来

下面,我们将之前的分析总结到一个文本文件里:

1
print 10000 * (n + (10000 - 1)) / 10000

将n替换为提示用户输入待收数量:

1
print (int(raw_input("Tell me the quantity: ")) + (10000 - 1)) / 10000 * 10000

将以上内容保存到a.py,然后输入python a.py并回车,试试看:

1
2
3
$ python a.py
Tell me the quantity: 1234
10000

嗯,不错,可以。

一点遗憾

这是我们迈出的很重要的一点,如果你跟下来并且看懂了,这说明我们已经掌握了把自己的思想描述给计算机的技巧。

但这还不完美,离我们的目标自动处理数据并保存结果还是有一定距离的。多远的距离呢,嗯,可能是4-5篇文章吧。不算远,对吧,假定一篇文章40分钟,加起来也就是3-4个小时的样子。想想我们前面提到的,这些数据手工处理都要3-4个小时,太他妈赚了。

当然,3-4个小时是不够的,我们还需要一些时间消化和遗忘。不过,不管怎么算,这都是人生中很成功的一笔交易。

这篇文章中,还有很多有趣的东西被我一带而过了,留到下一篇我们再一起聊聊吧。

已经晚上23了,洗洗睡了。明天又是崭新的一天,啊哈哈。