嵌套方法的成本 [英] The cost of nested methods

查看:68
本文介绍了嵌套方法的成本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Scala中,可能会在其他方法中定义方法.这将它们的使用范围限制在定义块内部.我使用它们来提高使用多个高阶函数的代码的可读性.与匿名函数文字相反,这使我可以在传递它们之前给它们赋予有意义的名称.

In Scala one might define methods inside other methods. This limits their scope of use to inside of definition block. I use them to improve readability of code that uses several higher-order functions. In contrast to anonymous function literals, this allows me to give them meaningful names before passing them on.

例如:

class AggregatedPerson extends HashSet[PersonRecord] {
  def mostFrequentName: String = {
    type NameCount = (String, Int)
    def moreFirst(a: NameCount, b: NameCount) = a._2 > b._2
    def countOccurrences(nameGroup: (String, List[PersonRecord])) =
      (nameGroup._1, nameGroup._2.size) 

    iterator.toList.groupBy(_.fullName).
      map(countOccurrences).iterator.toList.
      sortWith(moreFirst).head._1
  }
}

由于我应该知道的嵌套方法定义,是否会产生运行时成本?

Is there any runtime cost because of the nested method definition I should be aware of?

对于闭包,答案是否有所不同?

Does the answer differ for closures?

推荐答案

在编译过程中,嵌套函数moveFirstcountOccurences被移出与mostFrequentName相同的级别.它们获得编译器综合名称:moveFirst$1countOccurences$1.

During compilaton, the nested functions are moveFirst and countOccurences are moved out to the same level as mostFrequentName. They get compiler synthesized names: moveFirst$1 and countOccurences$1.

此外,当您在没有参数列表的情况下引用这些方法之一时,它将被提升为一个函数.因此map(countOccurences)与编写map((a: (String, List[PersonRecord])) => countOccurences(a))相同.此匿名函数被编译到单独的类AggregatedPerson$$anonfun$mostFrequentName$2中,该类只不过转发到countOccurences$.

In addition, when you refer to one of these methods without an argument list, it is lifted into a function. So map(countOccurences) is the same as writing map((a: (String, List[PersonRecord])) => countOccurences(a)). This anonymous function is compiled to a separate class AggregatedPerson$$anonfun$mostFrequentName$2, which does nothing more than forward to countOccurences$.

