【问题标题】:Scala Higher Order Function Little ConfusedScala高阶函数有点困惑
【发布时间】:2013-11-06 07:59:08
【问题描述】:

我在 Worksheet 中运行以下 Scala 代码:

package src.com.sudipta.week2.coursera
import scala.math.abs
import scala.annotation.tailrec

object FixedPoint {
  println("Welcome to the Scala worksheet")       //> Welcome to the Scala worksheet

  val tolerance = 0.0001                          //> tolerance  : Double = 1.0E-4
  def isCloseEnough(x: Double, y: Double): Boolean = {
    abs((x - y) / x) / x < tolerance
  }                                               //> isCloseEnough: (x: Double, y: Double)Boolean
  def fixedPoint(f: Double => Double)(firstGuess: Double): Double = {
    @tailrec
    def iterate(guess: Double): Double = {
      val next = f(guess)
      if (isCloseEnough(guess, next)) next
      else iterate(next)
    }
    iterate(firstGuess)
  }                                               //> fixedPoint: (f: Double => Double)(firstGuess: Double)Double

  def myFixedPoint = fixedPoint(x => 1 + x / 2)(1)//> myFixedPoint: => Double
  myFixedPoint                                    //> res0: Double = 1.999755859375

  def squareRoot(x: Double) = fixedPoint(y => (y + x / y) / 2)(1)
                                                  //> squareRoot: (x: Double)Double
  squareRoot(2)                                   //> res1: Double = 1.4142135623746899

  def calculateAverate(f: Double => Double)(x: Double) = (x + f(x)) / 2
                                                  //> calculateAverate: (f: Double => Double)(x: Double)Double
  def myNewSquareRoot(x: Double): Double = fixedPoint(calculateAverate(y => x / y))(1)
                                                  //> myNewSquareRoot: (x: Double)Double
  myNewSquareRoot(2)                              //> res2: Double = 1.4142135623746899
}

让我感到困惑的是:

  • 下面显示了我的 fixedPoint 函数的 Scala 工作表

fixedPoint: (f: Double => Double)(firstGuess: Double)Double

这是什么?这是函数类型/函数定义还是我错过了这个术语? 基本上我如何用英语解释这个功能?

  • 下面显示了我的 calculateAverate 函数的 Scala 工作表

calculateAverate: (f: Double => Double)(x: Double)Double

但在我看来,我的函数的返回类型是 Double,但我期待的是 Double => Double。原因是我打算将它与期望 Double => Double 的 fixedPoint 一起使用,如下所示:

def myNewSquareRoot(x: Double): Double = fixedPoint(calculateAverate(y => x / y))(1)

请帮助我更清楚地理解高阶函数/柯里化。提前致谢。

