【问题标题】:F# If statement type mismatchF# If 语句类型不匹配
【发布时间】:2021-01-19 04:18:24
【问题描述】:

我有点迷茫,我已经连续尝试了 2 个小时,试图让这段代码正常工作,但我迷路了。

let DropColumn list =
    if List.exists List.isEmpty list then "empty value"
    else
    list |> List.map List.tail

这给了我一个错误错误 FS0001:类型“字符串”与类型“列表”不匹配

【问题讨论】:

  • “空值”是一个字符串。 List.map 返回一个列表。 if \ else 块必须返回相同的数据类型。
  • 是的,这就是我的想法,但是我该如何解决这个问题?
  • 你无法解决没有意义的事情。此处的修复将取决于您实际需要对返回值执行的操作。我能想到至少四种可行的解决方案:1)返回原始列表,2)返回一个空列表,3)返回一个选项,4)引发异常。
  • 很难说你想用这个函数做什么。 if 表达式返回两种不同的数据类型。哪一个是正确的?字符串还是列表?你把什么传递给这个函数?你想检查什么?你想返回什么?
  • 好吧,我试图让它基本上“优雅地”失败,从某种意义上说,我不想用“failswith”引发异常,但我希望函数吐出错误输出,最好是自定义错误留言

标签: list recursion error-handling f#


【解决方案1】:

函数式编程中处理失败的常用方法是使用Result类型,其本质定义为:

type Result<'T,'TError> =
    | Ok of 'T
    | Error of 'TError

要将其应用到您的代码中,我们只需将string 包装在不快乐的路径中,使用Error 和来自快乐路径的list 使用Ok

let DropColumn list =
    if List.exists List.isEmpty list then
        Error "empty value"
    else
        list |> List.map List.tail |> Ok

然后要使用它,您可以使用模式匹配:

match DropColumn myList with
| Ok newList ->
    newList
| Error message ->
    printfn "Error occurred: %s" message
    []

【讨论】:

    【解决方案2】:

    @glennsl 给出的答案是正确的,并且在许多情况下是首选方式。不过,我想补充一下,还有另外两种常见的处理无效输入的方法:

    • 引发异常。仅将其用于特殊情况,即您希望代码因无效数据而停止的情况。请勿将其用于您认为数据可能经常出错的常规验证。
    • 使用option。这类似于使用Result,但不维护无效案例的信息。这种方法很常见,在库函数中使用很多,如List.tryFindList.tryHead 等。

    引发异常

    在您展示的 cmets 中,您已经知道此选项存在,但为了完整起见,让我们在这里给出:

    let dropColumnOrRaise list =
        if List.exists List.isEmpty list then failwith "empty value"
        else
        list |> List.map List.tail
    

    使用option

    这种方法通常要求显示错误或进行恢复的业务逻辑转移到别处。

    let tryDropColumn list =
        if List.exists List.isEmpty list then None
        else
        list 
        |> List.map List.tail
        |> Some
    

    如下使用:

    match tryDropColumn myCols with
    | Some columns -> 
         // do something with valid columns, i.e., display them
         printfn "%i columns remaining (List.length (List.head myCols))"
    | None -> 
         // error recovery or showing a message
         printfn "No column selected"
    

    当您处理对所有返回 option 的数据进行操作的多个函数时,您可以将它们与 Option.bind(或 Option.map 如果函数不返回 option)一起使用管道。

    myCols
    |> tryDropColumn
    |> Option.map logColumns      // function that always succeeds
    |> Option.bind tryAtLeastTwoColumns  // function that returns None on 1 or 0
    |> Option.map showColumns
    

    上面的代码不需要为每个返回的option 提供一个match x with。上一个答案中的Result 可以使用类似的代码。

    【讨论】:

      猜你喜欢
      • 2021-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-13
      • 2021-09-22
      • 1970-01-01
      相关资源
      最近更新 更多