【问题标题】:Pattern matching casting types模式匹配铸造类型
【发布时间】:2018-08-30 10:18:23
【问题描述】:

我是 F# 的新手,一直在按照指南尝试使一段代码工作,但它没有。

我通过继承创建单人和合作运动的类型。

然后我使用模式匹配来了解类型,如果是合作运动,还获取玩家数量。然后相应地对每个进行排名。

但是,我遇到了错误。我在这方面遵循了 Microsoft 示例,但我并不真正理解这些错误。我没有函数式编程背景。

type Sport (name: string) =
    member x.Name = name

type Individual(name: string) =
    inherit Sport(name)

type Team(name: string, numberOfPlayers : int) =
    inherit Sport(name)
    member x.numberOfPlayers = numberOfPlayers


let MK = new Individual("Combate Mortal")
let SF = new Individual("Lutadores de Rua")
let Tk = new Individual("Tekken Chupa")

let MvC = new Team("Marvel Contra Capcom", 3)
let Dbz = new Team("Bolas do Dragao", 3)

let interpretSport (sport:string) (players:int)  =
    match sport with
    | "Combate Mortal" -> printfn "Rank1"  
    | "Lutadores de Rua" -> printfn "Rank2"
    | "Tekken Chupa" -> printfn "Rank3"
    | "Bolas do Dragao" -> printfn "Rank4. No of players: %d " players
    | "Marvel Contra Capcom" -> printfn "Rank5. No of players: %d" players
    | _ -> printfn "not a sport in our list..." 



let matchSport (sport:Sport)  = 
    match sport with
    | :? Individual -> interpretSport(sport.Name)
    | :? Team as teamSport -> interpretSport(teamSport.Name,teamSport.numberOfPlayers)
    | _ -> printfn "not a sport" 

matchSport(MK)
matchSport(SF)
matchSport(Tk)
matchSport(MvC)
matchSport(Dbz)

调用具有多个参数的函数时的第一个错误:

打印时的第二个错误:

