当function
和match
陈述中的条款不相互排斥时,该命令显然很重要。但是当条款 互斥时,它们可以按任何顺序书写。例如,为了找到列表中的最小元素,以下是功能上等同的:
let rec minElt =
function
| [] -> failwith "empty list"
| [x0] -> x0
| x0::xtl -> min x0 (minElt xtl)
let rec minElt =
function
| [x0] -> x0
| x0::xtl -> min x0 (minElt xtl)
| [] -> failwith "empty list"
我更喜欢风格上的第一个,因为模式按大小的顺序列出/基本情况是第一个。但是第二个有什么优势吗?特别是,第二个更有效,因为在正常评估过程中永远不会检查例外情况吗?
答案 0 :(得分:6)
我认为没有任何惯用的风格。我会首先专注于使代码可读和可理解 - 我认为这取决于个人喜好,但我想你可以写:
x::xs
)我想我通常倾向于编写模式匹配(因为这是我考虑可能情况的顺序)。
我不会太担心性能。出于好奇,我测试了你的功能。我在长度为1到100的列表上调用了1000次(这是100000次迭代),第一次是大约895ms,而第二次是878ms,所以差异是2%。听起来不像是对可读性有影响的东西(这是在F#Interactive中,因此差异可能更小)。
答案 1 :(得分:5)
我通常首先使用递归函数的完成情况。这通常是0
案例或1
案例。接下来我把recurses的案例(假设只有一个),然后是任何特殊情况。
这背后的原因是它是我理解归纳推理的方式:即我理解基本情况(递归如何结束),后面是如何使用该基本情况来证明算法的正确性(递归如何到达结束)。我把特殊情况放在最后,因为它们不会增加我对递归背后逻辑的理解。如果你从参数的中间开始而不是从最后开始(并且由于递归没有明确定义的起点,你不能很好地从那里开始),递归更难以理解。
这种推理在很大程度上依赖于函数式编程的数学基础,所以如果这不是你如何处理函数式编程,那么也许其他一些顺序对你来说更有意义。