在scala user邮件列表中看到一个函数,判断一个对象是否是元组。从这个函数中,Easwy才了解到原来模式匹配(Pattern Match)也可以应用于正则表达式,这使得在scala中使用正则表达式(regex)更加容易了。另外,这个函数还展现了scala的另外两个特点:
- 尽量使用递归解决方案,而不是使用循环。这样做的优点之一是避免使用变量,优点之二是代码简洁。是否有其它优点,Easwy仍在总结。但Easwy始终有个担心,递归会不会导致效率降低?会不会有堆栈溢出风险?
- 使用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定义了一个Pattern对象,可以看到,在scala中使用正则表达式非常的简单。
-
行3定义了一个递归函数checka(),它的参数是Class[_],返回值是Option[Int]类型。参数”Class[_]”的意思是这个函数可以接受任意Class[A]类型,也就是接受任何参数化的Class类型(Type Parameterization)。
- 从行3的后半句,到行5,是一个模式匹配,检查类的名字是否匹配正则表达式Ptrn。如果类名匹配Ptrn,也就是说是一个Tuple,则返回它的维数。例如,对Tuple3返回Some(3)。如果类名不匹配Ptrn,递归调用checka()检查其父类是否为Tuple,如果全部失败,则返回None。
- 行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)”的一个回复