互斥条款的顺序在函数或匹配表达式中是否重要

Too*_*one 5 f# functional-programming

当条款functionmatch陈述不相互排斥时,该命令显然很重要.然而,当从句相互排斥的,他们可以用任何顺序.例如,要查找列表中的最小元素,以下内容在功能上是等效的:

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"
Run Code Online (Sandbox Code Playgroud)

我更喜欢风格上的第一个,因为模式按大小的顺序列出/基本情况是第一个.但是第二个有什么优势吗?特别是,第二个更有效,因为在正常评估过程中永远不会检查特殊情况吗?

Tom*_*cek 6

我不认为有任何惯用的风格.我会首先专注于使代码可读和可理解 - 我认为这取决于个人喜好,但我想你可以写:

  • 首先是特殊情况(任何需要特殊处理或处理特殊但有效值的事物)
  • 接下来最常见的情况(典型路径,例如x::xs列表)
  • 特殊情况(任何意味着无效输入)

我想这就是我通常倾向于编写模式匹配的方式(因为这是我考虑可能情况的顺序).

我不会太担心性能.出于好奇,我测试了你的功能.我在长度为1到100的列表上调用了1000次(这是100000次迭代),第一次是大约895ms,而第二次是878ms,所以差异是2%.听起来不像是对可读性有影响的东西(这是在F#Interactive中,因此差异可能更小).


N_A*_*N_A 5

我通常首先使用递归函数的完成情况.这通常是0案例或1案例.接下来我把recurses的案例(假设只有一个),然后是任何特殊情况.

这背后的原因是它是我理解归纳推理的方式:即我理解基本情况(递归如何结束),后面是如何使用该基本情况来证明算法的正确性(递归如何到达结束).我把特殊情况放在最后,因为它们不会增加我对递归背后逻辑的理解.如果你从参数的中间开始,那么递归比你最后开始时更难以理解(因为递归没有明确定义的起点,你不能很好地从那里开始).

这种推理在很大程度上依赖于函数式编程的数学基础,所以如果这不是你如何处理函数式编程,那么也许其他一些排序对你来说更有意义.