149

すべての Scala 開発者が知っておくべき Scala の隠れた機能は何ですか?

回答ごとに 1 つの隠し機能をお願いします。

4

28 に答える 28

85

さて、もう1つ追加する必要がありました。Scala のすべてRegexのオブジェクトには、一致グループへのアクセスを可能にするエクストラクタ (上記の oxbox_lakes からの回答を参照) があります。したがって、次のようなことができます:

// Regex to split a date in the format Y/M/D.
val regex = "(\\d+)/(\\d+)/(\\d+)".r
val regex(year, month, day) = "2010/1/13"

パターン マッチングとエクストラクタの使用に慣れていない場合、2 行目は混乱を招くように見えます。valまたはを定義するときはいつでもvar、キーワードの後に​​来るのは単なる識別子ではなくパターンです。これが機能する理由です:

val (a, b, c) = (1, 3.14159, "Hello, world")

右手の式はTuple3[Int, Double, String]、パターンに一致する を作成します(a, b, c)

ほとんどの場合、パターンはシングルトン オブジェクトのメンバーであるエクストラクタを使用します。たとえば、次のようなパターンを記述した場合

Some(value)

次に、暗黙的に extractor を呼び出していますSome.unapply

しかし、パターンでクラス インスタンスを使用することもできます。それがここで起こっていることです。val 正規表現は のインスタンスでありRegex、パターンで使用する場合、暗黙的に呼び出していますregex.unapplySeq( unapplyvsunapplySeqはこの回答の範囲を超えています)。これにより、一致グループが に抽出されSeq[String]、その要素が割り当てられます。変数年、月、日。

于 2010-01-13T21:41:41.100 に答える
51

構造型定義 - つまり、サポートするメソッドによって記述される型。例えば:

object Closer {
    def using(closeable: { def close(): Unit }, f: => Unit) {
      try { 
        f
      } finally { closeable.close }
    }
}

パラメータのは、メソッドcloseableを持っている以外は定義されていないことに注意してくださいclose

于 2009-07-07T16:26:42.353 に答える
45

型コンストラクター ポリモーフィズム(別名、より高い種類の型)

この機能がないと、たとえば、リストに関数をマッピングして別のリストを返す、またはツリーに関数をマッピングして別のツリーを返すというアイデアを表現できます。しかし、高次の種類なしでは、この考えを一般的に表現することはできません。

上位の種類を使用すると、別の型でパラメーター化された任意の型のアイデアをキャプチャできます。1 つのパラメーターを受け取る型コンストラクターは、 kind であると言われます(*->*)。たとえば、List. 別の型コンストラクターを返す型コンストラクターは、 kind であると言われます(*->*->*)。たとえば、Function1. しかし、Scala では高次の種類があるため、他の型コンストラクターでパラメーター化された型コンストラクターを持つことができます。したがって、それらは のような種類です((*->*)->*)

例えば:

trait Functor[F[_]] {
  def fmap[A, B](f: A => B, fa: F[A]): F[B]
}

があれば、Functor[List]リストをマッピングできます。がある場合は、Functor[Tree]ツリーをマッピングできます。しかし、もっと重要なFunctor[A] のは、種類の A がある(*->*)場合、関数を にマップできることですA

于 2009-08-04T15:10:30.970 に答える
39

乱雑if-elseif-elseなスタイル コードをパターンに置き換えることができるエクストラクタ。これらが完全に隠されているわけではないことはわかっていますが、Scala を数か月間使用してきましたが、それらの機能をよく理解していません。(長い)例では、次のように置き換えることができます:

val code: String = ...
val ps: ProductService = ...
var p: Product = null
if (code.endsWith("=")) {
  p = ps.findCash(code.substring(0, 3)) //e.g. USD=, GBP= etc
}
else if (code.endsWith(".FWD")) {
  //e.g. GBP20090625.FWD
  p = ps.findForward(code.substring(0,3), code.substring(3, 9))
}
else {
  p = ps.lookupProductByRic(code)
}

これで、私の意見でははるかに明確です

