当参数为空列表时,如何避免值限制错误?

Posted

技术标签:

【中文标题】当参数为空列表时,如何避免值限制错误?【英文标题】:How do I avoid the Value Restriction error when the argument is an empty list? 【发布时间】:2018-05-14 21:48:56 【问题描述】:

当参数为空列表时,List 模块中的某些函数会失败。 List.rev 就是一个例子。问题是可怕的价值限制。

我在尝试定义一个返回列表的函数时遇到了同样的问题,该列表除了最后一个元素之外的所有元素:

let takeAllButLast (xs: 'a list) =
    xs |> List.take (xs.Length - 1)

该函数适用于非空列表,但处理空列表的版本失败:

let takeAllButLast (xs: 'a list) =
    if List.isEmpty xs then []
    else xs |> List.take (xs.Length - 1)
takeAllButLast []
error FS0030: Value restriction. The value 'it' has been inferred to have generic type
    val it : '_a list, etc.

我尝试了几件事:使其成为内联函数,不为参数指定类型,为返回值指定类型,使函数依赖于类型参数,以及稍后使用 Option 类型获取中间结果转换为列表。没有任何效果。

比如这个函数也有同样的问题:

let takeAllButLast<'a> (xs: 'a list) =
    let empty : 'a list = [] 
    if List.isEmpty xs then empty
    else xs |> List.take (xs.Length - 1)

之前在 SO:F# value restriction in empty list 中提出了类似的问题,但当参数为空列表时,唯一的答案也会失败。

有没有办法编写一个同时处理空列表和非空列表的函数?

注意:这个问题并不特定于返回列表中除最后一个元素之外的所有元素的函数。

【问题讨论】:

【参考方案1】:

函数本身完全没问题。该功能不会“失败”。 您不需要修改函数的主体。没错。

问题仅在于您尝试调用函数的方式:takeAllButLast []。在这里,编译器不知道结果应该是什么类型。应该是string list?还是应该是int list?也许bool list?编译器无法知道。所以它会抱怨。

为了编译这样的调用,你需要帮助编译器:告诉它你期望得到什么类型。这可以从上下文中完成:

// The compiler gleans the result type from the type of receiving variable `l`
let l: int list = takeAllButLast []

// Here, the compiler gleans the type from what function `f` expects:
let f (l: int list) = printfn "The list: %A" l
f (takeAllButLast [])

也可以直接声明调用表达式的类型:

(takeAllButLast [] : int list)

或者你可以声明函数的类型,然后调用它:

(takeAllButLast : int list -> int list) []

您也可以分两步完成:

let takeAllButLast_Int : int list -> int list = takeAllButLast
takeAllButLast_Int []

在每种情况下,原理都是一样的:编译器需要知道从某个地方你在这里期望什么类型。

或者,您可以给它一个名称并将其命名为通用

let x<'a> = takeAllButLast [] : 'a list

这样的值可以像普通值一样被访问,但在幕后它被编译为一个无参数的泛型函数,这意味着每次访问它都会导致其主体的执行。这就是List.empty 和类似的“通用值”在标准库中的实现方式。

当然,如果您尝试在 F# 交互中评估此类值,您将再次面临同样的问题 - 必须知道类型 - 无论如何您都必须解决它:

> x   // value restriction
> (x : int list)  // works

【讨论】:

以上是关于当参数为空列表时,如何避免值限制错误?的主要内容,如果未能解决你的问题,请参考以下文章

我有一种形式可以更新mysql中的某些值,当输入为空时,如何避免更新单元格?

当某个值返回为空时,如何使用下拉列表中的选定值?

Java返回值为null时如何避免报错?

选择下拉菜单的选项时,“无法设置属性‘值’为空”

System.ArgumentNullException: '值不能为空。参数名称:实体'我在使用 POST 方法时收到此错误

在 Java8 中使用 lambda 仅在不为空时过滤值