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)
次に、エクストラクターSome.unapply
を暗黙的に呼び出しています。
しかし、パターンでクラスインスタンスを使用することもできます。それがここで行われていることです。 val正規表現はRegex
のインスタンスであり、パターンで使用すると、暗黙的にregex.unapplySeq
を呼び出します(unapply
対unapplySeq
はこの回答の範囲)、一致グループをSeq[String]
に抽出し、その要素は変数year、month、dayに順番に割り当てられます。
Structural型定義-サポートするメソッドによって記述された型。例えば:
object Closer {
def using(closeable: { def close(): Unit }, f: => Unit) {
try {
f
} finally { closeable.close }
}
}
パラメーターcloseable
の-typeは、close
メソッドがある以外は定義されていないことに注意してください。
この機能がなければ、たとえば、関数をリストにマッピングして別のリストを返す、または関数をツリーにマッピングして別のツリーを返すというアイデアを表現できます。しかし、あなたはこの考えを表現することはできません一般的により高い種類なしでは。
上位の種類では、任意のタイプのアイデアをキャプチャできます。これは別のタイプでパラメーター化されます。 1つのパラメーターをとる型コンストラクターは、種類が(*->*)
と呼ばれます。たとえば、List
です。別の型コンストラクタを返す型コンストラクタは、種類が(*->*->*)
と呼ばれます。たとえば、Function1
。しかし、Scalaにはhigher種類があるため、他の型コンストラクターでパラメーター化された型コンストラクターを使用できます。したがって、それらは((*->*)->*)
のような種類です。
例えば:
trait Functor[F[_]] {
def fmap[A, B](f: A => B, fa: F[A]): F[B]
}
これで、Functor[List]
がある場合、リストにマップできます。 Functor[Tree]
がある場合、ツリー上にマップできます。しかし、もっと重要なのは、Functor[A]
種類Aの(*->*)
がある場合、A
に関数をマッピングできることです。
Extractors 乱雑なif-elseif-else
スタイルコードをパターンに置き換えることができます。これらは正確にはhiddenではないことを知っていますが、私は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)
}
これで、これはmuchより明確です
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)
}
Manifests これは、Scalaが型を具体化したかのように、実行時に型情報を取得する一種の方法です。
ケースクラスは、製品特性を自動的にミックスインし、リフレクションなしでフィールドへの型なしのインデックス付きアクセスを提供します。
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))
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)
正確に隠されているわけではありませんが、宣伝されていない機能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.scalaにある_implicit def augmentString
_のアプリケーションであるscala.this.Predef.augmentString("xx").r
に注意してください。
scalac -Xprint:<phase>は、コンパイラフェーズの後に構文ツリーを出力します。利用可能なフェーズを確認するには、scalac -Xshow-phasesを使用します。
これは、舞台裏で何が起こっているかを学ぶのに最適な方法です。
で試す
case class X(a:Int,b:String)
typerフェーズを使用して、その有用性を実感します。
独自の制御構造を定義できます。それは実際には単なる関数とオブジェクトであり、いくつかの構文上の砂糖ですが、見た目と振る舞いは本物のようです。
たとえば、次のコードは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:
一致表現に適用される注釈。存在する場合、コンパイラは一致がtableswitchまたはlookupswitchにコンパイルされたことを確認し、代わりに一連の条件式にコンパイルされるとエラーを発行します。
例:
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
}
}
in 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)
名前による呼び出しパラメータ(編集済み:これは遅延パラメータとは異なります!)を関数に指定できます。関数によって使用されるまで評価されません(編集:実際には、毎回再評価されます中古)。詳細については this faq をご覧ください
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
インラインであるため、追加のオーバーヘッドは発生しません。
「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
の)式には、識別子が有効な場所に埋め込みアンダースコア記号_
を含めることができます。このような式は、アンダースコアの後続の出現が連続したパラメータを示す匿名関数を表します。
From Scala Language Changes :
_ + 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))
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
with AbstractT2
句の前に、ブロックのvalue
フィールドを初期化して、匿名の内部クラスをインスタンス化します。これにより、value
は、AbstractT2
の本体が実行される前に初期化されます。これは、スクリプトを実行したときに示されます。
暗黙の定義、特に変換。
たとえば、入力文字列の中央を「...」に置き換えて、サイズに合わせて入力文字列をフォーマットする関数を想定します。
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内のストリングのように処理済みであるため、暗黙的な変換が行われているためです。
このタイプの暗黙は、予期しない変換を避けるために、一般的でないタイプに対してより適切に定義されます。明示的に変換を渡すこともできますが、それでも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のStream
sを使用して無限のデータ構造を構築します。 http://www.codecommit.com/blog/scala/infinite-lists-for-the-finitely-patient
結果タイプは暗黙的な解決に依存しています。これにより、複数のディスパッチの形式を提供できます。
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
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
ステートメントからメンバーを除外するLogger
メソッドとprintln
メソッドを含むprinterr
を使用したいが、エラーメッセージにのみ使用し、古き良きPredef.println
標準出力用。これを行うことができます:
val logger = new Logger(...)
import logger.printerr
しかし、logger
にもインポートして使用したい別の12のメソッドが含まれている場合、それらをリストするのは不便になります。代わりに試すことができます:
import logger.{println => donotuseprintlnt, _}
ただし、これはインポートされたメンバーのリストを「汚染」します。 über-powerfulワイルドカードを入力してください:
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つの例は here です。
要件が失敗するたびにアクションを実行できます。
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