implicit val ps: ProductService = ...
val p = code match {
  case SyntheticCodes.Cash(c) => c
  case SyntheticCodes.Forward(f) => f
  case _ => ps.lookupProductByRic(code)
}

バックグラウンドで少し足を踏み入れる必要があります...

object SyntheticCodes {
  // Synthetic Code for a CashProduct
  object Cash extends (CashProduct => String) {
    def apply(p: CashProduct) = p.currency.name + "="

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[CashProduct] = {
      if (s.endsWith("=") 
        Some(ps.findCash(s.substring(0,3))) 
      else None
    }
  }
  //Synthetic Code for a ForwardProduct
  object Forward extends (ForwardProduct => String) {
    def apply(p: ForwardProduct) = p.currency.name + p.date.toString + ".FWD"

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[ForwardProduct] = {
      if (s.endsWith(".FWD") 
        Some(ps.findForward(s.substring(0,3), s.substring(3, 9)) 
      else None
    }
  }

しかし、ビジネス ロジックの一部を適切な場所に分離するという事実のために、足を運ぶ価値はあります。次のようにメソッドを実装できProduct.getCodeます..

class CashProduct {
  def getCode = SyntheticCodes.Cash(this)
}

class ForwardProduct {
  def getCode = SyntheticCodes.Forward(this)     
}
于 2009-06-22T09:14:14.987 に答える
35

scala 2.8 では、パッケージ scala.util.control.TailCalls を使用して末尾再帰メソッドを使用できます (実際にはトランポリンです)。

例:

def u(n:Int):TailRec[Int] = {
  if (n==0) done(1)
  else tailcall(v(n/2))
}
def v(n:Int):TailRec[Int] = {
  if (n==0) done(5)
  else tailcall(u(n-1))
}
val l=for(n<-0 to 5) yield (n,u(n).result,v(n).result)
println(l)
于 2010-05-18T00:10:47.910 に答える
35

Scala が型を具体化したかのように、実行時に型情報を取得する一種の方法であるマニフェスト。

于 2009-06-22T06:55:21.927 に答える
35

ケース クラスは自動的に Product トレイトにミックスインされ、リフレクションなしでフィールドへの型指定されていないインデックス付きアクセスを提供します。

case class Person(name: String, age: Int)

val p = Person("Aaron", 28)
val name = p.productElement(0) // name = "Aaron": Any
val age = p.productElement(1) // age = 28: Any
val fields = p.productIterator.toList // fields = List[Any]("Aaron", 28)

toStringこの機能は、メソッドの出力を変更する簡単な方法も提供します。

case class Person(name: String, age: Int) {
   override def productPrefix = "person: "
}

// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28)) 
于 2010-10-29T20:56:45.023 に答える
32

正確に隠されているわけではありませんが、確かに宣伝されていない機能です: scalac -Xprint

使用例として、次のソースを検討してください。

class A { "xx".r }

これをscalac -Xprint:typer出力でコンパイルすると:

package <empty> {
  class A extends java.lang.Object with ScalaObject {
    def this(): A = {
      A.super.this();
      ()
    };
    scala.this.Predef.augmentString("xx").r
  }
}

Predef.scalascala.this.Predef.augmentString("xx").rの現在のアプリケーションであることに注意implicit def augmentStringしてください。

scalac -Xprint:<phase>は、コンパイラ フェーズの後に構文ツリーを出力します。使用可能なフェーズを表示するには、 scalac -Xshow-phasesを使用します。

これは、舞台裏で何が起こっているかを知るのに最適な方法です。

試してみてください

case class X(a:Int,b:String)

typerフェーズを使用して、その有用性を実感してください。

于 2010-10-26T12:41:16.980 に答える
30

独自の制御構造を定義できます。これは実際には関数とオブジェクト、およびいくつかの構文糖衣にすぎませんが、それらは本物のように見え、動作します。

たとえば、次のコードは and を定義dont {...} unless (cond)dont {...} until (cond)ます。

def dont(code: => Unit) = new DontCommand(code)

class DontCommand(code: => Unit) {
  def unless(condition: => Boolean) =
    if (condition) code

  def until(condition: => Boolean) = {
    while (!condition) {}
    code
  }
}

これで、次のことができます。

/* This will only get executed if the condition is true */
dont {
  println("Yep, 2 really is greater than 1.")
} unless (2 > 1) 

/* Just a helper function */
var number = 0;
def nextNumber() = {
  number += 1
  println(number)
  number
}

/* This will not be printed until the condition is met. */
dont {
  println("Done counting to 5!")
} until (nextNumber() == 5) 
于 2009-11-10T14:29:54.420 に答える
26

@switchScala 2.8 の注釈:

一致式に適用される注釈。存在する場合、コンパイラは一致がテーブルスイッチまたはルックアップスイッチにコンパイルされていることを確認し、代わりに一連の条件式にコンパイルされる場合はエラーを発行します。

例:

scala> val n = 3
n: Int = 3

scala> import annotation.switch
import annotation.switch

scala> val s = (n: @switch) match {
     |   case 3 => "Three"
     |   case _ => "NoThree"
     | }
<console>:6: error: could not emit switch for @switch annotated match
       val s = (n: @switch) match {
于 2010-07-15T07:43:42.063 に答える
26

これが本当に隠されているかどうかはわかりませんが、とてもいいと思います。

2 つの型パラメーターを取る型コンストラクターは、中置表記で記述できます。

object Main {                                                                   
  class FooBar[A, B]

  def main(args: Array[String]): Unit = {
    var x: FooBar[Int, BigInt] = null
    var y: Int FooBar BigInt   = null
  }
}
于 2010-11-30T20:46:07.963 に答える
24

scala 2.8 では、 @specialized を汎用クラス/メソッドに追加できます。これにより、プリミティブ型 (AnyVal を拡張) 用のクラスの特別なバージョンが作成され、不要なボックス化/ボックス化解除のコストが節約されます。 class Foo[@specialized T]...

AnyVals のサブセットを選択できます。 class Foo[@specialized(Int,Boolean) T]...

于 2010-06-20T15:27:33.163 に答える
24

Scala 2.8 では、デフォルト引数と名前付き引数が導入されました。これにより、Scala がケース クラスに追加する新しい「コピー」メソッドの追加が可能になりました。これを定義すると:

case class Foo(a: Int, b: Int, c: Int, ... z:Int)

既存の Foo に似た新しい Foo を作成したいが、"n" の値が異なるだけの場合は、次のように言えます。

foo.copy(n = 3)
于 2010-01-13T21:08:07.673 に答える
23

言語の拡張。私はいつもJavaでこのようなことをしたいと思っていました(できませんでした)。しかし、Scalaでは次のことができます。

  def timed[T](thunk: => T) = {
    val t1 = System.nanoTime
    val ret = thunk
    val time = System.nanoTime - t1
    println("Executed in: " + time/1000000.0 + " millisec")
    ret
  }

そして、次のように書きます。

val numbers = List(12, 42, 3, 11, 6, 3, 77, 44)
val sorted = timed {   // "timed" is a new "keyword"!
  numbers.sortWith(_<_)
}
println(sorted)

取得します

Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)
于 2010-10-26T19:15:04.087 に答える
23

名前による呼び出しパラメーター (編集済み: これは遅延パラメーターとは異なります!) を関数に指定することができ、関数によって使用されるまで評価されません (編集: 実際、それが使用されるたびに再評価されます)。使用済み)。詳細はこちらのよくある質問をご覧ください

class Bar(i:Int) {
    println("constructing bar " + i)
    override def toString():String = {
        "bar with value: " + i
    }
}

// NOTE the => in the method declaration.  It indicates a lazy paramter
def foo(x: => Bar) = {
    println("foo called")
    println("bar: " + x)
}


foo(new Bar(22))

/*
prints the following:
foo called
constructing bar 22
bar with value: 22
*/
于 2009-06-23T20:16:06.770 に答える
20

locallyセミコロン推論の問題を発生させることなく、ローカルブロックを導入するために使用できます。

使用法:

scala> case class Dog(name: String) {
     |   def bark() {
     |     println("Bow Vow")
     |   }
     | }
defined class Dog

scala> val d = Dog("Barnie")
d: Dog = Dog(Barnie)

scala> locally {
     |   import d._
     |   bark()
     |   bark()
     | }
Bow Vow
Bow Vow

locally「Predef.scala」では次のように定義されています。

@inline def locally[T](x: T): T = x

インラインであるため、追加のオーバーヘッドは発生しません。

于 2010-08-23T10:15:26.137 に答える
17

早期初期化:

trait AbstractT2 {
  println("In AbstractT2:")
  val value: Int
  val inverse = 1.0/value
  println("AbstractT2: value = "+value+", inverse = "+inverse)
}

val c2c = new {
  // Only initializations are allowed in pre-init. blocks.
  // println("In c2c:")
  val value = 10
} with AbstractT2

println("c2c.value = "+c2c.value+", inverse = "+c2c.inverse)

出力:

In AbstractT2:  
AbstractT2: value = 10, inverse = 0.1  
c2c.value = 10, inverse = 0.1

valueの前に、ブロック内のフィールドを初期化して、匿名の内部クラスをインスタンス化します。これにより、スクリプトの実行時に示されるように、 の本体が実行される前に が初期化with AbstractT2されることが保証されます。valueAbstractT2

于 2010-06-28T09:29:07.820 に答える
17

「with」キーワードを使用して構造型を構成できます

object Main {
  type A = {def foo: Unit}
  type B = {def bar: Unit}

