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

时间:2017-11-30 20:50:29

标签: list f# value-restriction

当参数为空列表时,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类型以后获取中间结果转换为列表<' a>。没有任何效果。

例如,此功能具有相同的问题:

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 个答案:

答案 0 :(得分:4)

功能本身完全没问题。该功能没有&#34;失败&#34;。
您无需修改​​函数体。这是对的。

问题仅在于您尝试调用该函数的方式:equals。这里,编译器不知道结果应该具有什么类型。它应该是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) []

在每种情况下,原理都是相同的:编译器需要从某个地方知道 你期望的类型。

或者,您可以为其命名并将该名称设为 generic

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

这样的值可以像访问常规值一样被访问,但是在幕后它被编译为无参数的泛型函数,这意味着每次访问它都会导致执行它的正文。这就是let x<'a> = takeAllButLast [] : 'a list 和类似的&#34;通用值&#34;在标准库中实现。

但是,当然,如果你试图在F#互动中评估这样的价值,那么你将再次面对同样的问题 - 必须知道这种类型 - 而且你必须要解决它:<\ n \ n < / p>

List.empty