从问题传递到代码的过程。你是怎么学的?
我正在教/帮助学生编程。
我记得当我开始时以下过程总是对我有帮助;它看起来很直观,我想知道其他人是否也有类似的方法。
- 阅读问题并理解它(当然)。
- 确定可能的"功能"和变量。
- 写下我将如何逐步执行(算法)
- 将其转换为代码,如果我们无法执行某些操作,请创建一个为我们执行的函数并继续前进。
随着时间和实践的发展,我似乎已经忘记了从问题描述转移到编码解决方案的难度,但是,通过应用这种方法,我设法学习了如何编程。
因此,对于像这样的项目描述:
A system has to calculate the price of an Item based on the following rules ( a description of the rules... client, discounts, availability etc.. etc.etc. )
我的第一步是了解问题所在。
然后确定项目,规则变量等。
伪代码类似:
function getPrice( itemPrice, quantity , clientAge, hourOfDay ) : int if( hourOfDay > 18 ) then discount = 5% if( quantity > 10 ) then discount = 5% if( clientAge > 60 or < 18 ) then discount = 5% return item_price - discounts... end
然后将其传递给编程语言。
public class Problem1{ public int getPrice( int itemPrice, int quantity,hourOdDay ) { int discount = 0; if( hourOfDay > 10 ) { // uh uh.. U don't know how to calculate percentage... // create a function and move on. discount += percentOf( 5, itemPriece ); . . . you get the idea.. } } public int percentOf( int percent, int i ) { // .... } }
我们是否采用了类似的方法?..有人教过我们类似的方法吗?还是我们发现了自己(就像我一样:()
解决方案
我做了类似的事情。
- 找出规则/逻辑。
- 弄清楚数学。
- 然后尝试对其进行编码。
经过几个月的努力,它才被内部化了。在遇到需要将其分解的复杂问题之前,我们不会意识到自己已经做到了。
如意算盘可能是解决复杂问题的最重要工具。如有疑问,请假定存在解决问题的功能(首先创建一个存根)。稍后我们将再次使用它进行扩展。
我通过测试驱动的方法。
1.我写下(可以在纸上或者纯文本编辑器上)满足问题需求的测试或者规范列表。
- simple calculations (no discounts and concessions) with: - single item - two items - maximum number of items that doesn't have a discount - calculate for discounts based on number of items - buying 10 items gives you a 5% discount - buying 15 items gives you a 7% discount - etc. - calculate based on hourly rates - calculate morning rates - calculate afternoon rates - calculate evening rates - calculate midnight rates - calculate based on buyer's age - children - adults - seniors - calculate based on combinations - buying 10 items in the afternoon
2.寻找我认为最容易实现的项目并为此编写测试。例如单件物品看起来很简单
该示例使用Nunit和C#。
[Test] public void SingleItems() { Assert.AreEqual(5, GetPrice(5, 1)); }
使用以下方法实现该目的:
public decimal GetPrice(decimal amount, int quantity) { return amount * quantity; // easy! }
然后转到两个项目。
[Test] public void TwoItemsItems() { Assert.AreEqual(10, GetPrice(5, 2)); }
该实现仍通过测试,因此继续进行下一个测试。
3.始终注意复制并删除。当所有测试通过时,我们就完成了,我们再也无法想到任何测试。
这不能保证我们将创建最有效的算法,但是只要我们知道要测试的内容并且所有测试都通过了,就可以保证获得正确的答案。
请记住,如果我们获得5%的折扣,那么再享受5%的折扣,我们就不会获得10%的折扣。相反,我们要支付95%的95%,即90.25%,即9.75%的折扣。因此,我们不应该添加百分比。
对于初学者寻找过程的一本好书:测试驱动开发:通过示例
是的..当我开始的时候,TDD并不存在(或者不那么受欢迎)。 TDD是从问题描述传递到代码的方法吗?...这有点高级吗?我的意思是,当"未来"的开发人员几乎不了解编程语言是什么时,会适得其反吗?
那么从算法到代码的过渡又如何呢?
老式的OO方式:
- 写下问题及其解决方案的描述
- 圈出名词,这些是候选对象
- 在动词周围画框,这些是候选消息
- 将动词与会"做"动作的名词分组;列出需要帮助的任何其他名词
- 看看是否可以使用名词。动词(其他名词)的形式重述解决方案
- 编码
[此方法先于CRC卡,但是它已经使用了20年之久,以至于我不记得自己从哪里学到的]
我从顶部开始,然后一直往下走。基本上,我将从编写一个高级过程开始,勾勒出其中的细节,然后开始填写细节。
说我有这个问题(来自项目欧拉)
The sum of the squares of the first ten natural numbers is, 1^2 + 2^2 + ... + 10^2 = 385 The square of the sum of the first ten natural numbers is, (1 + 2 + ... + 10)^2 = 55^2 = 3025 Hence the difference between the sum of the squares of the first ten natural numbers and the square of the sum is 3025 385 = 2640. Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum.
所以我开始是这样的:
(display (- (sum-of-squares (list-to 10)) (square-of-sums (list-to 10))))
现在,在Scheme中,没有平方和,平方和或者列表至函数。因此,下一步将是构建所有这些。在构建这些功能中的每一个时,我可能会发现我需要进行更多的抽象。我尝试使事情保持简单,以便每个功能只能真正完成一件事。当构建一些可测试的功能时,我为此编写了单元测试。当我开始注意到某些数据的逻辑分组以及作用于它们的函数时,我可能会将其推入对象中。
在学习编程时,我认为TDD没有帮助。稍后,当我们对编程的概念有了一些了解时,TDD会很不错,但是对于初学者来说,拥有一个可以在尽可能短的周转时间内编写代码并查看结果的环境是最重要的。
我会立即从问题陈述转到代码。乱搞。帮助学生了解组成软件/结构算法的不同方式。教导学生改变主意并重新编写代码。尝试并教一些有关代码美感的知识。
一旦他们可以破解代码...。然后在重构方面引入正式重构的思想。然后介绍TDD的想法,作为使该过程更可靠的一种方法。但是只有一次,他们在操纵代码来完成自己想要的事情时就感到很自在。这样一来,能够指定测试就容易一些了。原因是TDD与设计有关。在学习时,我们并不是很在乎设计,而是在乎我们能做什么,我们必须玩什么玩具,它们如何工作,如何将它们组合在一起。一旦有了这种感觉,那么我们就应该考虑设计,那就是TDD何时真正发挥作用。
从那里开始,我将介绍引入设计模式的微模式
我父亲有很多流程图模板,当他第一次教我编程方面的知识时,他经常让我使用。到目前为止,我绘制正方形和菱形以构建如何分析问题的逻辑过程。
自从将TDD引入我后,我就一直很喜欢它。帮助我计划代码,这让我放心,每次修改代码时,所有测试都以"成功"返回,让我知道我今天准时回家!
我认为在编程方面,我知道大约有十二种不同的启发式方法,因此,我有时会按照自己想做的事情逐一列出。开始时,重要的是要知道所需的最终结果,然后尝试向后工作以找到所需的最终结果。
我记得算法类涵盖了其中的一些方式,例如:
- 将其简化为已知问题或者琐碎问题
- 分而治之(MergeSort是此处的经典示例)
- 使用具有正确功能的数据结构(此处以HeapSort为例)
- 递归(了解琐碎的解决方案,并能够简化为这些解决方案)
- 动态编程
组织解决方案并针对特殊情况进行测试,例如如果有人认为L应该是一个数字,那么在编写它之前,我通常会用它来用伪代码测试一下这个想法。
设计模式可以是一组方便的工具集,可用于特定情况,例如需要适配器或者将事物组织到状态或者策略解决方案中的情况。
我认为有一种更好的方法来陈述问题。
与其将其定义为"系统",不如定义用户期望的输入和输出。
"在窗口上,用户应从列表中选择一个项目,然后一个方框应显示其成本。"
然后,我们可以给他一些确定成本的因素,包括样品项目及其最终成本。
(这也是非常类似于TDD的想法)