  type C = A with B

  class myA {
    def foo: Unit = println("myA.foo")
  }


  class myB {
    def bar: Unit = println("myB.bar")
  }
  class myC extends myB {
    def foo: Unit = println("myC.foo")
  }

  def main(args: Array[String]): Unit = { 
    val a: A = new myA 
    a.foo
    val b: C = new myC 
    b.bar
    b.foo
  }
}
于 2010-11-30T08:30:21.807 に答える
17

無名関数のプレースホルダー構文

Scala 言語仕様から:

SimpleExpr1 ::= '_'

(構文カテゴリの) 式には、識別子が有効な場所にExprアンダースコア記号が埋め込まれている場合があります。_このような式は、後続のアンダースコアが連続するパラメーターを表す無名関数を表します。

Scala 言語の変更点から:

_ + 1                  x => x + 1
_ * _                  (x1, x2) => x1 * x2
(_: Int) * 2           (x: Int) => x * 2
if (_) x else y        z => if (z) x else y
_.map(f)               x => x.map(f)
_.map(_ + 1)           x => x.map(y => y + 1)

これを使用すると、次のようなことができます。

def filesEnding(query: String) =
  filesMatching(_.endsWith(query))
于 2009-07-05T05:50:15.923 に答える
16

暗黙の定義、特に変換。

たとえば、入力文字列の中央を「...」に置き換えることで、サイズに合わせて入力文字列をフォーマットする関数を想定します。

def sizeBoundedString(s: String, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

これは任意の String で使用でき、もちろん toString メソッドを使用して何でも変換できます。しかし、次のように書くこともできます。

def sizeBoundedString[T](s: T, n: Int)(implicit toStr: T => String): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

そして、これを行うことで他のタイプのクラスを渡すことができます:

implicit def double2String(d: Double) = d.toString

これで、double を渡してその関数を呼び出すことができます。

sizeBoundedString(12345.12345D, 8)

最後の引数は暗黙的であり、暗黙的な de 宣言のために自動的に渡されます。さらに、"s" は sizeBoundedString 内で String のように扱われます。これは、そこから String への暗黙的な変換があるためです。

この型の暗黙は、予期しない変換を避けるために、一般的でない型に対してより適切に定義されています。変換を明示的に渡すこともできますが、これは引き続き sizeBoundedString 内で暗黙的に使用されます。

sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)

複数の暗黙の引数を指定することもできますが、その場合、それらすべてを渡すか、いずれも渡さないようにする必要があります。暗黙的な変換のショートカット構文もあります。

def sizeBoundedString[T <% String](s: T, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

これはまったく同じように使用されます。

暗黙は任意の値を持つことができます。たとえば、ライブラリ情報を非表示にするために使用できます。たとえば、次の例を見てください。

case class Daemon(name: String) {
  def log(msg: String) = println(name+": "+msg)
}

object DefaultDaemon extends Daemon("Default")

trait Logger {
  private var logd: Option[Daemon] = None
  implicit def daemon: Daemon = logd getOrElse DefaultDaemon

  def logTo(daemon: Daemon) = 
    if (logd == None) logd = Some(daemon) 
    else throw new IllegalArgumentException

  def log(msg: String)(implicit daemon: Daemon) = daemon.log(msg)
}

class X extends Logger {
  logTo(Daemon("X Daemon"))

  def f = {
    log("f called")
    println("Stuff")
  }

  def g = {
    log("g called")(DefaultDaemon)
  }
}

class Y extends Logger {
  def f = {
    log("f called")
    println("Stuff")
  }
}

この例では、Y オブジェクトで "f" を呼び出すとログがデフォルトのデーモンに送信され、X のインスタンスでは Daemon X デーモンに送信されます。ただし、X のインスタンスで g を呼び出すと、明示的に指定された DefaultDaemon にログが送信されます。

この単純な例はオーバーロードとプライベート ステートで書き直すことができますが、インプリシットはプライベート ステートを必要とせず、インポートを使用してコンテキストに組み込むことができます。

于 2009-07-07T18:24:42.600 に答える
13

あまり隠されていないかもしれませんが、これは便利だと思います:

@scala.reflect.BeanProperty
var firstName:String = _

これにより、Bean 規則に一致するフィールドのゲッターとセッターが自動的に生成されます。

developerworksでの詳細な説明

于 2009-06-23T19:54:11.713 に答える
13

クロージャの暗黙の引数。

関数の引数は、メソッドと同様に暗黙的としてマークできます。関数の本体のスコープ内で、暗黙的なパラメーターが表示され、暗黙的な解決の対象となります。

trait Foo { def bar }

trait Base {
  def callBar(implicit foo: Foo) = foo.bar
}

object Test extends Base {
  val f: Foo => Unit = { implicit foo =>
    callBar
  }
  def test = f(new Foo {
    def bar = println("Hello")
  })
}
于 2011-02-16T10:19:39.583 に答える
12

結果の型は、暗黙的な解決に依存します。これにより、複数のディスパッチの形式が得られます。

scala> trait PerformFunc[A,B] { def perform(a : A) : B }
defined trait PerformFunc

scala> implicit val stringToInt = new PerformFunc[String,Int] {
  def perform(a : String)  = 5
}
stringToInt: java.lang.Object with PerformFunc[String,Int] = $anon$1@13ccf137

scala> implicit val intToDouble = new PerformFunc[Int,Double] {
  def perform(a : Int) = 1.0
}
intToDouble: java.lang.Object with PerformFunc[Int,Double] = $anon$1@74e551a4

scala> def foo[A, B](x : A)(implicit z : PerformFunc[A,B]) : B = z.perform(x)
foo: [A,B](x: A)(implicit z: PerformFunc[A,B])B

scala> foo("HAI")
res16: Int = 5

scala> foo(1)
res17: Double = 1.0
于 2010-08-10T02:38:48.333 に答える
12

StreamScala のs を使用して無限のデータ構造を構築する: http://www.codecommit.com/blog/scala/infinite-lists-for-the-finitely-patient

于 2010-06-24T16:15:50.943 に答える
4

Scala の Java 二重ブレース初期化子に相当するもの。

Scala では、そのクラスのインスタンスを初期化するためのステートメントを含むクラスの本体 (コンストラクター) を持つ匿名サブクラスを作成できます。

このパターンは、UI コンポーネントを作成し、それらのプロパティをより簡潔に宣言できるため、コンポーネントベースのユーザー インターフェイス ( Swing 、Vaadin など) を構築するときに非常に役立ちます。

詳細については、 http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdfを参照してください。

Vaadin ボタンを作成する例を次に示します。

val button = new Button("Click me"){
 setWidth("20px")
 setDescription("Click on this")
 setIcon(new ThemeResource("icons/ok.png"))
}
于 2011-07-12T08:49:28.023 に答える
3

importステートメントからのメンバーの除外

aとメソッドLoggerを含む aを使用したいが、エラー メッセージには 1 つしか使用せず、標準出力には古き良きものを残したいとします。あなたはこれを行うことができます:printlnprinterrPredef.println

val logger = new Logger(...)
import logger.printerr

しかし、loggerインポートして使用したい別の 12 個のメソッドも含まれている場合、それらをリストするのは不便です。代わりに試すことができます:

import logger.{println => donotuseprintlnt, _}

ただし、これはインポートされたメンバーのリストを「汚染」します。超強力なワイルドカードを入力してください:

import logger.{println => _, _}

そして、それはまさに正しいことをします™。

于 2011-09-26T16:31:28.037 に答える
2

requireメソッド ( で定義Predef) を使用して、実行時にチェックされる追加の関数制約を定義できます。さらに別の Twitter クライアントを開発していて、ツイートの長さを最大 140 シンボルに制限する必要があるとします。また、空のツイートを投稿することはできません。

def post(tweet: String) = {
  require(tweet.length < 140 && tweet.length > 0) 
  println(tweet)
 }

不適切な長さの引数を指定して post を呼び出すと、例外が発生します。

scala> post("that's ok")
that's ok

scala> post("")
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

scala> post("way to looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong tweet") 
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

複数の要件を記述したり、それぞれに説明を追加したりすることもできます。

def post(tweet: String) = {
  require(tweet.length > 0, "too short message")
  require(tweet.length < 140, "too long message")
  println(tweet)
}

例外は冗長になりました。

scala> post("")
java.lang.IllegalArgumentException: requirement failed: too short message
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:8)

もう 1 つの例を次に示します


ボーナス

要件が失敗するたびにアクションを実行できます。

scala> var errorcount = 0
errorcount: Int = 0

def post(tweet: String) = {
  require(tweet.length > 0, {errorcount+=1})
  println(tweet)
  }

scala> errorcount
res14: Int = 0

scala> post("")
java.lang.IllegalArgumentException: requirement failed: ()
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:9)
...

scala> errorcount
res16: Int = 1
于 2011-08-24T11:10:59.947 に答える
1

メソッドを持つトレイトabstract overrideは Scala の機能ですが、他の機能ほど広く宣伝されていません。修飾子を持つメソッドの意図は、abstract overrideいくつかの操作を実行し、呼び出しを委任することsuperです。次に、これらの特性をabstract overrideメソッドの具体的な実装と混合する必要があります。

trait A {
  def a(s : String) : String
}

trait TimingA extends A {
  abstract override def a(s : String) = {
    val start = System.currentTimeMillis
    val result = super.a(s)
    val dur = System.currentTimeMillis-start
    println("Executed a in %s ms".format(dur))
    result
  }
}

trait ParameterPrintingA extends A {
  abstract override def a(s : String) = {
    println("Called a with s=%s".format(s))
    super.a(s)
  }
}

trait ImplementingA extends A {
  def a(s: String) = s.reverse
}

scala> val a = new ImplementingA with TimingA with ParameterPrintingA

scala> a.a("a lotta as")
Called a with s=a lotta as
Executed a in 0 ms
res4: String = sa attol a

私の例は貧乏人の AOP にすぎませんが、これらのStackable Traitsを好きなように使用して、定義済みのインポート、カスタム バインディング、およびクラスパスを使用して Scala インタープリター インスタンスを構築しました。Stackable Traitsにより、次のラインに沿ってファクトリを作成new InterpreterFactory with JsonLibs with LuceneLibsし、ユーザー スクリプト用の便利なインポートとスコープ変数を使用できるようになりました。

于 2011-08-18T09:44:28.697 に答える