为Scala创建`**`幂运算符?

14

我非常喜欢许多语言中都可以使用的用 ** 表示乘方的语法(例如 Python)。

在不修改 Scala 基本代码的情况下,是否可能将其引入到 Scala 中呢?

以下是我尝试仅使用 Int 的实现:

import scala.math.pow
implicit class PowerInt(i: Int) {
    def `**`(n: Int, b: Int): Int = pow(n, b).intValue
}

(请参见IDEone上的失败情况


10
请注意,**^ 都没有正确的优先级(这就是标准库不包含它们的原因)。4*5**3 等同于 (4*5)**3 而不是 4*(5**3) - kiritsuku
Scala 可以改变它们的解析方法,采用非 LL(1) 语法;例如:看看 C++ 如何确定地处理多个 > < - A T
4个回答

19

这个答案来得有些晚,但仍然为了其他人的利益,我想指出被接受的回答在 AnyVal 中不必要地扩展了。

原始回答中只需要修复一个小错误。 def ** 方法只需要一个参数,即底数已经在构造函数中传递,而不是像原始代码中一样需要两个。修复这个问题并去掉反引号后的结果如下:

import scala.math.pow
implicit class PowerInt(i: Int) {
    def ** (b: Int): Int = pow(i, b).intValue
}

在这里查看,它按预期运作。

如果调用的方法未定义,则Scala编译器将把Int强制转换为PowerInt。这就是为什么您不需要从AnyVal扩展的原因。

在幕后,Scala会寻找一个隐式类,其构造函数参数类型与被转换对象的类型相同。由于对象只能有一种类型,因此隐式类的构造函数不能有多个参数。此外,如果您定义了两个具有相同构造函数类型的隐式类,请确保它们的函数具有唯一签名,否则Scala将不知道要转换为哪个类,并会抱怨歧义性。


Scala编译器仅在对其调用的方法未定义时将Int强制转换为PowerInt。这就是为什么您不需要从AnyVal扩展的原因,扩展AnyVal是为了避免实际创建PowerInt对象。 - Alexey Romanov

18

这对我有效:(问题#1 pow在双精度上定义,问题#2扩展任意值)(同时,在方法名称中使用那些反引号是没有意义的吗?)

import scala.math.pow

object RichIntt {

  implicit class PowerInt(val i:Double) extends AnyVal {
    def ** (exp:Double):Double = pow(i,exp)
  }

  def main(args:Array[String])
  {
    println(5**6)
  }

}

3

有一种方法可以使用 Numeric 类型类使解决方案更加通用:

implicit class PowerOp[T: Numeric](value: T) {
    import Numeric.Implicits._
    import scala.math.pow

    def **(power: T): Double = pow(value.toDouble(), power.toDouble())
}

0
这是我使用递归的解决方案(因此我不需要import scala.Math.pow):
object RichInt {
    implicit class PowerInt(val base:Double) {
        def ** (pow:Double):Double = if (pow==0) 1 else base*(base**(pow-1))
    }

    def main(args:Array[String]){
        println(2.0**3.0) //8.0
        println(2.0**0.0) //1.0
    }  
}

这对负数或小数无效。 - Gabber

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