私は自分がこのようなことをかなり頻繁に書いていることに気づきました:
_a match {
case `b` => // do stuff
case _ => // do nothing
}
_
ある値がパターンに一致するかどうかを確認する短い方法はありますか?つまり、この場合はif (a == b) // do stuff
と書くことができますが、パターンがもっと複雑な場合はどうでしょうか。リストまたは任意の複雑さのパターンと照合する場合のように。私はこのようなものを書くことができるようにしたいと思います:
_if (a matches b) // do stuff
_
私はScalaに比較的慣れていないので、何か大きなものが足りない場合はご容赦ください:)
これがまさに私がこれらの関数を書いた理由ですが、誰も言及していないため、明らかに印象的にあいまいです。
scala> import PartialFunction._
import PartialFunction._
scala> cond("abc") { case "def" => true }
res0: Boolean = false
scala> condOpt("abc") { case x if x.length == 3 => x + x }
res1: Option[Java.lang.String] = Some(abcabc)
scala> condOpt("abc") { case x if x.length == 4 => x + x }
res2: Option[Java.lang.String] = None
Scalaのmatch
演算子は、関数型スタイルで使用すると最も強力です。つまり、case
ステートメントで「何かをする」のではなく、次のようになります。有用な値。命令型スタイルの例を次に示します。
var value:Int = 23
val command:String = ... // we get this from somewhere
command match {
case "duplicate" => value = value * 2
case "negate" => value = -value
case "increment" => value = value + 1
// etc.
case _ => // do nothing
}
println("Result: " + value)
上記の「何もしない」は余計に見えるので少し痛いのはよくわかります。ただし、これは上記が命令型で書かれているためです。このような構成が必要になる場合もありますが、多くの場合、コードを機能スタイルにリファクタリングできます。
val value:Int = 23
val command:String = ... // we get this from somewhere
val result:Int = command match {
case "duplicate" => value * 2
case "negate" => -value
case "increment" => value + 1
// etc.
case _ => value
}
println("Result: " + result)
この場合、たとえば変数に割り当てることができる値として、match
ステートメント全体を使用します。また、match
ステートメントがどのような場合でも値を返さなければならないこともはるかに明白です。最後のケースが欠落している場合、コンパイラーは単に何かを補うことができませんでした。
これは好みの問題ですが、一部の開発者は、このスタイルがより現実的な例でより透過的で扱いやすいと考えています。 Scalaプログラミング言語の発明者は、match
のより機能的な使用を念頭に置いていたに違いありません。実際、if
ステートメントの方が理にかなっています。特定のアクションを実行する必要があるかどうかを判断するだけで済みます(一方、戻り値もあるため、if
を機能的に使用することもできます...)
これは役立つかもしれません:
_class Matches(m: Any) {
def matches[R](f: PartialFunction[Any, R]) { if (f.isDefinedAt(m)) f(m) }
}
implicit def any2matches(m: Any) = new Matches(m)
scala> 'c' matches { case x: Int => println("Int") }
scala> 2 matches { case x: Int => println("Int") }
Int
_
ここで、問題の一般的な性質について説明します。
パターンマッチングが発生する可能性のある場所は、val
、case
、およびfor
の3つです。それらのルールは次のとおりです。
_// throws an exception if it fails
val pattern = value
// filters for pattern, but pattern cannot be "identifier: Type",
// though that can be replaced by "id1 @ (id2: Type)" for the same effect
for (pattern <- object providing map/flatMap/filter/withFilter/foreach) ...
// throws an exception if none of the cases match
value match { case ... => ... }
_
ただし、case
が表示される可能性がある別の状況があります。これは、関数リテラルと部分関数リテラルです。例えば:
_val f: Any => Unit = { case i: Int => println(i) }
val pf: PartialFunction[Any, Unit] = { case i: Int => println(i) }
_
関数と部分関数の両方が、caseステートメントのいずれにも一致しない引数で呼び出された場合に例外をスローします。ただし、部分関数には、一致するかどうかをテストできるisDefinedAt
というメソッドと、_PartialFunction[T, R]
_をに変換するlift
というメソッドもあります。 _Function[T, Option[R]]
_。これは、値が一致しない場合、例外をスローする代わりにNone
になることを意味します。
一致は、多くの異なるテストの組み合わせです。
_// assign anything to x
case x
// only accepts values of type X
case x: X
// only accepts values matches by pattern
case x @ pattern
// only accepts a value equal to the value X (upper case here makes a difference)
case X
// only accepts a value equal to the value of x
case `x`
// only accept a Tuple of the same arity
case (x, y, ..., z)
// only accepts if extractor(value) returns true of Some(Seq()) (some empty sequence)
case extractor()
// only accepts if extractor(value) returns Some something
case extractor(x)
// only accepts if extractor(value) returns Some Seq or Tuple of the same arity
case extractor(x, y, ..., z)
// only accepts if extractor(value) returns Some Tuple2 or Some Seq with arity 2
case x extractor y
// accepts if any of the patterns is accepted (patterns may not contain assignable identifiers)
case x | y | ... | z
_
現在、エクストラクタはメソッドunapply
またはunapplySeq
であり、最初はBoolean
または_Option[T]
_を返し、2番目は_Option[Seq[T]]
_を返します。ここでNone
は一致が行われないことを意味し、Some(result)
は上記のようにresult
と一致しようとします。
したがって、ここにはあらゆる種類の構文上の選択肢がありますが、パターンの一致が発生する可能性のある3つの構造のいずれかを使用しないと不可能です。値の同等性や抽出機能など、一部の機能をエミュレートできる場合がありますが、すべてではありません。
パターンは、式にも使用できます。コードサンプル
a match {
case b => // do stuff
case _ => // do nothing
}
その後、次のように表すことができます
for(b <- Some(a)) //do stuff
秘訣は、をラップして有効な列挙子にすることです。例えば。 List(a)も機能しますが、Some(a)は意図した意味に最も近いと思います。
私が思いつくことができる最高のものはこれです:
def matches[A](a:A)(f:PartialFunction[A, Unit]) = f.isDefinedAt(a)
if (matches(a){case ... =>}) {
//do stuff
}
ただし、これではスタイルポイントは獲得できません。
キムの答え 要件に合わせて「改善」することができます。
class AnyWrapper[A](wrapped: A) {
def matches(f: PartialFunction[A, Unit]) = f.isDefinedAt(wrapped)
}
implicit def any2wrapper[A](wrapped: A) = new AnyWrapper(wrapped)
その後:
val a = "a" :: Nil
if (a matches { case "a" :: Nil => }) {
println("match")
}
しかし、私はそれをしません。 => }) {
シーケンスはここでは本当に醜く、コード全体は通常の一致よりもはるかに明確ではありません。さらに、暗黙的な変換を検索するコンパイル時のオーバーヘッドと、一致をPartialFunction
でラップする実行時のオーバーヘッドが発生します(他の定義済みのmatches
メソッド(String
)のようなメソッド。
少し見栄えを良くする(そして冗長性を減らす)ために、この定義をAnyWrapper
に追加できます。
def ifMatch(f: PartialFunction[A, Unit]): Unit = if (f.isDefinedAt(wrapped)) f(wrapped)
次のように使用します。
a ifMatch { case "a" :: Nil => println("match") }
これにより、case _ =>
行が節約されますが、単一のステートメントではなくブロックが必要な場合は、二重中括弧が必要です...それほど良くありません。
この構成は、副作用のあるものを実行するためにのみ使用できるため、実際には関数型プログラミングの精神ではないことに注意してください。関数が部分的であるため、値を返すためにそれを簡単に使用することはできません(したがって、Unit
戻り値)—デフォルト値が必要です。または、Option
インスタンスを返すことができます。 。しかし、ここでも、おそらくマッチでそれをアンラップするので、何も得られません。
率直に言って、これらのmatch
を頻繁に見て使用することに慣れ、この種の命令型構造から離れたほうがよいでしょう(以下 マドックの素晴らしい説明 )。