【问题标题】:What is wrong in F# code?F# 代码有什么问题?
【发布时间】:2023-11-08 06:05:01
【问题描述】:

在 F# 中,我试图获取给定列表的最后一个元素。我写了下面的代码

let rec findLast t =
    match t with
        | hd :: [] -> hd
        | hd :: tl -> findLast tl
        | _ -> -1

printfn "%A" (findLast [1,2,3,4,5])

但是当我尝试在 F# Interactive 中执行它时,它会抱怨如下

错误 FS0001:此表达式应具有类型 int 但这里有类型 'a * 'b * 'c * 'd * 'e

我只是想知道上面的代码有什么问题。我知道有不同的智能和优雅的方法可以从 F# 中的列表中获取最后一个元素。但我很想知道上面的代码有什么问题?

【问题讨论】:

  • 列表元素由; 而不是, 分隔。使用findLast [1;2;3;4;5] 或仅使用findLast [1..5]
  • 在这里您可以找到每个 F# 程序员都会遇到的常见错误列表:fsharpforfunandprofit.com/troubleshooting-fsharp 还提到了提示“使用分号作为列表分隔符”。

标签: f# f#-interactive f#-3.0


【解决方案1】:

1,2,3,4,5 是一个元组。 'a * 'b * 'c * 'd * 'e 是一个元组定义。用分号[1;2;3;4;5] 创建一个列表。 [1,2,3,4,5] 是一个元组列表,其中一项是五元组。

let rec findLast t =
    match t with
        | hd :: [] -> hd
        | hd :: tl -> findLast tl
        | _ -> -1

printfn "%A" (findLast [1;2;3;4;5])

【讨论】:

    【解决方案2】:

    试试这个:

    let rec lastElem = function
        | []    -> None
        | [x]   -> Some x
        | x::xs -> lastElem xs
    

    你可以在 REPL 中试试:

    > lastElem [1;2;3];;
    
    val it : int option = Some 3
    
    > lastElem ["a";"b";"c"];;
    
    val it : string option = Some "c"
    

    【讨论】:

    • 虽然代码是正确的,但不能解释为什么 OP 代码是错误的;主要是因为使用您的代码,他的测试将起作用(尽管不是他预期的结果)返回他列表中唯一的元组项
    【解决方案3】:

    正如@Phillip-Scott-Givens 所指出的那样,您可能犯了一个完全常见的错误(尤其是对于 C#'ers),并使用逗号而不是分号来分隔列表。

    这会产生一个元组列表 [(1, 2, 3, 4, 5)] 而不是一个整数列表 [1;2;3;4;5]。在您的类型定义中出现意外的星号是这种情况的一个症状:)

    也就是说,这里有几个不同的函数可以从您的元组、列表和元组列表中获取最后一个值(参考:https://*.com/a/1175123/5470873):

    // Data: 
    let tuples = [ (1,2,3,4,5); ]      // = [1,2,3,4,5]
    let firstListElement = tuples.[0]  
    
    
    // Access: 
    let rec lastItemInList = function
        | hd :: [] -> hd
        | hd :: tl -> lastItemInList tl
        | _ -> failwith "Empty list."
    let lastValueOfFirstItem = function
        | (_, _, _, _, last) :: _ -> last
        | _ -> -1
    let lastValueOfTuple = function _, _, _, _, last -> last
    // same as: let lastValueOfTuple myTuple = 
    //              match myTuple with
    //              | (_, _, _, _, last) -> last
    
    
    // Examples:
    tuples |> lastItemInList              // val it : int * int * int * int * int = (1, 2, 3, 4, 5)
    tuples |> lastValueOfFirstItem        // val it : int = 5
    tuples |> List.map lastValueOfTuple   // val it : int list = [5]
    firstListElement |> lastValueOfTuple  // val it : int = 5
    

    【讨论】:

    • 感谢您分享上面的链接。