如何在f#中正确创建和使用多项式类型和项类型

时间:2018-12-29 18:56:35

标签: f#

我正在尝试执行此练习: enter image description here

我不确定如何在F#中使用Type,在F#交互中,我写了type term = Term of float *int,然后我尝试通过让x: term = (3.5,8);;创建type term的值,但是它给出了错误。 然后我尝试了let x: term = Term (3.5,8);;并成功了。那为什么呢?

对于第一个功能,我尝试过:

let multiplyPolyByTerm (x:term, p:poly)=
    match p with
    |[]->[]

但这在行|[]->[]上给出了一个错误,表明该表达式期望的是poly类型,但poly实际上是一个列表吗?那么,为什么这里错了?我通过|Poly[]->Poly[]进行了修复。然后,我尝试通过给出将多项式的每个项与给定项相乘的递归定义来完成该功能:|Poly a::af->这给出了一个错误,所以我坚持尝试分解Poly列表。

如果有人对F#中的Type有好的阅读建议,请分享。

我现在拥有所有方法,但是,当多项式为空列表时,我发现自己无法抛出异常,因为递归函数的基本情况是空列表。另外,我不知道如何将常用术语归为一组,请帮助,这是我的代码:

type poly=Poly of (float*int) list
type term = Term of float *int
exception EmptyList
(*
let rec mergeCommonTerm(p:poly)=
    let rec iterator ((a: float,b: int ),  k: (float*int) list)=
        match k with
        |[]->(a,b)
        |ki::kf-> if b= snd ki then (a+ fst ki,b)
    match p with
    |Poly [] -> Poly []
    |Poly (a::af)-> match af with
                    |[]-> Poly [a]
                    |b::bf -> if snd a =snd b then Poly (fst a +fst b,snd a)::bf 
                              else   
*)


let rec multiplyPolyByTerm (x:term, p:poly)=
    match x with
    | Term (coe,deg) -> match p with
                        |Poly[] -> Poly []
                        |Poly (a::af) -> match multiplyPolyByTerm (x,Poly af) with
                                         |Poly recusivep-> Poly ((fst a *coe,snd a + deg)::recusivep)


let rec addTermToPoly (x:term, p:poly)=
    match x with
    |Term (coe, deg)-> match p with
                       |Poly[] ->  Poly [(coe,deg)]
                       |Poly (a::af)-> if snd a=deg then Poly ((fst a+coe,deg)::af)
                                       else match addTermToPoly (x,Poly af) with
                                         |Poly recusivep-> Poly (a::recusivep)




let rec addPolys (x:poly, y: poly)=
    match x with
    |Poly []->y
    |Poly (xh::xt)-> addPolys(Poly xt,addTermToPoly(Term xh, y))


let rec multPolys (x:poly,y:poly)=
    match x with
    |Poly []-> Poly[]
    |Poly (xh::xt)->addPolys (multiplyPolyByTerm(Term xh,y),multPolys(Poly xt,y))

let evalTerm  (values:float) (termmm : term) :float=
    match termmm with
    |Term (coe,deg)->coe*(values**float(deg))

let rec evalPoly (polyn :  poly, v: float) :float=
    match polyn with
    |Poly []->0.0
    |Poly (ph::pt)-> (evalTerm v (Term ph)) + evalPoly (Poly pt,v)

let rec diffPoly (p:poly) :poly=
    match p with
    |Poly []->Poly []
    |Poly (ah::at)-> match diffPoly (Poly at) with
                    |Poly [] -> if snd ah = 0 then Poly []
                                else Poly [(float(snd ah)*fst ah,snd ah - 1)]
                    |Poly (bh::bt)->Poly ((float(snd ah)*fst ah,snd ah - 1)::bh::bt)

1 个答案:

答案 0 :(得分:1)

正如我在评论中提到的那样,阅读https://fsharpforfunandprofit.com/posts/discriminated-unions/对您非常有帮助。但是,让我给您一些快速帮助,以帮助您摆脱困境,并开始解决您眼前的问题。您处在正确的轨道上,只是在语法上稍作挣扎(运算符优先级是语法的一部分)。

首先,在阅读此答案的其余部分时,将MSDN operator precedence documentation加载到另一个标签中。稍后您将要对其进行研究,但是首先,我将解释一下F#如何对待您可能还不了解的歧视工会的微妙之处。