作为旁注,将方法提升为功能的过程称为Eta扩展.如果在需要使用函数类型的上下文中省略参数列表(如您的示例),或者如果使用_代替整个参数列表,或者使用每个参数(val f1 = countOccurences _ ; val f2 = countOccurences(_)

As a side note, the process of lifting the method to a function is called Eta Expansion. It is triggered if you omit the argument list in a context where a function type is expected (as in your example), or if you use _ in place of the entire argument list, or in place of each argument (val f1 = countOccurences _ ; val f2 = countOccurences(_).

如果代码直接位于闭包中,则堆栈中将减少一个方法调用,并生成一个较少的综合方法.在大多数情况下,此操作对性能的影响可能为零.

If the code was directly in the closure, you would have one fewer method call in your stack, and one fewer synthetic method generated. The performance impact of this is likely to be zero in most cases.

我发现它是用于构造代码的非常有用的工具,并且认为您的示例非常习惯于Scala.

I find it to be a fantastically useful tool to structure code, and consider your example very idiomatic Scala.

另一个有用的工具是使用小块来初始化val:

Another useful tool is using small blocks to initialize a val:

val a = {
   val temp1, temp2 = ...
   f(temp1, temp2)
}

您可以使用scalac -print准确查看Scala代码如何转换为可用于JVM的形式.继承程序的输出:

You can use scalac -print to see exactly how Scala code is translated into a form ready for the JVM. Heres the output from your program:

[[syntax trees at end of cleanup]]// Scala source: nested-method.scala
package <empty> {

  class AggregatedPerson extends scala.collection.mutable.HashSet with ScalaObject {
    def mostFrequentName(): java.lang.String = AggregatedPerson.this.iterator().toList().groupBy({
      (new AggregatedPerson$$anonfun$mostFrequentName$1(AggregatedPerson.this): Function1)
    }).map({
      {
        (new AggregatedPerson$$anonfun$mostFrequentName$2(AggregatedPerson.this): Function1)
      }
    }, collection.this.Map.canBuildFrom()).$asInstanceOf[scala.collection.MapLike]().iterator().toList().sortWith({
      {
        (new AggregatedPerson$$anonfun$mostFrequentName$3(AggregatedPerson.this): Function2)
      }
    }).$asInstanceOf[scala.collection.IterableLike]().head().$asInstanceOf[Tuple2]()._1().$asInstanceOf[java.lang.String]();
    final def moreFirst$1(a: Tuple2, b: Tuple2): Boolean = scala.Int.unbox(a._2()).>(scala.Int.unbox(b._2()));
    final def countOccurrences$1(nameGroup: Tuple2): Tuple2 = new Tuple2(nameGroup._1(), scala.Int.box(nameGroup._2().$asInstanceOf[scala.collection.SeqLike]().size()));
    def this(): AggregatedPerson = {
      AggregatedPerson.super.this();
      ()
    }
  };

  @SerialVersionUID(0) @serializable final <synthetic> class AggregatedPerson$$anonfun$mostFrequentName$1 extends scala.runtime.AbstractFunction1 {
    final def apply(x$1: PersonRecord): java.lang.String = x$1.fullName();
    final <bridge> def apply(v1: java.lang.Object): java.lang.Object = AggregatedPerson$$anonfun$mostFrequentName$1.this.apply(v1.$asInstanceOf[PersonRecord]());
    def this($outer: AggregatedPerson): AggregatedPerson$$anonfun$mostFrequentName$1 = {
      AggregatedPerson$$anonfun$mostFrequentName$1.super.this();
      ()
    }
  };

  @SerialVersionUID(0) @serializable final <synthetic> class AggregatedPerson$$anonfun$mostFrequentName$2 extends scala.runtime.AbstractFunction1 {
    final def apply(nameGroup: Tuple2): Tuple2 = AggregatedPerson$$anonfun$mostFrequentName$2.this.$outer.countOccurrences$1(nameGroup);
    <synthetic> <paramaccessor> private[this] val $outer: AggregatedPerson = _;
    final <bridge> def apply(v1: java.lang.Object): java.lang.Object = AggregatedPerson$$anonfun$mostFrequentName$2.this.apply(v1.$asInstanceOf[Tuple2]());
    def this($outer: AggregatedPerson): AggregatedPerson$$anonfun$mostFrequentName$2 = {
      if ($outer.eq(null))
        throw new java.lang.NullPointerException()
      else
        AggregatedPerson$$anonfun$mostFrequentName$2.this.$outer = $outer;
      AggregatedPerson$$anonfun$mostFrequentName$2.super.this();
      ()
    }
  };
  @SerialVersionUID(0) @serializable final <synthetic> class AggregatedPerson$$anonfun$mostFrequentName$3 extends scala.runtime.AbstractFunction2 {
    final def apply(a: Tuple2, b: Tuple2): Boolean = AggregatedPerson$$anonfun$mostFrequentName$3.this.$outer.moreFirst$1(a, b);
    <synthetic> <paramaccessor> private[this] val $outer: AggregatedPerson = _;
    final <bridge> def apply(v1: java.lang.Object, v2: java.lang.Object): java.lang.Object = scala.Boolean.box(AggregatedPerson$$anonfun$mostFrequentName$3.this.apply(v1.$asInstanceOf[Tuple2](), v2.$asInstanceOf[Tuple2]()));
    def this($outer: AggregatedPerson): AggregatedPerson$$anonfun$mostFrequentName$3 = {
      if ($outer.eq(null))
        throw new java.lang.NullPointerException()
      else
        AggregatedPerson$$anonfun$mostFrequentName$3.this.$outer = $outer;
      AggregatedPerson$$anonfun$mostFrequentName$3.super.this();
      ()
    }
  }
}

这篇关于嵌套方法的成本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