【问题标题】:Is a @specialized Ordered possible?@specialized Ordered 可能吗?
【发布时间】:2014-08-27 09:13:15
【问题描述】:

我想为数字类型实现“钳位”功能:IntDoubleFloat

(如果它适用于其他东西,比如字符串,那也很好,但这不是我的目标。)

This 演示了具有隐式 Ordering 的特化仍然对参数进行装箱/取消装箱。

但它从未最终确定是否有解决方案,也许是通过不接受任意Ordering

这行得通吗?

def clamp[@specialized A <% Ordered[A]](low: A, high: A)(value: A) =
  if(low > value) {
    low
  } else if(high < value) {
    high
  } else {
    value
  }

或者&lt;% 会导致装箱和拆箱?

如果是这样,我唯一的办法是为每种原始类型编写一个单独的函数吗?


编辑:有一个类似意图的问题——How to write a limit function in Scala?——尽管它既不要求泛型也不要求专业化。

【问题讨论】:

    标签: scala autoboxing


    【解决方案1】:

    拳击仍然发生。

    &lt;% 表示“可通过隐式转换进行转换”,在这种情况下,这意味着A =&gt; Ordered[A] 类型的附加隐式参数。为了调用&lt; 方法,代码必须将数字包装到Ordered[A] 对象中,在原语的情况下,这些是类scala.runtime.Rich***。专业化无法猜测整数或双精度的&lt; 比这更具体。

    此外,A=&gt;Ordered[A] 还需要盒装输入,因为 Function1 不是专门用于案例 primitive=&gt;reference,仅适用于 primitive=&gt;primitive

    因此拳击会发生两次。

    使用-print 编译会产生这样的结果:

    <specialized> def clamp$mIc$sp(low: Int, high: Int, value: Int, evidence$1: Function1): Int = 
    if (evidence$1.apply(scala.Int.box(low)).$asInstanceOf[math.Ordered]().>(scala.Int.box(value)))
      low
    else
      if (evidence$1.apply(scala.Int.box(high)).$asInstanceOf[math.Ordered]().<(scala.Int.box(value)))
        high
      else
        value;
    

    Int.box 将整数装箱到 java.lang.Integerevidence$1.apply 将其拆箱并再次装箱到 scala.runtime.RichInt

    我建议手工专门编写代码。

    【讨论】:

    • 是的,我怀疑我必须这样做。 C++ 模板在这些情况下真的很出色。
    【解决方案2】:

    您基本上可以使用 scala 宏来实现这一点,尽管您不会为 Ordering 这样做。相反,您将拥有一个适用于任何实现&lt;&gt; 函数形式的类的函数。它会在编译时做尽可能多的工作,如果没有找到可接受的小于或大于的实现,它会在编译时失败

    首先你需要定义一个宏对象,来包含实现:

    // macros.scala
    import scala.reflect.runtime.universe._
    import scala.reflect.macros.blackbox.Context
    
    object Macros {
      def clamp[A](c: Context)(low: c.Expr[A], high: c.Expr[A])(value: c.Expr[A]): c.Expr[A] = {
        import c.universe._
    
        val tree = 
          q"""
            val lowResult = $low
            val valueResult = $value
            var hasValue = false
            var result = valueResult
    
            if (valueResult < lowResult) {
              hasValue = true
              result = lowResult
            }
    
            if (!hasValue) {
              val highResult = $high
              if (valueResult > highResult) {
                result = highResult
              }
            }
    
            result
          """
    
        c.Expr(tree)
      }
    }
    

    注意:根据 Karol S 的建议,我重写了宏以尽可能少地评估其参数。如果lowhighvalue 的输入很昂贵,它只会评估lowvalue 一次。 high 仅在未发现值为 low 时才被评估。这依赖于一些 var,但可变状态完全包含在宏的主体中,调用时可以安全地忽略。

    一旦编写完成,宏就可以被外部代码中的普通函数引用:

    // main.scala
    import scala.language.experimental.macros
    
    object Main {
      def clamp[A](low: A, high: A)(value: A): A = macro Macros.clamp[A]
    
      def main(args: Array[String]): Unit = {
        val int = clamp(0, 10)(20)
      }
    }
    

    使用 -print 编译时会生成以下代码:

    package <empty> {
      object Main extends Object {
        def main(args: Array[String]): Unit = {
          val int: Int = ({
            val lowResult: Int = 0;
            val valueResult: Int = 20;
            var hasValue: Boolean = false;
            var result: Int = valueResult;
            if (valueResult.<(lowResult))
              {
                hasValue = true;
                result = lowResult
              }
            else
              ();
            if (hasValue.unary_!())
              {
                val highResult: Int = 10;
                if (valueResult.>(highResult))
                  result = highResult
                else
                  ()
              }
            else
              ();
            result
          }: Int);
          ()
        };
        def <init>(): Main.type = {
          Main.super.<init>();
          ()
        }
      }
    }
    

    这避免了装箱,尽管(如@specialized)它会稍微增加编译代码的大小,方法是将 if 语句直接插入到任何使用它的代码中。它还需要创建一些临时变量并进行布尔检查以防止对输入进行多次评估,但这些影响应该非常低。它适用于文字和运行时值,只要它知道类型(它几乎总是可以推断)。

    【讨论】:

    • 这个宏不会多次评估它的参数吗?
    • @KarolS 正确。我用只评估一次参数的版本更新了答案(如果可能,完全跳过评估high 输入)。它使代码有点膨胀,但很好地处理了输入是昂贵函数的情况。感谢您的输入。它可能会进一步优化以检测输入是否为文字,并在编译时实际进行钳位,尽管我不认为这是典型的用例。
    猜你喜欢
    • 2011-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多