回到倒计时问题(countdown problem)
回忆:“给定一系列数字和一个目标数字,尝试要构造其值为目标的表达式,请将使用加法、减法或加法从序列中选择一个或多个数字,乘法、除法和括号。”
基本解决方案:
•获取数字序列的所有可能组合
•在所有合理位置插入运算符和括号
•将评估值返回到目标值
所有可能组合
获取所有可能的子列表
• 对于列表中的第一项,获取从该点开始的所有子列表,从该点开始的子列表: item :(子列表的其余部分)然后对列表的其余部分执行相同操作
• 对于每个子列表,获取该列表的所有可能排序
• 计算每个子列表的所有可能排列,取子列表中的第一项,并在所有可能的位置尝试它子列表其余部分的排列每个可能的位置:在第一个位置,然后在第一个位置之后的所有可能位置
这个图非常非常非常重要!
你的设计应该包括……
• 数据结构定义
• 功能规格:针对每个功能:论据是什么?它返回什么?什么叫它,它叫什么?
• 考虑使用层次图(如上图)
• 向下处理层次结构,直到它变得微不足道
• 先设计再编码
• 在这个阶段,可能并不是所有的细节都已经到位,但我们已经掌握了所需功能的基本概念
• 现在我们可以开始实施了。我们可能需要额外的奇数(odd)功能,但我们知道基本结构。
从底层工作
• 一旦你确定了你需要什么功能,实施他们自下而上,在你去的时候测试每一个
• 自上而下实施是个坏主意,因为您只会实现所有功能后即可进行测试
实施时顺序是:
• 从数据类型和相关函数开始(显示和评估
表达式)。
• 然后是分解列表的简单内容(不依赖于其他
功能)
• 然后那些使用上述函数的函数
When implementing:
• Start with the data type and associated functions (to show and evaluate the expressions).
• Then the simple stuff to break up lists (that doesn’t depend on other functions)
• Then those functions that use the above functions
懒惰的评价
• Haskell 有一个惰性求值策略:函数的参数只求值
需要时 - 按需提供。
• 相反的策略称为热切评估(eager evaluation)。
• 惰性评估之所以有意义,有以下几个原因:
• 效率,
• 避免没有意义的计算,
• 允许无限的数据结构。
• 它还影响编程风格。
效率
• 如果一个函数有多个由不同模式触发的方程,同样的原则也适用:Haskell 通过按顺序排列方程,进行足够的评估以能够决定图案是否合适。
• 如果函数在where 中定义了本地绑定,那么这些也是只有在需要时才会对其进行评估。
避免无效评估
• 惰性求值还可以帮助您避免进行不符合要求的计算,例如除以零。
Haskell 提供了一种强制执行此操作的方法
• 投入$!在一个论点之前强制它的评估在函数被调用
• 这样做的主要原因是为了提高空间(内存)Haskell 程序的性能。