【问题讨论】:

    标签: f# functional-programming


    【解决方案1】:

    这个问题已经回答了,但是因为提问者说他​​是 F# 的新手,也许值得迭代一下。 首先,您定义一个带有两个参数的函数:

    let interpreteSport (sport:string) (player:int) =
    

    在 F# 中,没有与 C# 中相同的可选参数的概念,因此如果您声明一个具有两个参数的函数,并且要调用它并获取其返回值,则必须提供所有参数您在其定义中放入的参数。 因此,在匹配表达式的第一个分支中,当您编写时:

    :? Individual -> interpretSport(sport.Name)
    

    你犯了一个错误,只将一个参数传递给一个需要两个参数的函数。

    但是等等! 为什么编译器没有提示您使用一个参数调用函数时需要两个参数? 因为事实证明,你写的东西,即使它没有像你想象的那样调用interpreteSport 函数,它也是F# 中一个完美的有效表达式。 它返回的是一个叫做“部分应用函数”的表达式,即一个函数已经接收到它的第一个参数,并且正在等待另一个参数。 如果你将这样一个表达式的结果赋值给一个值,假设:

    let parzFun = interpretSport sport.Name 
    

    然后你可以在你的代码中传递这个值,当你准备好提供缺少的参数时,像这样评估它:

    let result = parzFun 1
    

    这就是编译器在谈论 'int -> unit' 时告诉你的:F# 中的函数签名以这种形式给出:

    a -> b -> c -> d -> retval,其中a、b、c、d等为参数类型,retVal为返回值。

    您的interpreteSport 函数的签名为:string -> int -> unit,其中unit 是表示“无值”的特殊类型,类似于 C# void,但最大的区别在于 unit 是您可以正确分配的表达式给一个值,而 void 只是一个关键字,在 C# 中不能将变量分配给 void。

    好的,所以,当你调用你的函数时只传递第一个参数(一个字符串),你得到的是一个int -> unit类型的表达式,这是另一个期望和整数并返回单位的函数。

    因为这个表达式在一个匹配表达式的一个分支中,并且因为一个匹配表达式的所有分支都必须返回相同的类型,所以其他2个分支也应该返回一个int -> unit函数,它不是什么,和这解释了你的第二个错误。

    稍后会详细介绍,但在此之前,我们必须查看编译器报告的第一个错误,由这行代码引起:

    :? Team as teamSport -> interpretSport(teamSport.Name,teamSport.numberOfPlayers)
    

    在这里,您认为您正在使用 2 个参数调用您的函数,但实际上并非如此:当您将 2 个值放在括号中并用逗号分隔时,您正在创建一个元组,即由以下各项组成的单个值两个或多个值。就像您再次仅传递第一个参数,但现在类型错误:函数的第一个参数是字符串,而您传递的是元组: ('a * 'b) 是 F# 表示元组的方式:表示由 'a 类型的值(通用,在您的情况下为字符串)和另一个类型的值 'b(通用,在您的情况下为整数)组成的单个值。 要正确调用你的函数,你必须这样调用它:

    :? Team as teamSport -> interpretSport teamSport.Name teamSport.numberOfPlayers
    

    但是,即使您将自己限制在此更正范围内,您也会遇到第二个错误,因为请记住,匹配的第一个表达式返回一个部分应用的函数,因此 int -> unit(一个需要整数并返回的函数一个单元),而您的第二个和第三个表达式现在是 unit 类型,因为它们实际上调用了两个返回 unit 的函数(interpreteSportprintfn)。要完全修复您的代码,正如其他答案中已经说过的那样,您必须在第一次调用时提供缺少的整数参数,所以:

    let matchSport (sport:Sport)  = 
        match sport with
        | :? Individual -> interpretSport sport.Name 1
        | :? Team as teamSport -> interpretSport teamSport.Name teamSport.numberOfPlayers
        | _ -> printfn "not a sport" 
    

    【讨论】:

    • 我喜欢详细的解释!客观的答案很好,但是您通过解释编译器告诉我的内容告诉了我所有内容是如何组合在一起的。我从没想过我会在某个时候创建​​一个元组...... F# 对我来说感觉很棘手。
    • 谢谢@Kevin,我很高兴能帮上忙。顺便说一句,您当然可以定义一个以元组为参数的函数,例如: let add (a, b) = a + b 在这种情况下,函数签名是 int * int -> int,您可以这样称呼它您在 c# 中调用方法,但目前我建议您坚持使用标准 f# 参数定义,即使元组参数在语言中肯定有其存在的理由,例如在编写需要由其他 .net 语言调用的 f# 代码时,或者在处理您本来希望将其视为元组的数据时。
    【解决方案2】:

    您的函数interpretSport 有两个参数,但您对它的第一次调用只有一个。试着这样称呼它:

    | :? Individual -> interpretSport sport.Name 1
    

    另外,第二次调用使用元组参数,但函数被声明为采用柯里化参数。试着这样称呼它:

    | :? Team as teamSport -> interpretSport teamSport.Name teamSport.numberOfPlayers
    

    【讨论】:

      【解决方案3】:

      如果这是一个 F# 学习练习,那么最好完全避免类和继承。基本的惯用 F# 类型是记录和区分联合。

      您的代码的意图对我来说根本不清楚,但我已尝试重构以删除对类的使用:

      type Players =
          | Individual
          | Team of numberOfPlayers:int
      
      type Sport = { Name : string; Players : Players }
      
      let MK = { Name = "Combate Mortal"; Players = Individual }
      let SF = { Name = "Lutadores de Rua"; Players = Individual }
      let Tk = { Name = "Tekken Chupa"; Players = Individual }
      
      let MvC = { Name = "Marvel Contra Capcom"; Players = Team 3 }
      let Dbz = { Name = "Bolas do Dragao"; Players = Team 3 }
      
      let interpretSport (sport:Sport) =
          let players =
              match sport.Players with
              | Individual -> ""
              | Team numberOfPlayers -> sprintf ". No of players: %d" numberOfPlayers
          let rank =
              match sport.Name with
              | "Combate Mortal" -> Some 1
              | "Lutadores de Rua" -> Some 2
              | "Tekken Chupa" -> Some 3
              | "Bolas do Dragao" -> Some 4
              | "Marvel Contra Capcom" -> Some 5
              | _ -> None
          match rank with
          | Some r -> printfn "Rank%d%s" r players
          | None -> printfn "not a sport in our list..."
      

      【讨论】:

        【解决方案4】:

        您的代码存在几个小问题。最明显的是在 matchSport 中,您以非柯里化风格调用 translateSport,并带有参数元组。调用应如下所示:

        Team as teamSport -> interpretSport teamSport.Name teamSport.numberOfPlayers
        

        但是,这是一个问题,因为在模式匹配的第一种情况下,您只使用一个参数调用 interpretSport,所以您部分应用它并得到一个类型 int -> unit,但是当您完全应用它时第二种情况你得到unit,当然所有类型的模式匹配情况都必须匹配。最便宜的解决方案是在您的通话中添加1,如下所示:

        Individual -> interpretSport sport.Name 1
        

        但您可能希望使用之前绑定的运动(可能在您作为参数提供的列表中)进行检查。硬编码这么多字符串通常是一个坏主意(在函数式编程和其他地方),您可能想做某种关联列表,或运动到排名的映射,然后折叠运动列表并匹配当与个人或团队一起找到时,然后打印地图为您提供的任何内容。这将更短且更具可扩展性。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-12-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多