すべてのScala開発者が知っておくべきScalaの隠された機能は何ですか?
回答ごとに1つの非表示機能をお願いします。
すべてのScala開発者が知っておくべきScalaの隠された機能は何ですか?
回答ごとに1つの非表示機能をお願いします。
回答:
もう1つ追加する必要がありました。Regex
Scalaのすべてのオブジェクトには、マッチグループにアクセスできるエクストラクター(上記の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
。
しかし、パターンでクラスインスタンスを使用することもできます。それがここで行われていることです。ヴァル・正規表現は、のインスタンスでありRegex
、そしてあなたは、パターンで使用するとき、あなたは暗黙的に呼び出しているregex.unapplySeq
(unapply
対unapplySeq
にマッチグループ抽出し、この答えの範囲を超えている)Seq[String]
に順番に割り当てられているの要素を変数年、月、日。
構造型定義-つまり、それがサポートするメソッドによって記述される型。例えば:
object Closer {
def using(closeable: { def close(): Unit }, f: => Unit) {
try {
f
} finally { closeable.close }
}
}
パラメータのタイプcloseable
は、close
メソッドがあること以外は定義されていないことに注意してください
この機能がなければ、たとえば、関数をリストにマッピングして別のリストを返す、または関数をツリーにマッピングして別のツリーを返すというアイデアを表現できます。しかし、一般的に、より高い種類なしではこのアイデアを表現することはできません。
より高い種類では、別の型でパラメーター化された任意の型のアイデアを取り込むことができます。1つのパラメーターを受け取る型コンストラクターは、種類と呼ばれます(*->*)
。たとえば、List
。別の型コンストラクタを返す型コンストラクタは、種類と呼ばれます(*->*->*)
。たとえば、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が型を具体化したかのように、実行時に型情報を取得する一種の方法であるマニフェスト。
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)
Caseクラスは自動的に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
}
}
お知らせscala.this.Predef.augmentString("xx").r
のアプリケーションであり、implicit def augmentString
Predef.scalaに存在します。
scalac -Xprint:<phase>は、コンパイラフェーズの後に構文ツリーを出力します。利用可能なフェーズを確認するには、scalac -Xshow-phasesを使用します。
これは、舞台裏で何が行われているのかを知るための優れた方法です。
で試す
case class X(a:Int,b:String)
タイパーフェーズを使用して、それがいかに便利かを実際に感じます。
独自の制御構造を定義できます。それは実際には単なる関数とオブジェクトであり、いくつかの構文上の砂糖ですが、それらは本物と同じように見え、動作します。
たとえば、次のコードは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)
zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero
。Scalazが必要です。
@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 {
これが本当に隠されているなら、Dunnoですが、私はそれがとても良いと思います。
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
}
}
var foo2barConverter: Foo ConvertTo Bar
、型パラメーターの順序は自明です。
Scala 2.8はデフォルトと名前付きの引数を導入しました。これにより、Scalaがケースクラスに追加する新しい「コピー」メソッドを追加できるようになりました。これを定義すると:
case class Foo(a: Int, b: Int, c: Int, ... z:Int)
そして、既存のFooのように、 "n"の値だけが異なる新しいFooを作成したい場合は、次のように言うだけです。
foo.copy(n = 3)
scala 2.8では、@ specializedをジェネリッククラス/メソッドに追加できます。これにより、プリミティブ型(AnyValを拡張)のクラスの特別なバージョンが作成され、不要なボックス化/ボックス化解除のコストが節約されます。
class Foo[@specialized T]...
AnyValsのサブセットを選択できます。
class Foo[@specialized(Int,Boolean) T]...
言語を拡張する。私はいつも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)
名前による呼び出しパラメーター(編集済み:これは遅延パラメーターとは異なります!)を関数に指定でき、関数によって使用されるまで評価されません(編集:実際には、再評価されるたびに再評価されます)中古)。詳細については、この 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
*/
lazy val xx: Bar = x
あなたのメソッドで何かのようなことをし、その瞬間からあなただけが使うなら、あなたはそれを遅延パラメータとして使うことができると思いますxx
。
を使用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のインスタンスでは、デーモン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
: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
foo
用途の定義はa
、これらのコマンドを実行する前に環境に存在していなければなりません。私はあなたが意味しz.perform(x)
たと思います。
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, _}
しかし、これはインポートされたメンバーのリストを「汚染」します。強力なワイルドカードを入力します。
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
require
予約語ではありません。これはで定義されたメソッドPredef
です。
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に過ぎませんが、事前定義されたインポート、カスタムバインディング、およびクラスパスを使用してScalaインタープリターインスタンスを構築するために、これらのStackableトレイトを多く使用しました。スタッカブル形質はの線に沿って私の工場を作成することが可能になりnew InterpreterFactory with JsonLibs with LuceneLibs
、その後、便利な輸入と範囲を持つユーザースクリプトのvaribles。