すべての Scala 開発者が知っておくべき Scala の隠れた機能は何ですか?
回答ごとに 1 つの隠し機能をお願いします。
さて、もう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
( unapply
vsunapplySeq
はこの回答の範囲を超えています)。これにより、一致グループが に抽出されSeq[String]
、その要素が割り当てられます。変数年、月、日。
構造型定義 - つまり、サポートするメソッドによって記述される型。例えば:
object Closer {
def using(closeable: { def close(): Unit }, f: => Unit) {
try {
f
} finally { closeable.close }
}
}
パラメータの型は、メソッドcloseable
を持っている以外は定義されていないことに注意してくださいclose
この機能がないと、たとえば、リストに関数をマッピングして別のリストを返す、またはツリーに関数をマッピングして別のツリーを返すというアイデアを表現できます。しかし、高次の種類なしでは、この考えを一般的に表現することはできません。
上位の種類を使用すると、別の型でパラメーター化された任意の型のアイデアをキャプチャできます。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
。
乱雑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)
}
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)
Scala が型を具体化したかのように、実行時に型情報を取得する一種の方法であるマニフェスト。
ケース クラスは自動的に 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))
正確に隠されているわけではありませんが、確かに宣伝されていない機能です: 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フェーズを使用して、その有用性を実感してください。
独自の制御構造を定義できます。これは実際には関数とオブジェクト、およびいくつかの構文糖衣にすぎませんが、それらは本物のように見え、動作します。
たとえば、次のコードは 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)
@switch
Scala 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 {
これが本当に隠されているかどうかはわかりませんが、とてもいいと思います。
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
}
}
scala 2.8 では、 @specialized を汎用クラス/メソッドに追加できます。これにより、プリミティブ型 (AnyVal を拡張) 用のクラスの特別なバージョンが作成され、不要なボックス化/ボックス化解除のコストが節約されます。
class Foo[@specialized T]...
AnyVals のサブセットを選択できます。
class Foo[@specialized(Int,Boolean) T]...
Scala 2.8 では、デフォルト引数と名前付き引数が導入されました。これにより、Scala がケース クラスに追加する新しい「コピー」メソッドの追加が可能になりました。これを定義すると:
case class Foo(a: Int, b: Int, c: Int, ... z:Int)
既存の Foo に似た新しい Foo を作成したいが、"n" の値が異なるだけの場合は、次のように言えます。
foo.copy(n = 3)
言語の拡張。私はいつも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)
名前による呼び出しパラメーター (編集済み: これは遅延パラメーターとは異なります!) を関数に指定することができ、関数によって使用されるまで評価されません (編集: 実際、それが使用されるたびに再評価されます)。使用済み)。詳細はこちらのよくある質問をご覧ください
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
*/
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
インラインであるため、追加のオーバーヘッドは発生しません。
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
されることが保証されます。value
AbstractT2
「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
}
}
無名関数のプレースホルダー構文
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))
暗黙の定義、特に変換。
たとえば、入力文字列の中央を「...」に置き換えることで、サイズに合わせて入力文字列をフォーマットする関数を想定します。
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 にログが送信されます。
この単純な例はオーバーロードとプライベート ステートで書き直すことができますが、インプリシットはプライベート ステートを必要とせず、インポートを使用してコンテキストに組み込むことができます。
あまり隠されていないかもしれませんが、これは便利だと思います:
@scala.reflect.BeanProperty
var firstName:String = _
これにより、Bean 規則に一致するフィールドのゲッターとセッターが自動的に生成されます。
developerworksでの詳細な説明
クロージャの暗黙の引数。
関数の引数は、メソッドと同様に暗黙的としてマークできます。関数の本体のスコープ内で、暗黙的なパラメーターが表示され、暗黙的な解決の対象となります。
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")
})
}
結果の型は、暗黙的な解決に依存します。これにより、複数のディスパッチの形式が得られます。
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
Stream
Scala のs
を使用して無限のデータ構造を構築する: http://www.codecommit.com/blog/scala/infinite-lists-for-the-finitely-patient
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"))
}
import
ステートメントからのメンバーの除外aとメソッドLogger
を含む aを使用したいが、エラー メッセージには 1 つしか使用せず、標準出力には古き良きものを残したいとします。あなたはこれを行うことができます:println
printerr
Predef.println
val logger = new Logger(...)
import logger.printerr
しかし、logger
インポートして使用したい別の 12 個のメソッドも含まれている場合、それらをリストするのは不便です。代わりに試すことができます:
import logger.{println => donotuseprintlnt, _}
ただし、これはインポートされたメンバーのリストを「汚染」します。超強力なワイルドカードを入力してください:
import logger.{println => _, _}
そして、それはまさに正しいことをします™。
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
メソッドを持つトレイト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
し、ユーザー スクリプト用の便利なインポートとスコープ変数を使用できるようになりました。