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 からキャストバックする必要があるため、これは大きな問題の 1 つです。リフレクション、型システムなどについて読んだことがありますが、グーグルで調べた後、このポイントに到達しました。また、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 を使用する必要がある場合は、おそらく独自のカスタム設定でインタープリターを使用したいため、次のようにすることができます。

a. まず、結果を保持するためのクラスを作成します。

    class ResHolder(var value: Any)

b. 結果を保持し、コードをそのオブジェクトに解釈するコンテナ オブジェクトを作成します。

    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

コメントを使用して前の回答を詳しく説明し、ソリューションの評価を実行したかったのです。

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 に答える