霍纳的双变量多项式规则

时间:2017-02-04 01:13:30

标签: functional-programming sml smlnj polynomial-math mlton

Horner的规则用于简化在特定变量值处评估多项式的​​过程。 https://rosettacode.org/wiki/Horner%27s_rule_for_polynomial_evaluation#Standard_ML

我很容易将使用SML的方法应用于一个变量多项式,表示为一个int列表:

fun horner coeffList x = foldr (fn (a, b) => a + b * x) (0.0) coeffList

这很好用。然后我们可以使用:

来调用它
- val test = horner [1.0, 2.0, 3.0] 2.0;
> val test = 17.0 : real

其中[1.0, 2.0, 3.0]是表示多项式系数的列表,2.0是变量x的值,17.0是评估多项式的​​结果。

我的问题是这样的:我们有一个由(int列表列表)表示的两个变量多项式。高级列表中的第n个项将表示包含y ^ n的所有多项式项,而低级列表中的第m个项将表示包含x ^ m的所有多项式项。

例如:[[2],[3,0,0,3],[1,2]]是多项式

  

(2(x ^ 0)(y ^ 0))+
  (3(x ^ 0)(y ^ 1)+ 0(x ^ 1)(y ^ 1)+ 0(x ^ 2)(y ^ 1)+ 3(x ^ 3)(y ^ 1))+
  (1(x ^ 0)(y ^ 2)+ 2(x ^ 1)(y ^ 2))

该函数需要在指定的x和y处返回多项式的值。

我尝试过使用mlton编译器的各种方法。

  1. 首先我尝试了一个嵌套的foldr函数:

    fun evalXY (z::zs) x y = 
            foldr 
            (fn (s, li:list) => 
                s + ((foldr (fn(a, b) => a + b*x) 0 li)*y)
            )
            0 
            z:zs
    
  2. 你可以看到我试图使用" s"作为一个累加器,像" a"用于单变量示例。由于每个被折叠器处理的元素需要被折叠"折叠"本身,我在描述外部折叠的函数中再次调用foldr。我知道帽子这个内部折叠工作正常,我证明了它。 *我的问题似乎是我无法访问外部折叠器所在的列表元素以将该列表传递到内部折叠器中。 >看看我在内部折叠器中使用li的位置,这就是我的问题。 *

    1. 然后我尝试将我的单个变量函数应用于map。我遇到了同样的问题:

      fun evalXY (z::zs) x y = 
              map 
              (foldr (fn(a, b) => a + b*x) 0 ???)
              z:zs
      

      *通过这次尝试,我知道我正在获取一份整数列表。我输入了一个int列表列表,其中内部列表被处理并返回到外部列表,由foldr作为int。在此之后,我将再次折叠以将y值应用于多项式。 这里的函数应该看起来像:: fn evalXY:(int list list)* int * int) - > ... - > int list *

    2. 我是SML的新手,所以也许我在这里缺少一些基本的东西。我知道这是一种函数式编程语言,所以我试图积累值而不是改变不同的变量,

2 个答案:

答案 0 :(得分:4)

你非常接近。让我们从问题的形式化开始吧。将系数C指定为您指定的嵌套列表,您需要评估

请注意,您可以从内部总和中提取来获取

仔细观察内在的总和。这只是变量x的多项式,系数由给出。在SML中,我们可以根据您的horner函数将内部和写为

fun sumj Ci = horner Ci x

让我们更进一步,定义

在SML中,这是val D = map sumj C。我们现在可以用D:

来编写原始多项式

应该很清楚,这只是horner的另一个实例,因为我们有一个系数为的多项式。在SML中,该多项式的值为

horner D y

......我们已经完成了!

这是最终的代码:

fun horner2 C x y =
  let
    fun sumj Ci = horner Ci x
    val D = map sumj C
  in
    horner D y
  end

不是很好吗?我们所需要的只是Horner方法的多种应用,以及map

答案 1 :(得分:2)

你的第二种方法似乎走上正轨。如果您已经定义了horner,那么您需要做的是将horner应用于在外部列表上映射horner applied to inner list x的结果,例如:

fun evalXY coeffLists x y = horner (map (fn coeffList => horner coeffList x) coeffLists) y

您可以通过相应的折叠替换对horner的两次调用,但它的可读性会低得多。

请注意,如果您颠倒horner中两个参数的顺序,那么您可以缩短evalXY

fun horner x coeffList = foldr (fn (a, b) => a + b * x) (0.0) coeffList
fun evalXY x y coeffLists = horner y (map (horner x) coeffLists)

关键是currying的工作方式,如果你使用第二个顺序,那么horner x已经是coeffList的函数,所以你不再需要匿名函数fn coeffList => horner coeffList x。故事的寓意是,在定义一个curried函数时,你应该仔细考虑参数的顺序,因为它会使一些部分应用程序比其他应用程序更容易创建。

顺便说一下,SML很挑剔。在您对horner的讨论中,您说过您会将其称为horner list 2。它需要horner list 2.0。同样,在您的第二次尝试中,使用0而不是0.0会产生问题。