F#递归添加多项式

4
我正在尝试用F#编写一个递归添加多项式的函数。我的多项式可以表示为元组列表。
例如,2x^4 + 3x^2 + x + 5等于[(2.0,4);(3.0,2);(1.0,1);(5.0,0)]
所有的多项式都是正确结构化的(没有相同次数的重复项,除非是零多项式,按照降幂排序,没有空输入列表)。
我在做这件事时遇到了麻烦。以下是我的代码。
type term = float * int
type poly = term list

let rec atp(t:term,p:poly):poly =
  match p with
  | [] -> []
  | (a, b) :: tail -> if snd t = b then (fst t + a, b) :: [] elif snd t > b then t :: [] else ([]) :: atp(t, tail)
(* val atp : t:term * p:poly -> poly *)

let rec addpolys(p1:poly,p2:poly):poly =
  match p1 with
  | [] -> []
  | (a,b) :: tail -> atp((a,b), p2) @ addpolys(tail, p2)

我有两个多项式。
val p2 : poly = [(4.5, 7); (3.0, 4); (10.5, 3); (2.25, 2)]
val p1 : poly = [(3.0, 5); (2.0, 2); (7.0, 1); (1.5, 0)]

当我调用函数时,我的结果是:
val p4 : poly =
  [(4.5, 7); (3.0, 5); (3.0, 4); (3.0, 5); (10.5, 3); (3.0, 5); (4.25, 2)]

当正确答案为

  [(4.5, 7); (3.0, 5); (3.0, 4); (10.5, 3); (4.25, 2); (7.0, 1); (1.5, 0)]

你的代码在FSI中给了我一个错误:“error FS0001:此表达式应该具有term类型,但这里具有'a list'类型。” 请提供正确的代码。 - Olaf
2个回答

4

很遗憾,您的代码无法编译,因此我很难理解您的意图。但是,我有自己的解决方案来解决您的问题。也许它能帮助您:

// addpoly:  (float * 'a) list -> (float * 'a) list -> (float * 'a) list
let rec addpoly p1 p2 =
    match (p1, p2) with
    | [], p2 -> p2
    | p1, [] -> p1
    | (a1, n1)::p1s, (a2, n2)::p2s -> 
        if   n1 < n2 then (a2,    n2) :: addpoly p1  p2s
        elif n1 > n2 then (a1,    n1) :: addpoly p1s p2
        else              (a1+a2, n1) :: addpoly p1s p2s

let p1 = [(3.0, 5); (2.0, 2); ( 7.0, 1);  (1.5, 0)]
let p2 = [(4.5, 7); (3.0, 4); (10.5, 3); (2.25, 2)]

let q = addpoly p1 p2
// val q : (float * int) list =
//   [(4.5, 7); (3.0, 5); (3.0, 4); (10.5, 3); (4.25, 2); (7.0, 1); (1.5, 0)]

我想提醒一下。如果你稍微改变多项式的表示方式,那么可以简化函数的实现。你可以将一个多项式表示为其系数的列表。

例如,当你有这个多项式时:

p1 = 5.0x^5 + 2.0x^2 + 7.0x

you can write it also like this

p1 = 1.5x^0 + 7.0x^1 + 2.0x^2 + 0.0x^3 + 0.0x^4 + 5.0x^5     

因此,您可以使用此列表定义多项式:
let p1 = [1.5; 7.0; 2.0; 0.0; 0.0; 5.0]

这里有两个函数,它们操作在表示上。polyval计算给定值的结果,而polyadd将两个多项式相加。它们的实现非常简单:

// p1 = 1.5x^0 + 7.0x^1 + 2.0x^2 + 0.0x^3 + 0.0x^4 + 5.0x^5
let p1 = [1.5; 7.0; 2.0; 0.0; 0.0; 5.0]

// p2 = 0.0x^0 + 0.0x^1 + 2.25x^2 + 10.5x^3 + 3.0x^4 + 0.0x^5 + 0.0x^6 + 4.5x^7
let p2 = [0.0; 0.0; 2.25; 10.5; 3.0; 0.0; 0.0; 4.5]

// polyval: float list -> float -> float
let rec polyval ps x =
    match ps with
    | []    -> 0.0
    | p::ps -> p + x * (polyval ps x)

// polyadd: float int -> float int -> float int
let rec polyadd ps qs =
    match (ps, qs) with
    | [], ys       -> ys
    | xs, []       -> xs
    | x::xs, y::ys -> (x+y)::polyadd xs ys

let v = polyval p1 2.3
// val v : float = 349.99715

let p = polyadd p1 p2
// val p : float list = [1.5; 7.0; 4.25; 10.5; 3.0; 5.0; 0.0; 4.5]

非常感谢,我只是在想你写那个程序花了多长时间。因为我自己写了两个多小时,结果还是没成功! - jqdc2224
我认为这不超过15分钟,但这仅因为我尝试解决了“使用F#进行函数式编程”中的所有练习,所以我有很多实践经验。顺便说一句,这是一本很棒的书。 - Olaf
1
请注意,这些实现不是尾递归的。 - Mark Seemann
@mark-seemann:是的,那是正确的。这些实现不是尾递归的。但在多项式的上下文中,除非你要处理极大的多项式,否则这可能不是一个大问题。 - Olaf

2

这是一个完全通用的尾递归实现:

let inline addPolys xs ys =
    let rec imp acc = function
        | (coeffx, degx)::xt, (coeffy, degy)::yt when degx = degy ->
            imp ((coeffx + coeffy, degx)::acc) (xt, yt)
        | (coeffx, degx)::xt, (coeffy, degy)::yt when degx > degy ->
            imp ((coeffx, degx)::acc) (xt, (coeffy, degy)::yt)
        | xs, yh::yt -> imp (yh::acc) (xs, yt)
        | xh::xt, [] -> imp (xh::acc) (xt, [])
        | [], yh::yt -> imp (yh::acc) ([], yt)
        | [], [] -> acc
    imp [] (xs, ys) |> List.rev

它有一个类型:

xs:( ^a * 'b) list -> ys:( ^a * 'b) list -> ( ^a * 'b) list
    when  ^a : (static member ( + ) :  ^a *  ^a ->  ^a) and 'b : comparison

由于float具有成员+,而int支持比较,因此类型float * int符合这些通用约束:

> addPolys p1 p2;;
val it : (float * int) list =
  [(4.5, 7); (3.0, 5); (3.0, 4); (10.5, 3); (4.25, 2); (7.0, 1); (1.5, 0)]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接