ScalaのExtractorを検討する
このコンセプト(Extractor)に初めて触れるのは、ちょっと理解しにくいですが、本人が英語が通じないのかもしれません.推敲を重ねた結果、何が起こっているのかやっと分かった.やはり一つの例から言えば.
文字列のフォーマットがメールアドレスのフォーマットに合致するかどうかを検証したいとします.もしそうであれば、ユーザー部分とドメイン名部分を抽出します.たとえば、指定された文字列"[email protected]」と、テストによりメールアドレスのフォーマットに合致することを発見し、「jack」と「163」を抽出する.com".一般的には、正規表現でマッチングを行い、対応するマッチンググループを抽出する.
Scalaではモードマッチングにより実現できる.具体的には、オブジェクト(objと呼んでもよい)にunapplyメソッドを定義し、このメソッドはOption[T]タイプを返さなければならない.オブジェクトを指定したパラメータ(selectorと呼んでもよい)とモードマッチングすると、オブジェクトのunapplyメソッドが呼び出されます.unapplyメソッドを呼び出すと、一致するパラメータ(selector)が入力されます.マッチングロジック定義unapplyメソッドでは、マッチングに成功した場合、unapplyメソッドはSome[T]タイプのオブジェクトを返し、そうでなければNoneを返します.Some()に含まれるコンテンツには制限はありませんが、一致するコンテンツを持ち帰るのが一般的です.
たとえば
実際にstrがEmailとモードマッチングすると
extractorとは、unapplyメソッド(またはunapplySeqメソッド)を含むオブジェクトを指す.
文字列が合法的なメールアドレスであるかどうかを確認したい場合は、unapplyにBooleanタイプを返すことができます.
メールのユーザー名に大文字が含まれておらず、重複する2つの部分から構成されている場合は、たとえば[email protected]ああ、そうすることができます.
もちろん正規表現で実現できますが、それはずっと複雑で柔軟性に欠けていると信じています.
selectorの複数のコンポーネントをマッチングまたは分解する必要があり、事前にどれだけあるか分からない場合は、unapplySeqメソッドを使用します.unapplySeqの使い方はunapplyとあまり差がありませんが、Option[Seq[T]]タイプを返さなければなりません.例えば私は[email protected]のメールドメイン名の各構成部分を抽出し,cn,edu,gdut.次の例は、メールユーザー名の接頭辞がstu_であることを示しています.ドメイン名cnのメールアドレス:
もちろんunapplyを使ってもいいですが、書き方はそんなに直感的ではありません.
実はScalaのArray,Listなどの集合クラスはunapplySeqメソッドを実現し,このように書くことができる.
Constructor Patternに似ているように見えますが.
extractorに対する理解が「ユーザー定義のパターンマッチングが可能」という技術的な面にとどまっている場合は、あまりにも浅いと思います.ScalaがExtractorという文法糖を提供する目的は、データモデルとビューを論理的に分離したり、アダプタのような役割を果たしたりして、関数式を比較することだと思います.
実際のプログラミングでcase classを採用すべきかextractorを採用すべきかについては、公式の提案は次のとおりです.
1. 定義したデータ構造やインタフェースが内部使用に限られ、頻繁に変更されない場合はcase classを推奨します.
2. インタフェースが他の人に使用されている場合、またはいくつかの残留クラスに直面している場合は、extractorを使用することをお勧めします.
3. 間違っている場合はcase classを採用し、case classが需要の変化に適応できないことを発見したらextractorを変更することができます.
case classを使用してモードマッチングを行うメリットは、コンパイラがコードを最適化できることです.もしあなたのcase classがパッケージクラス(sealキーワードで修飾されたクラス)から継承されている場合、コンパイラはmatch式をチェックし、可能性のある状況を漏らしたかどうかを注意することもできます.extractorは柔軟で、あなたが望むマッチングロジックを実現できますが、case classよりも実行効率が遅いです.
文字列のフォーマットがメールアドレスのフォーマットに合致するかどうかを検証したいとします.もしそうであれば、ユーザー部分とドメイン名部分を抽出します.たとえば、指定された文字列"[email protected]」と、テストによりメールアドレスのフォーマットに合致することを発見し、「jack」と「163」を抽出する.com".一般的には、正規表現でマッチングを行い、対応するマッチンググループを抽出する.
Scalaではモードマッチングにより実現できる.具体的には、オブジェクト(objと呼んでもよい)にunapplyメソッドを定義し、このメソッドはOption[T]タイプを返さなければならない.オブジェクトを指定したパラメータ(selectorと呼んでもよい)とモードマッチングすると、オブジェクトのunapplyメソッドが呼び出されます.unapplyメソッドを呼び出すと、一致するパラメータ(selector)が入力されます.マッチングロジック定義unapplyメソッドでは、マッチングに成功した場合、unapplyメソッドはSome[T]タイプのオブジェクトを返し、そうでなければNoneを返します.Some()に含まれるコンテンツには制限はありませんが、一致するコンテンツを持ち帰るのが一般的です.
たとえば
object Email{
def unapply(str: String): Option[(String, String)] = {
val parts = str split "@"
if (parts.length == 2) Some(parts(0), parts(1)) else None
}
}
val str = "[email protected]"
str match{
case Email(username, address) => println("username: "+username+" address: "+addres);
case _ => println("this is not an email address ");
}
実際にstrがEmailとモードマッチングすると
Email.unapply(str) match{
case Some(username, address) => ...
case None => ...
}
extractorとは、unapplyメソッド(またはunapplySeqメソッド)を含むオブジェクトを指す.
文字列が合法的なメールアドレスであるかどうかを確認したい場合は、unapplyにBooleanタイプを返すことができます.
メールのユーザー名に大文字が含まれておらず、重複する2つの部分から構成されている場合は、たとえば[email protected]ああ、そうすることができます.
object LowerCase{
def unapply(name: String) = name.toLowerCase == name
}
object Twice{
def unapply(s: String) : Option[String] = {
val len = s.length /2
val half = s.substring(0, len);
if (half == s.substring(len)) Some(half) else None
}
}
def userTwiceLower(s: String) = s match{
case Email(Twice(x @ LowerCase()), domain) => "match: "+ x + " in domain " + domain
case _ => "no mach"
}
userTwiceLower("[email protected]")
userTwiceLower("[email protected]")
userTwiceLower("[email protected]")
もちろん正規表現で実現できますが、それはずっと複雑で柔軟性に欠けていると信じています.
selectorの複数のコンポーネントをマッチングまたは分解する必要があり、事前にどれだけあるか分からない場合は、unapplySeqメソッドを使用します.unapplySeqの使い方はunapplyとあまり差がありませんが、Option[Seq[T]]タイプを返さなければなりません.例えば私は[email protected]のメールドメイン名の各構成部分を抽出し,cn,edu,gdut.次の例は、メールユーザー名の接頭辞がstu_であることを示しています.ドメイン名cnのメールアドレス:
object Email { ... }
object Domain{
def unapplySeq(whole: String) : Option[Seq[String]] ={
Some(whole.split("\\.").reverse);
}
}
object StuPrefix{
def unapply(name: String) = name.startsWith("stu_")
}
def isStuCnMail(str: String) = str match{
case Email(StuPrefix(), Domain("cn", _*)) => true
case _ => false
}
isStuCnMail("[email protected]")
もちろんunapplyを使ってもいいですが、書き方はそんなに直感的ではありません.
実はScalaのArray,Listなどの集合クラスはunapplySeqメソッドを実現し,このように書くことができる.
val Array(a,b,c,d) = Array(1,2,3,4)
val List(head, tail @ _ *) = List(1,2,3,4)
Constructor Patternに似ているように見えますが.
extractorに対する理解が「ユーザー定義のパターンマッチングが可能」という技術的な面にとどまっている場合は、あまりにも浅いと思います.ScalaがExtractorという文法糖を提供する目的は、データモデルとビューを論理的に分離したり、アダプタのような役割を果たしたりして、関数式を比較することだと思います.
実際のプログラミングでcase classを採用すべきかextractorを採用すべきかについては、公式の提案は次のとおりです.
1. 定義したデータ構造やインタフェースが内部使用に限られ、頻繁に変更されない場合はcase classを推奨します.
2. インタフェースが他の人に使用されている場合、またはいくつかの残留クラスに直面している場合は、extractorを使用することをお勧めします.
3. 間違っている場合はcase classを採用し、case classが需要の変化に適応できないことを発見したらextractorを変更することができます.
case classを使用してモードマッチングを行うメリットは、コンパイラがコードを最適化できることです.もしあなたのcase classがパッケージクラス(sealキーワードで修飾されたクラス)から継承されている場合、コンパイラはmatch式をチェックし、可能性のある状況を漏らしたかどうかを注意することもできます.extractorは柔軟で、あなたが望むマッチングロジックを実現できますが、case classよりも実行効率が遅いです.