【问题讨论】:

    标签: scala functional-programming currying higher-order-functions


    【解决方案1】:
    fixedPoint: (f: Double => Double)(firstGuess: Double)Double
    

    这里的任务是找到函数的不动点,该任务的应用将是我们众所周知的牛顿法平方根

    牛顿法求平方根:

    Calculates the square root of parameter x

    def sqrt(x: Double): Double = ...
    

    实现这一点的经典方法是使用牛顿法进行逐次逼近。

    计算 sqrt(x):

    1. 从初始估计 y 开始(让我们选择 y = 1)。
    2. 通过取 y 和 x/y 的平均值来反复改进估计值。

    示例:x=2

      Estimation               Quotient                    Mean
        1                       2/1=2                       1.5
       1.5                      2/1.5=1.333                1.4167
      1.4167                    2/1.4167=1.4118            1.4142
      1.4142 so on
    

    首先,定义一个计算一个迭代步骤的函数

    def sqrtIter(guess: Double, x: Double): Double =
    if (isGoodEnough(guess, x)) guess
    else sqrtIter(improve(guess, x), x)
    

    第二,定义一个函数改进来改进估计和一个测试来检查终止:

    def improve(guess: Double, x: Double) =
    (guess + x / guess) / 2
    def isGoodEnough(guess: Double, x: Double) =
    abs(guess * guess - x) < 0.001
    

    第三,定义sqrt函数:

    def sqrt(x: Double) = srqtIter(1.0, x)
    

    使用您的定点函数,我们还可以计算数字的平方根

    先看看什么是定点:

    如果f(x) = x,则将number x称为函数f的不动点

    对于某些函数 f,我们可以通过从初始估计开始,然后通过重复应用 f 来定位固定点。

    x, f(x), f(f(x))
    

    这导致了以下查找不动点的函数:

    val tolerance = 0.0001
    def isCloseEnough(x: Double, y: Double) =
    abs((x - y) / x) / x < tolerance
    def fixedPoint(f: Double => Double)(firstGuess: Double) = {
    def iterate(guess: Double): Double = {
    val next = f(guess)
    if (isCloseEnough(guess, next)) next
    else iterate(next)
    }
    iterate(firstGuess)
    }
    

    在这个公式中,fixedPoint 是一个返回另一个函数的函数,即专用函数iterate

    这里fixedPoint

    def fixedPoint(f: Double => Double)(firstGuess: Double)
    

    函数fixedPoint 应用于函数f: Double =&gt; Double。所结果的 然后将函数应用于第二个参数

    (firstGuess: Double)
    

    这种表示法是可能的,因为函数应用程序关联到左侧。那是, 如果 args1 和 args2 是参数列表,则 f(args1)(args2) 等价于 (f(args1))args2

    在您的程序中,第一个参数是函数,输入类型为 Double,返回类型为 Double,然后将此函数应用于猜测

    平方根函数的重新表述。

    让我们从 sqrt 的规范开始:

    sqrt(x) = the y such that y*y = x
            = the y such that y = x / y
    

    因此,sqrt(x) 是函数y =&gt; x / y 的不动点。这表明

    sqrt(x) 
    

    可以通过定点迭代计算:

    def sqrt(x: double) = fixedPoint(y => x / y)(1.0)
    

    这是一个很长的答案,但我认为它会帮助你理解这个概念

    【讨论】:

      【解决方案2】:

      功能

      def fixedPoint (f: Double => Double)(firstGuess: Double):Double
      

      是一个接受两个参数的函数定义:

      1. 函数“f”接受Double 类型的参数,返回Double
      2. 一个名为“firstGuess”的Double

      并返回Double。将单个参数分离到它们自己的括号组中允许该函数用于柯里化:

      def mySqrtCalc(x:Double)(firstGuess:Double):Double = {...}
      
      val mySqrtFunc = mySqrtCalc(2.0) _
      
      val v = mySqrtFunc(1) // parameter firstGuess is set to 1
      

      除了柯里化的能力外,这相当于未柯里化的版本

      def fixedPoint (f: Double => Double,firstGuess: Double):Double
      

      您可能看起来更熟悉。

      calculateAverate 的结果是 Double,因为您将传递的函数 f 与 x 应用到 x 的结果相加,得到 Double,因为 f 是函数 Double =&gt; Double

      您在 myNewSquareRoot 方法体中以柯里化的方式使用 calculateAverate 方法,即仅将两个参数中的第一个放在首位并省略第二个参数,该参数取自外部 fixedPoint 方法.省略calculateAverate 的第二个参数会给你一个方法Double=&gt;Double,因为它是fixedPoint 方法所需要的。

      您可以插入一些println(s"&lt;methodname&gt; value=$value") 来观察执行流程并了解方法调用顺序。

      【讨论】:

      • 感谢您的精彩解释。我认为在您提供的示例中,您错过了一部分 def mySqrtCalc(x:Double)(firstGuess: Double):Double = {...}.. 对吗?
      猜你喜欢
      • 1970-01-01
      • 2021-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-28
      • 1970-01-01
      相关资源
      最近更新 更多