F#,检查有效参数时合理的程度是多少?

发布于 2024-10-01 19:17:32 字数 498 浏览 5 评论 0原文

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

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

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

| ([], _) -> ()

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

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

I'm trying to learn a little of the mindset of functional programming in F#, so any tips are appreciated. Right now I'm making a simple recursive function which takes a list and returns the i:th element.

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

The function itself seems to work, but it warns me about an incomplete pattern. I'm not sure what to return when I match the empty list in this case, since if I for example do the following:

| ([], _) -> ()

The whole function is treated like a function that takes a unit as argument. I want it to treat is as a polymorphic function.

While I'm at it, I may as well ask how far is reasonable to go to check for valid arguments when designing a function when developing seriously. Should I check for everything, so "misuse" of the function is prevented? In the above example I could for example specify the function to try to access an element in the list that is larger than its size. I hope my question isn't too confusing :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

最终幸福 2024-10-08 19:17:32

通过查看标准 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.nth 异常。 ArgumentException 带有一些有关异常的附加信息,以便用户可以轻松找出出了什么问题。要实现相同的功能,您可以使用 invalidArg 函数:

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

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

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

You can learn a lot about the "usual" library design by looking at the standard F# libraries. There is already a function that does what you want called List.nth, but even if you're implementing this as an exercise, you can check how the function behaves:

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

The function throws System.ArgumentException with some additional information about the exception, so that users can easily find out what went wrong. To implement the same functionality, you can use the invalidArg function:

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

This is probably better than just using failwith which throws a more general exception. When using invalidArg, users can check for a specific type of exceptions.

As kvb noted, another option is to return option 'a. Many standard library functions provide both a version that returns option and a version that throws an exception. For example List.pick and List.tryPick. So, maybe a good design in your case would be to have two functions - nth and tryNth.

女皇必胜 2024-10-08 19:17:32

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

| _ -> failwith "Invalid list index"

如果您预计无效列表索引很少见,那么这可能就足够了。然而,另一种选择是更改您的函数,使其返回一个'a option

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

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

If you want your function to return a meaningful result and to have the same type as it has now, then you have no alternative but to throw an exception in the remaining case. A matching failure will throw an exception, so you don't need to change it, but you may find it preferable to throw an exception with more relevant information:

| _ -> failwith "Invalid list index"

If you expect invalid list indices to be rare, then this is probably good enough. However, another alternative would be to change your function so that it returns an 'a option:

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

This places an additional burden on the caller, who must now explicitly deal with the possibility of failure.

寻梦旅人 2024-10-08 19:17:32

据推测,如果采用空列表无效,您最好只是抛出异常?

一般来说,你应该采取何种防御措施的规则并不会因语言的不同而改变——我总是遵循这样的指导原则:如果是公共代码,则对验证输入持偏执态度,但如果是私有代码,你可以不那么严格。 (实际上,如果它是一个大型项目,并且是私有代码,请严格一点......基本上严格程度与可能调用您的代码的开发人员数量成正比。)

Presumably, if taking an empty list is invalid, you're best off just throwing an exception?

Generally the rules for how defensive you should be don't really change from language to language - I always go by the guideline that if it's public be paranoid about validating input, but if it's private code, you can be less strict. (Actually if it's a large project, and it's private code, be a little strict... basically strictness is proportional to the number of developers who might call your code.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文