当您定义诸如poly之类的区分联合类型时,名称Poly的作用类似于该类型的构造函数。在F#中,构造函数是函数。因此,当您编写Poly (something)时,F#解析器将其解释为“获取值(something)并将其传递给名为Poly的函数”。在这里,函数Poly并不是您必须明确定义的函数;它已隐式定义为类型定义的一部分。要明确说明这一点,请考虑以下示例:

type Example =
    | Number of int
    | Text of string

5           // This has type int
Number 5    // This has type Example
Number      // This has type (int -> Example), i.e. a function
"foo"       // This has type string
Text "foo"  // This has type Example
Text        // This has type (string -> Example), i.e. a function

现在查看在另一个选项卡中加载的运算符优先级列表。最低优先级在表的顶部,最高优先级在表的底部;换句话说,桌子上的东西越低,它绑定的越“紧密”。如您所见,函数应用程序(f x,使用参数f调用x)与非常绑定得比::运算符更紧密。因此,当您编写f a::b时,不是读为f (a::b),而是读为(f a)::b。换句话说,f a::b读作“项目b是我们将称为T的某种类型的列表,而函数调用f a产生的类型为{ {1}}应该放在列表T的前面”。相反,如果您的意思是“将通过将项目b放在列表a的开头形成的列表,然后用结果列表调用b,则需要括号:您必须输入f即可获得该含义。

因此,当您编写f (a::b)时,它被解释为Poly a::af,表示“这里是列表。第一项是(Poly a)::af,这意味着Poly a是a a。列表的其余部分将称为(float * int) list”。而且由于您传递给它的值不是列表,而是一个af类型,所以类型不匹配。 (请注意,poly类型的项目包含列表,但它们本身不是列表)。您需要写的是poly,这意味着“这里是Poly (a::af)类型的项目,其中包含一个列表。该列表应分为头poly和休息,a。”

我希望这有助于而不是使水进一步混乱。如果您不了解其中的任何部分,请告诉我,我会尽力使其更清楚。

P.S。您可能还想知道语法的另一点:F#提供了多种方法来指示错误情况(例如此作业中的空列表),但是您的教授要求您在给出无效输入时使用af。这意味着他希望您的代码在遇到错误时“抛出”或“引发”异常。在C#中,术语是“ throw”,但在F#中,术语是“ raise”,语法如下:

exception EmptyList

这应该解决您需要问的下一个问题。 :-)

更新2 :您已对问题进行了编辑,以澄清您遇到的另一个问题,其中递归函数归结为一个空列表作为基本案例-但您的教授要求您考虑一个空列表作为无效输入。有两种解决方法。我将首先讨论较复杂的一个,然后再讨论较简单的一个。

解决这个问题的更复杂的方法是,对于要求您定义的每个功能,都有两个单独的功能,一个“外部”功能和一个“内部”功能。在每种情况下,“外部”都会检查输入是否为空列表,并在这种情况下引发异常。如果输入不是空列表,则将输入传递给“内部”函数,该函数执行递归算法(并且不会将空列表视为错误)。因此,“外部”功能基本上仅执行错​​误检查,而“内部”功能则完成所有工作。这是专业编程中非常常见的方法,其中所有错误检查都在代码的“边缘”进行,而“内部”代码则永远不必处理错误。因此,这是一种了解的好方法-但在您的特定情况下,我认为它比您需要的还要复杂。

更简单的解决方案是重写函数,以将单项列表视为基本情况,以便递归函数永远不会一直到空列表。然后,您始终可以将空列表视为错误。由于这是家庭作业,因此我不会基于您的实际代码为您提供示例,而是基于简单的“获取一个整数列表的和”练习的示例,其中将空列表视为错误:

if someErrorCondition then
    raise EmptyList

// Or ...

match listThatShouldNotBeEmpty with
| [] -> raise EmptyList
| head::rest -> // Do something with head, etc.

match表达式中的语法let rec sumNonEmptyList (input : int list) : int = match input with | [] -> raise EmptyList | [x] -> x | x::rest -> x + sumNonEmptyList rest 的意思是“这将与其中一个完全匹配 的列表进行匹配,并将名称[x]分配给该项目的值” 。在您的情况下,您可能会与x进行匹配以引发异常,Poly []作为基本情况,而Poly [a]作为“多个项目”情况。 (这是我认为应该给您的线索;如果您自己解决其余问题,您会学得更好)。