模式匹配铸造类型

时间:2018-03-21 10:30:20

标签: f# functional-programming

我是F#的新手,并且一直在关注指南,试图让一段代码工作,但事实并非如此。

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

然后我使用模式匹配来了解类型,如果是合作运动,也可以获得玩家数量。然后相应地排名。

然而,我一直在收到错误。我在这上面跟着微软的例子,我真的不明白错误。我没有函数式编程背景。

/**
     * @return mixed
     */
    public function lastAnswer() {
        if( class_exists( Config::get( 'account.models.discussion' ) ) ) {
            return $this->hasOne( Config::get( 'account.models.answer' ) )->latest();
        }

        return null;
    }

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

打印时出现第二个错误:

4 个答案:

答案 0 :(得分:6)

问题已经得到解答,但因为提问者说他​​是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,但是区别在于单位是一个表达式你可以正确地赋值,而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类型时,它会返回一个单位),因为它们实际上调用了两个返回unitinterpreteSportprintfn)的函数。要完全修复代码,就像在其他答案中已经说过的那样,必须为第一次调用提供缺少的整数参数,所以:

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

答案 1 :(得分:5)

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

| :? Individual -> interpretSport sport.Name 1

此外,第二个调用使用了tupled参数,但声明函数采用curried参数。试着这样称呼它:

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

答案 2 :(得分:5)

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

我的代码的意图根本不清楚,但我试图重构以删除类的使用:

SELECT 10 as price  
    UNION ALL 
SELECT ('{"price":2}'::jsonb->>'price')::int as price;

答案 3 :(得分:3)

您的代码存在一些小问题。最明显的是在matchSport中,您使用参数元组以未经验证的样式调用interpretSport。电话应该如下:

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

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

Individual -> interpretSport sport.Name 1

但是你可能想要使用之前绑定的运动(可能在你给出的列表中作为参数)来进行检查。一般来说(在函数式编程和其他地方)硬编码很多字符串是一个坏主意,你可能想要做某种关联List,或者体育地图到Ranks,然后折叠体育列表并匹配当找到个人或团队时,然后打印地图为您提供的任何内容。这将更短,更具可扩展性。

相关问题