在scala中判断一个对象是否是元组(Tuple)

在scala user邮件列表中看到一个函数,判断一个对象是否是元组。从这个函数中,Easwy才了解到原来模式匹配(Pattern Match)也可以应用于正则表达式,这使得在scala中使用正则表达式(regex)更加容易了。另外,这个函数还展现了scala的另外两个特点:

  1. 尽量使用递归解决方案,而不是使用循环。这样做的优点之一是避免使用变量,优点之二是代码简洁。是否有其它优点,Easwy仍在总结。但Easwy始终有个担心,递归会不会导致效率降低?会不会有堆栈溢出风险?
  2. 使用Option类型做为函数返回值。使用Option类型的好处很明显,这样你的函数既可以返回执行失败的情况(None),也可以在执行成功时给出有用的返回值。这比使用tru/false作为返回值方便很多。

函数主体如下,为了方便讲解,在前面加了行号:

    
1 val Ptrn = """scala.Tuple(\d+)""".r
2
3 def checka( x: Class[ _ ]) : Option[ Int ] = x.getName match {
4      case Ptrn( i ) => Some( i.toInt )
5      case _ => { val sc = x.getSuperclass; if( sc != null ) checka( sc ) else None }
6 }
7
8 def isTuple( x: AnyRef ) = if( x.isInstanceOf[ Product ]) checka( x.getClass ) else None
     
  1. 行1定义了一个Pattern对象,可以看到,在scala中使用正则表达式非常的简单。
  2. 行3定义了一个递归函数checka(),它的参数是Class[_],返回值是Option[Int]类型。参数”Class[_]”的意思是这个函数可以接受任意Class[A]类型,也就是接受任何参数化的Class类型(Type Parameterization)。

  3. 从行3的后半句,到行5,是一个模式匹配,检查类的名字是否匹配正则表达式Ptrn。如果类名匹配Ptrn,也就是说是一个Tuple,则返回它的维数。例如,对Tuple3返回Some(3)。如果类名不匹配Ptrn,递归调用checka()检查其父类是否为Tuple,如果全部失败,则返回None。
  4. 行8定义isTuple()函数,调用checka()判断是否为Tuple。它首先会检查x是否是一个Product实例,满足时才调用checka(),否则直接返回None。

Easwy感觉checka()函数中的递归写的不是很好,在看过”The Little Schemer”后,Easwy更倾向与下面的写法:


    
  def checka(x: Class[_]): Option[Int] = x match {
    case null => None
    case _ => x.getName match {
      case Ptrn(i) => Some(i.toInt)
      case _ => checka(x.getSuperclass)
    }
  }
     

下面是Easwy用来测试该函数的程序,全文如下:


    
object TestTuple {
  def main(args: Array[String]) {
    class ttt(a: Any, b: Any, c: Any) extends Tuple3(a, b, c)

    val test = List(new Tuple2(1, 2), new ttt(1, 2, 3), "Hello World")

    for (elem <- test)
      isTuple(elem) match {
        case None => println("Not Tuple")
        case Some(x) => println("Is Tuple" + x)
      }
  }

  val Ptrn = """scala.Tuple(\d+)""".r

  def checka(x: Class[_]): Option[Int] = x match {
    case null => None
    case _ => x.getName match {
      case Ptrn(i) => Some(i.toInt)
      case _ => checka(x.getSuperclass)
    }
  }

  def isTuple( x: AnyRef ) = if( x.isInstanceOf[ Product ]) checka( x.getClass ) else None
}
    
  

有兴趣的朋友可以编译运行一下,体会一下scala的简捷与优雅。

更多内容,请阅读易水博客上的其它文章。

[ 参考文档 ]

  • [scala-user] Trying to discover if a object is a tuple

“在scala中判断一个对象是否是元组(Tuple)”的一个回复

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注