6

我正在尝试动态地解释作为字符串给出的代码。例如:

val myString = "def f(x:Int):Int=x+1".

我正在寻找一种可以从中返回真正功能的方法:例如:

val myIncrementFunction = myDarkMagicFunctionThatWillBuildMyFunction(myString)
println(myIncrementFunction(3))

将打印 4

用例:我想稍后在我的代码中使用该解释代码中的一些简单函数。例如,他们可以提供类似 def fun(x: Int): Int = x + 1 作为字符串,然后我使用解释器编译/执行该代码,然后我希望能够使用这个 fun(x ) 例如在地图中。

问题是该函数类型对我来说是未知的,这是一个大问题,因为我需要从 IMain 回退。我读过关于反射、类型系统等的文章,经过一番谷歌搜索后,我达到了这一点。我还检查了 twitter 的 util-eval,但我从文档和他们测试中的示例中看不到太多,这几乎是一回事。

如果我知道类型,我可以做类似的事情

val settings = new Settings
val imain = new IMain(settings)
val res = imain.interpret("def f(x:Int):Int=x+1; val ret=f _ ")
val myF = imain.valueOfTerm("ret").get.asInstanceOf[Function[Int,Int]]
println(myF(2))

它可以正常工作并打印 3 但我被上面所说的问题阻止了,我不知道函数的类型,而这个示例之所以有效,是因为我在定义字符串函数时使用了我使用的类型来测试 IMain作品。

您知道我如何实现此功能的任何方法吗?

我是新手,如有错误请见谅。

谢谢

4

3 回答 3

2

您可以使用 twitter-util 库来执行此操作,检查测试文件: https://github.com/twitter/util/blob/b0696d0/util-eval/src/test/scala/com/twitter/util/EvalTest。斯卡拉

如果您需要使用 IMain,可能是因为您想使用带有自己自定义设置的解释器,您可以执行以下操作:

一个。首先创建一个用于保存结果的类:

    class ResHolder(var value: Any)

湾。创建一个容器对象来保存结果并将代码解释为该对象:

    val settings = new Settings()
    val writer = new java.io.StringWriter()
    val interpreter = new IMain(settings, writer)

    val code = "def f(x:Int):Int=x+1"

    // Create a container object to hold the result and bind in the interpreter
    val holder = new ResHolder(null) 

    interpreter.bind("$result", holder.getClass.getName, holder) match {
       case Success => 
       case Error => throw new ScriptException("error in: binding '$result' value\n" + writer)
       case Incomplete => throw new ScriptException("incomplete in: binding '$result' value\n" + writer)
    }

    val ir = interpreter.interpret("$result.value = " + code)

    // Return cast value or throw an exception based on result
    ir match {
       case Success =>
          val any = holder.value
          any.asInstanceOf[(Int) => Int]

       case Error => throw new ScriptException("error in: '" + code + "'\n" + writer)
       case Incomplete => throw new ScriptException("incomplete in :'" + code + "'\n" + writer)
    }
于 2016-01-05T18:38:02.257 回答
2

好的,我设法实现了我想要的功能,我仍在寻找改进这段代码,但这个片段可以满足我的需求。

我使用了 scala 工具箱和 quasiquotes

import scala.reflect.runtime.universe.{Quasiquote, runtimeMirror}
import scala.tools.reflect.ToolBox

object App {
    def main(args: Array[String]): Unit = {
        val mirror = runtimeMirror(getClass.getClassLoader)
        val tb = ToolBox(mirror).mkToolBox()

        val data = Array(1, 2, 3)

        println("Data before function applied on it")
        println(data.mkString(","))


        println("Please enter the map function you want:")
        val function = scala.io.StdIn.readLine()
        val functionWrapper = "object FunctionWrapper { " + function + "}"
        val functionSymbol = tb.define(tb.parse(functionWrapper).asInstanceOf[tb.u.ImplDef])

        // Map each element using user specified function
        val dataAfterFunctionApplied = data.map(x => tb.eval(q"$functionSymbol.function($x)"))

        println("Data after function applied on it")
        println(dataAfterFunctionApplied.mkString(","))
    }
}

这是终端中的结果:

Data before function applied on it
1,2,3
Please enter the map function you want:
def function(x: Int): Int = x + 2
Data after function applied on it
3,4,5

Process finished with exit code 0
于 2016-01-07T22:08:05.657 回答
2

我想用评论详细说明之前的答案并对解决方案进行评估:

import scala.reflect.runtime.universe.{Quasiquote, runtimeMirror}
import scala.tools.reflect.ToolBox

object Runtime {

  def time[R](block: => R): R = {
    val t0 = System.nanoTime()
    val result = block    // call-by-name
    val t1 = System.nanoTime()
    println("Elapsed time: " + (t1 - t0) + " ns")
    result
  }

    def main(args: Array[String]): Unit = {
        val mirror = runtimeMirror(getClass.getClassLoader)
        val tb = ToolBox(mirror).mkToolBox()
        val data = Array(1, 2, 3)

        println(s"Data before function applied on it: '${data.toList}")
        val function = "def apply(x: Int): Int = x + 2"
        println(s"Function: '$function'")
        println("#######################")

        // Function with tb.eval
        println(".... with tb.eval")
        val functionWrapper = "object FunctionWrapper { " + function + "}"
        // This takes around 1sec!
        val functionSymbol = time { tb.define(tb.parse(functionWrapper).asInstanceOf[tb.u.ImplDef])}

        // This takes around 0.5 sec!
        val result = time {data.map(x => tb.eval(q"$functionSymbol.apply($x)"))}
        println(s"Data after function applied on it: '${result.toList}'")

        println(".... without tb.eval")
        val func = time {tb.eval(q"$functionSymbol.apply _").asInstanceOf[Int => Int]}
        // This takes around 0.5 sec!
        val result2 = time {data.map(func)}
        println(s"Data after function applied on it: '${result2.toList}'")

    }
}

如果我们执行上面的代码,我们会看到以下输出:

Data before function applied on it: 'List(1, 2, 3)
Function: 'def apply(x: Int): Int = x + 2'
#######################
.... with tb.eval
Elapsed time: 716542980 ns
Elapsed time: 661386581 ns
Data after function applied on it: 'List(3, 4, 5)'
.... without tb.eval
Elapsed time: 394119232 ns
Elapsed time: 85713 ns
Data after function applied on it: 'List(3, 4, 5)'

只是为了强调进行评估以提取函数然后应用于数据的重要性,而不是再次评估,正如答案中的评论所表明的那样。

于 2017-10-30T16:22:15.987 回答