【问题标题】:Is it possible to print definition of a function in Scala是否可以在 Scala 中打印函数的定义
【发布时间】:2018-03-22 11:21:34
【问题描述】:

我想知道我们是否可以在 Scala 中打印函数的定义。函数在 Scala 中被视为对象。

例如:

scala> val splitFunction = (value : String) => { value.split(" ")}

splitFunction: String => Array[String] = <function1>

上面,Scala 交互式 shell 表明 splitFunction 有输入参数 String,它返回字符串数组。这里的 function1 到底是什么意思?

是否可以打印或检索 splitFunction 的定义?

我们可以在 python 中实现同样的效果: Is it possible to print a function as a string in Python?

更新: 在 Apache Spark 中,RDD 沿袭或 DAG 存储有关每个阶段的父 RDD 和转换的信息。我有兴趣获取用作 flatMap 或 map 等转换参数的函数(甚至是 lambda 或匿名函数)的定义。

例如:文件 - DebugTest.scala

val dataRDD = sc.textFile( "README.md" )
val splitFunction = (value : String) => {value.split(" ")}
val mapRDD = dataRDD.map(splitFunction )

println(mapRDD.toDebugString)

输出:

(1) MapPartitionsRDD[2] at map at DebugTest.scala:43 []
 |  README.md MapPartitionsRDD[1] at textFile at DebugTest.scala:41 []
 |  README.md HadoopRDD[0] at textFile at DebugTest.scala:41 []

从上面的输出中,我可以理解执行了哪些转换,但无法理解或检索在“map”转换中用作参数的 splitFunction 的定义。有没有办法检索或打印?

【问题讨论】:

    标签: scala oop apache-spark user-defined-functions scala-collections


    【解决方案1】:

    没有。 (一般)

    之所以说&lt;function1&gt;,是因为没有很好的函数字符串表示,所以我们只说它是一个接受一个参数的函数。

    你不能得到一个函数的定义的原因是因为 Scala 是编译的。您的函数的 JVM 字节码已经相当不可读(当然是我的 cmets)

    aload_1           // Load the second argument (of reference type) onto the stack (the first is this function object)
    checkcast #mm     // Where mm is an index into the class constant pool, representing a reference to the class String (cast inserted because this is a generic method)
    ldc #nn           // Where nn is an index representing the string " "
    invokevirtual #oo // Where oo is another index, this time representing String::split
    areturn           // Return whatever's left on the stack
    

    “显示函数实现”函数必须 1) 从 JVM 检索实现(我相信有一个 API,但它适用于调试器),然后 2) 将代码反编译为 Scala/Java。这本身并不是不可能的,但我认为没有人做过(真的,为什么你会这样做?)

    现在,每个 Scala 匿名函数都可以只存储其代码并覆盖 toString 以输出它,但同样,根本没有理由这样做。即使你想要实现调试目的,你也有可能有源代码,你可以使用类文件中的行号跳转到它,如果你想存储它,它已经存储在类文件中。

    如果您真的需要它,理论上可以定义一个(选择加入)宏(甚至是编译器插件)

    import language.experimental.macros
    import reflect.macros.whitebox.Context
    def stringF(f: Any): Any = macro stringF_impl
    def stringF_impl(c: whitebox.Context)(f: c.Tree): c.Tree = ???
    

    转身

    stringF { arg => body } // BTW: say bye to type inference
    

    进入

    new Function1[A, R] {
      override def apply(arg: A) = body
      override def toString() = "{ arg => body }" // This part is not there normally
    }
    

    但是,我没有听说有人这样做,也没有充分的理由去尝试。

    【讨论】:

    • 感谢您的详细回复。欣赏它。我已经用目标和其他信息更新了我的问题。如果您有任何解决方案来实现同样的目标,请告诉我。
    • 我非常不同意你的推理。 Scala 不是“编译”的。 Scala 是一种编程语言。没有“编译的编程语言”这样的东西。编程语言不是编译或解释的。编程语言只是。编译和解释是编译器或解释器的特征(duh),而不是语言。每种语言都可以编译,每种语言都可以解释。 Scala 恰好有一些已编译的实现这一事实与您是否可以访问函数的源代码无关。您可以存储源代码...
    • … 一个函数,无论您使用解释器还是编译器来存储源代码。如果 Scala 语言规范要求函数具有 toSource 方法,那么编译器将简单地生成一个带有 toSource 方法的匿名类,其中包含函数的源代码作为静态字符串常量。举个例子:目前所有主流的 ECMAScript 实现都被编译了,但是它们都可以打印函数的源代码,只是因为 ECMA-262 规范说它们必须这样做。
    • Scala 不能打印函数源代码的原因是它不存储它。它不存储它的原因是因为它没有必要。这与编译或解释无关。
    • 我在尝试 stringF 方法时收到此错误::13: error: not found: value stringF_impl def stringF(f: Any): Any = macro stringF_impl
    猜你喜欢
    • 2021-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-14
    • 2016-09-15
    • 2020-08-24
    • 2010-09-25
    相关资源
    最近更新 更多