【问题标题】:F#, how far is it reasonable to go when checking for valid arguments?F#,检查有效参数时走多远是合理的?
【发布时间】:2011-05-09 02:33:01
【问题描述】:

我正在尝试在 F# 中学习一些函数式编程的思维方式,因此我们不胜感激。现在我正在做一个简单的递归函数,它接受一个列表并返回第 i:th 元素。

let rec nth(list, i) =
    match (list, i) with
    | (x::xs, 0) -> x
    | (x::xs, i) -> nth(xs, i-1)

函数本身似乎可以工作,但它会警告我模式不完整。在这种情况下,我不确定当我匹配空列表时会返回什么,因为如果我执行以下操作:

| ([], _) -> ()

整个函数被视为一个以单位为参数的函数。我希望它被视为一个多态函数。

当我在做这件事时,我不妨问一下,在认真开发时,在设计函数时检查有效参数的合理程度。我应该检查所有内容,以防止“滥用”该功能吗?在上面的示例中,我可以例如指定函数来尝试访问列表中大于其大小的元素。我希望我的问题不会太令人困惑:)

【问题讨论】:

    标签: f# arguments pattern-matching unit-type


    【解决方案1】:

    通过查看标准 F# 库,您可以了解很多关于“常规”库设计的信息。已经有一个函数可以满足您的需求,称为 List.nth,但即使您将其作为练习来实现,您也可以检查该函数的行为方式:

    > List.nth [ 1 .. 3 ] 10;;
    System.ArgumentException: The index was outside the range 
      of elements in the list. Parameter name: index
    

    该函数抛出System.ArgumentException 以及一些有关异常的附加信息,以便用户可以轻松找出问题所在。要实现相同的功能,您可以使用invalidArg 函数:

    | _ -> invalidArg "index" "Index is out of range."
    

    这可能比只使用failwith 更好,后者会引发更一般的异常。使用invalidArg时,用户可以检查特定类型的异常。

    正如 kvb 所指出的,另一种选择是返回 option 'a。许多标准库函数同时提供返回option 的版本和引发异常的版本。例如List.pickList.tryPick。因此,在您的情况下,一个好的设计可能是具有两个功能 - nthtryNth

    【讨论】:

      【解决方案2】:

      如果您希望您的函数返回有意义的结果并具有与现在相同的类型,那么您别无选择,只能在其余情况下抛出异常。匹配失败会引发异常,因此您不需要对其进行更改,但您可能会发现最好抛出具有更多相关信息的异常:

      | _ -> failwith "Invalid list index"
      

      如果您认为无效的列表索引很少见,那么这可能已经足够了。但是,另一种选择是更改您的函数,使其返回'a option

      let rec nth = function
      | x::xs, 0 -> Some(x)
      | [],_ -> None
      | _::xs, i -> nth(xs, i-1)
      

      这给调用者带来了额外的负担,他们现在必须明确地处理失败的可能性。

      【讨论】:

        【解决方案3】:

        大概,如果取一个空列表是无效的,你最好只是抛出一个异常?

        一般来说,你应该如何防御的规则并不会因语言而异——我总是遵循这样的准则:如果是公开的,则对验证输入持偏执态度,但如果是私有代码,则可以不那么严格。 (其实如果是大型项目,而且是私有代码,稍微严格一点……基本上严格程度与可能调用您代码的开发人员的数量成正比。)

        【讨论】:

        • “严格程度与可能调用您的代码的开发人员的数量成正比”很棒的总结。
        • 这听起来可能有点陈词滥调,但“坚如磐石”的代码永远不会出错。毕竟,您通常不能保证将来如何使用您的代码或由谁使用。
        • @Daniel,没错,但在开发时间和维护方面,这个决定是有代价的。如果每个方法都如此偏执,那么您最终会得到一个充满样板的代码库,以及缺乏灵感的开发人员。
        猜你喜欢
        • 1970-01-01
        • 2012-10-22
        • 1970-01-01
        • 2011-01-20
        • 1970-01-01
        • 2019-09-27
        • 2023-03-11
        • 2012-06-01
        • 2020-05-24
        相关资源
        最近更新 更多