什么是在Scala中重用函数结果的好方法

Vla*_*kov 3 functional-programming scala

让我通过例子澄清我的问题.这是在Scala中使用尾递归编写的标准取幂算法:

def power(x: Double, y: Int): Double = {
  def sqr(z: Double): Double = z * z
  def loop(xx: Double, yy: Int): Double = 
    if (yy == 0) xx
    else if (yy % 2 == 0) sqr(loop(xx, yy / 2))
    else loop(xx * x, yy - 1)

  loop(1.0, y)
}
Run Code Online (Sandbox Code Playgroud)

这里的sqr方法用于产生结果的平方loop.看起来不是一个好主意 - 为这样一个简单的操作定义一个特殊的功能.但是,我们不能只改写loop(..) * loop(..),因为它使计算加倍.

我们也可以使用val和不使用sqr函数编写它:

def power(x: Double, y: Int): Double = {
  def loop(xx: Double, yy: Int): Double = 
    if (yy == 0) xx
    else if (yy % 2 == 0) { val s = loop(xx, yy / 2); s * s }
    else loop(xx * x, yy - 1)

  loop(1.0, y)
}
Run Code Online (Sandbox Code Playgroud)

我不能说它看起来比变体更好sqr,因为它使用state variable.第一种情况更具功能性第二种方式更适合Scala.

无论如何,我的问题是当你需要后处理函数的结果时如何处理案例?也许Scala有其他方法可以实现这一目标?

Ing*_*ngo 6

你正在使用法律

x^(2n) = x^n * x^n
Run Code Online (Sandbox Code Playgroud)

但这是一样的

x^n * x^n = (x*x)^n
Run Code Online (Sandbox Code Playgroud)

因此,为避免递归后的平方,y为偶数的情况下的值应如下面的代码清单中所示.

通过这种方式,可以进行尾调用.这是完整的代码(不知道Scala,我希望通过类比得到正确的语法):

def power(x: Double, y: Int): Double = {
    def loop(xx: Double, acc: Double, yy: Int): Double = 
      if (yy == 0) acc
      else if (yy % 2 == 0) loop(xx*xx, acc, yy / 2)
      else loop(xx, acc * xx, yy - 1)

    loop(x, 1.0, y)
}
Run Code Online (Sandbox Code Playgroud)

这是一个类似Haskell的语言:

power2 x n = loop x 1 n 
    where 
        loop x a 0 = a 
        loop x a n = if odd n then loop x    (a*x) (n-1) 
                              else loop (x*x) a    (n `quot` 2)
Run Code Online (Sandbox Code Playgroud)


Ber*_*ium 5

你可以使用"前进管道".我从这里得到了这个想法:在单行中缓存一个中间变量.

所以

val s = loop(xx, yy / 2); s * s
Run Code Online (Sandbox Code Playgroud)

可以改写成

loop(xx, yy / 2) |> (s => s * s)
Run Code Online (Sandbox Code Playgroud)

使用像这样的隐式转换

implicit class PipedObject[A](value: A) {
  def |>[B](f: A => B): B = f(value)
}
Run Code Online (Sandbox Code Playgroud)

正如彼得指出的那样:使用隐式值类

object PipedObjectContainer {
  implicit class PipedObject[A](val value: A) extends AnyVal {
    def |>[B](f: A => B): B = f(value)
  }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用

import PipedObjectContainer._
loop(xx, yy / 2) |> (s => s * s)
Run Code Online (Sandbox Code Playgroud)

更好,因为它不需要临时实例(需要Scala> = 2.10).