标签:编程 scala functional programmi
上节我们介绍了新的数据类型Option:一个专门对付异常情况出现时可以有一致反应所使用的数据类型。Option可以使编程人员不必理会出现异常后应该如何处理结果,他只是获得了一个None值,但这个None值与他所期待的类型是一致的,他可以继续用处理这种类型数据的方法使用这个结果。不过遗憾的是我们通过None值只能知道某个计算没能得出结果,但到底发生了什么事Option并没有提供任何提示。这样我们也就无法向用户提供贴切的系统错误或着操作失误信息了。
这样我们就需要在Option的基础上添加一个扩展功能的新数据类型,让它可以返回一些异常描述:Either。可以想象Either在返回None的同时还要包含一个返回值,用来描述异常。那么这个None的形式就变成了None(e)了。我们先看看Eigher的框架设计:
trait Either[+E,+A] case class Left[+E](value: E) extends Either[E,Nothing] case class Right[+A](value: A) extends Either[Nothing,A]
提出了Either的基本描述后开始数据类型操作函数的实现:
def map[B](f: A => B): Either[E,B] = this match { case Right(a) => Right(f(a)) case Left(e) => Left(e) } def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = this match { case Left(e) => Left(e) case Right(a) => f(a) } def orElse[EE >: E, AA >: A](default: Either[EE, AA]): Either[EE, AA] = this match { case Left(_) => default case Right(a) => Right(a) }
在以下的函数中我们可以用一个函数 (A,B) => C 把两个Either[A],Either[B]组合成Either[C]:
//用递归算法 def map2[EE >: E, B, C](b: Either[EE, B])(f: (A,B) => C): Either[EE, C] = (this,b) match { case (Left(e),_) => Left(e) case (_, Left(e)) => Left(e) case (Right(a),Right(b)) => Right(f(a,b)) } //用for comprehension def map2_1[EE >: E, B, C](b: Either[EE, B])(f: (A,B) => C): Either[EE, C] = { for { aa <- this bb <- b } yield f(aa,bb) } //用 flatMap写 def map2_2[EE >: E, B, C](b: Either[EE, B])(f: (A,B) => C): Either[EE, C] = { flatMap(aa => b map(bb => f(aa,bb))) }
考虑map2时并不复杂:由于我只有一个利用低阶的函数(A,B) =??? ,我必须想办法把Either管子里的那个元素取出来计算完后塞到一个新的Either管子里去。以上我们已经实现了map,flatMap我们可以使用for comprehension来实现:
aa <- a: Either - 从Either管子取出元素
yield 产生新的Either。map2_1是for comprehension的直接写法。
由于我们有map和flatMap,我们可以试着用用Either:
case class Employee(name: String, age: Int, salary: Double) for { age <- Right(42) name <- Left("Invalid Name!") salary <- Right(10000.00) } yield Employee(name,age,salary) //> res0: ch4.either.Either[String,ch4.either.Employee] = Left(Invalid Name!) for { age <- Right(42) name <- Right("Jonny Cash!") salary <- Right(10000.00) } yield Employee(name,age,salary) //> res1: ch4.either.Either[Nothing,ch4.either.Employee] = Right(Employee(Jonny //| Cash!,42,10000.0))
当然,我们还是有可能对一个系列的Either类型值进行计算的,所以sequence,traverse这两个函数总是会用到的:
//用递归算法,用f把元素升格成Either后再用map2把连续的两个元素连接起来 def traverse[E,A,B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] = es match { case Nil => Right(Nil) case h :: t => (f(h) map2 traverse(t)(f))(_ :: _) } //用foldRight实现,用f把元素升格成Either后再用map2把连续的两个元素连接起来 def traverse_1[E,A,B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] = { es.foldRight[Either[E, List[B]]](Right(Nil))((h,t) => f(h).map2(t)(_ :: _)) } def sequence[E,A](es: List[Either[E,A]]): Either[E,List[A]] = es match { case Nil => Right(Nil) case h :: t => (h map2 sequence(t))(_ :: _) } def sequence_1[E,A](es: List[Either[E,A]]): Either[E,List[A]] = { traverse(es)(x => x) }
来个实际点的例子:
case class Name(value: String) case class Age(value: Int) case class Person(name: Name, age: Age) def mkName(name: String): Either[String, Name] = { if (name == "" || name == null) Left("Invalid Name") else Right(Name(name)) } //> mkName: (name: String)ch4.either.Either[String,ch4.either.Name] def mkAge(age: Int): Either[String,Age] = { if ( age < 0 ) Left("Invalid age") else Right(Age(age)) } //> mkAge: (age: Int)ch4.either.Either[String,ch4.either.Age] def mkPerson(name: String, age: Int): Either[String,Person] = { mkName(name).map2(mkAge(age))(Person(_,_)) } //> mkPerson: (name: String, age: Int)ch4.either.Either[String,ch4.either.Perso //| n] mkPerson("Tiger",18) //> res2: ch4.either.Either[String,ch4.either.Person] = Right(Person(Name(Tiger //| ),Age(18))) mkPerson("Tiger",-18) //> res3: ch4.either.Either[String,ch4.either.Person] = Left(Invalid age) mkPerson("",-1) //> res4: ch4.either.Either[String,ch4.either.Person] = Left(Invalid Name)
def map2_s[B, C](b: Either[String, B])(f: (A,B) => C): Either[String, C] = (this,b) match { case (Left(e),Left(ee)) => Left(e+ee) case (_, Left(e)) => Left(e) case (Left(e:String), _) => Left(e) case (Right(a),Right(b)) => Right(f(a,b)) }
def mkPerson(name: String, age: Int): Either[String,Person] = { mkName(name).map2_s(mkAge(age))(Person(_,_)) } //> mkPerson: (name: String, age: Int)ch4.either.Either[String,ch4.either.Perso //| n] mkPerson("Tiger",18) //> res2: ch4.either.Either[String,ch4.either.Person] = Right(Person(Name(Tiger //| ),Age(18))) mkPerson("Tiger",-18) //> res3: ch4.either.Either[String,ch4.either.Person] = Left(Invalid age) mkPerson("",-1) //> res4: ch4.either.Either[String,ch4.either.Person] = Left(Invalid NameInvali //| d age)
标签:编程 scala functional programmi
原文地址:http://blog.csdn.net/tiger_xc/article/details/44274537