Code Examples for

Programming in Scala

Return to chapter index

24 Extractors

Sample run of chapter's interpreter examples

24.1 An example: Extracting email addresses


def isEMail(s: String): Boolean def domain(s: String): String def user(s: String): String
if (isEMail(s)) println(user(s) +" AT "+ domain(s) else println("not an email address")
EMail(user, domain)
// In file extractors/IsEmail.scala s match { case EMail(user, domain) => println(user +" AT "+ domain) case _ => println("not an email address") }
ss match { case EMail(u1, d1) :: EMail(u2, d2) :: _ if (u1 == u2) => ... ... }

24.2 Extractors


// In file extractors/EMail.scala object EMail { // The injection method (optional) def apply(user: String, domain: String) = user +"@"+ domain // The extraction method (mandatory) def unapply(str: String): Option[(String, String)] = { val parts = str split "@" if (parts.length == 2) Some(parts(0), parts(1)) else None } }
object EMail extends (String, String) => String { ... }
unapply("John@epfl.ch") equals Some("John", "epfl.ch") unapply("John Doe") equals None
selectorString match { case EMail(user, domain) => ... }
EMail.unapply(selectorString)
val x: Any = ... x match { case EMail(user, domain) => ... }
// In file extractors/Misc.scala EMail.unapply(EMail.apply(user, domain))
Some(user, domain)
EMail.unapply(obj) match { case Some(u, d) => EMail.apply(u, d) }

24.3 Patterns with zero or one variables


// In file extractors/Twice.scala object Twice { def apply(s: String): String = s + s def unapply(s: String): Option[String] = { val length = s.length / 2 val half = s.substring(0, length) if (half == s.substring(length)) Some(half) else None } }
// In file extractors/UpperCase.scala object UpperCase { def unapply(s: String): Boolean = s.toUpperCase == s }
// In file extractors/Test2.scala def userTwiceUpper(s: String) = s match { case EMail(Twice(x @ UpperCase()), domain) => "match: "+ x +" in domain "+ domain case _ => "no match" }
scala> userTwiceUpper("DIDI@hotmail.com") res0: java.lang.String = match: DI in domain hotmail.com scala> userTwiceUpper("DIDO@hotmail.com") res1: java.lang.String = no match scala> userTwiceUpper("didi@hotmail.com") res2: java.lang.String = no match

24.4 Variable argument extractors


// In file extractors/DomainTest.scala dom match { case Domain("org", "acm") => println("acm.org") case Domain("com", "sun", "java") => println("java.sun.com") case Domain("net", _*) => println("a .net domain") }
// In file extractors/Domain.scala object Domain { // The injection method (optional) def apply(parts: String*): String = parts.reverse.mkString(".") // The extraction method (mandatory) def unapplySeq(whole: String): Option[Seq[String]] = Some(whole.split("\\.").reverse) }
// In file extractors/DomainTest.scala def isTomInDotCom(s: String): Boolean = s match { case EMail("tom", Domain("com", _*)) => true case _ => false }
scala> isTomInDotCom("tom@sun.com") res3: Boolean = true scala> isTomInDotCom("peter@sun.com") res4: Boolean = false scala> isTomInDotCom("tom@acm.org") res5: Boolean = false
// In file extractors/ExpandedEMail.scala object ExpandedEMail { def unapplySeq(email: String) : Option[(String, Seq[String])] = { val parts = email split "@" if (parts.length == 2) Some(parts(0), parts(1).split("\\.").reverse) else None } }
scala> val s = "tom@support.epfl.ch" s: java.lang.String = tom@support.epfl.ch scala> val ExpandedEMail(name, topdom, subdoms @ _*) = s name: String = tom topdom: String = ch subdoms: Seq[String] = List(epfl, support)

24.5 Extractors and sequence patterns


List() List(x, y, _*) Array(x, 0, 0, _)
package scala object List { def apply[T](elems: T*) = elems.toList def unapplySeq[T](x: List[T]): Option[Seq[T]] = Some(x) ... }
List() List(1, 2, 3)

24.6 Extractors versus case classes


case C(...)

24.7 Regular expressions


scala> import scala.util.matching.Regex
scala> val Decimal = new Regex("(-)?(\\d+)(\\.\\d*)?") Decimal: scala.util.matching.Regex = (-)?(\d+)(\.\d*)?
scala> val Decimal = new Regex("""(-)?(\d+)(\.\d*)?""") Decimal: scala.util.matching.Regex = (-)?(\d+)(\.\d*)?
scala> val Decimal = """(-)?(\d+)(\.\d*)?""".r Decimal: scala.util.matching.Regex = (-)?(\d+)(\.\d*)?
package scala.runtime import scala.util.matching.Regex class RichString(self: String) ... { ... def r = new Regex(self) }
scala> val Decimal = """(-)?(\d+)(\.\d*)?""".r Decimal: scala.util.matching.Regex = (-)?(\d+)(\.\d*)? scala> val input = "for -1.0 to 99 by 3" input: java.lang.String = for -1.0 to 99 by 3 scala> for (s <- Decimal findAllIn input) | println(s) -1.0 99 3 scala> Decimal findFirstIn input res1: Option[String] = Some(-1.0) scala> Decimal findPrefixOf input res2: Option[String] = None
scala> val Decimal(sign, integerpart, decimalpart) = "-1.23" sign: String = - integerpart: String = 1 decimalpart: String = .23
scala> val Decimal(sign, integerpart, decimalpart) = "1.0" sign: String = null integerpart: String = 1 decimalpart: String = .0
scala> for (Decimal(s, i, d) <- Decimal findAllIn input) | println("sign: "+ s +", integer: "+ | i +", decimal: "+ d) sign: -, integer: 1, decimal: .0 sign: null, integer: 99, decimal: null sign: null, integer: 3, decimal: null

24.8 Conclusion

For more information about Programming in Scala (the "Stairway Book"), please visit:

http://www.artima.com/shop/programming_in_scala

and:

http://booksites.artima.com/programming_in_scala

Copyright © 2007-2008 Artima, Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.