8

比如说,我有一堆“验证”函数,如果没有错误则返回 None,否则返回 Some(String) 指定错误消息。类似于以下内容...

def validate1:Option[String] 
def validate2:Option[String]
def validate3:Option[String]

我将按顺序调用它们,一旦返回 Some(String),我就会停止并返回相同的值。如果它返回 None,我会转到下一个,直到序列结束。如果他们都返回None,我返回None。

我想将它们粘合在一起以“表达”。就像是 ...

for( a <- validate1; b <- validate2; c <- validate3) yield None;

但是,选项的流动与我在这里想要的完全相反。它在 None 处停止,然后是 Some(String)。

我怎样才能实现这样的目标?

4

4 回答 4

17

您可以将调用与 Option 上的orElse方法链接在一起

validate1 orElse validate2 orElse validate3

或者您可以对转换为函数的验证方法集合运行折叠

val vlist= List(validate1 _, validate2 _, validate3 _)

vlist.foldLeft(None: Option[String]) {(a, b) => if (a == None) b() else a}
于 2010-11-15T13:19:43.973 回答
3

scalaz库有一个名为的类型,Validation它允许一些令人难以置信的体操,同时构建错误和成功。例如,假设您有一些方法可以返回失败消息或某些成功结果(A/B/C):

import scalaz._; import Scalaz._
def fooA : ValidationNEL[String, A]
def fooB : ValidationNEL[String, B]
def fooC : ValidationNEL[String, C]

这些可以与应用函子一起使用,将调用链接在一起:

(foo1 <|**|> (foo2, foo3)) match {
  case Success( (a, b, c) ) => //woot
  case Failure(msgs)        => //erk
}

请注意,如果任何一个foo1/2/3失败,则整个组合将失败,并显示失败消息的非空列表(NEL)。如果不止一个失败,您会收到所有失败消息。

这是一个杀手级应用程序。Tor如何返回成功和失败的示例如下

def foo1 : ValidationNEL[String, Int] = 1.success
def foo2 : ValidationNEL[String, Double] = "some error msg".failNel
于 2010-11-16T10:12:57.263 回答
2

你不能把迭代器组合在一起然后取第一个元素吗?就像是:

scala> def validate1: Option[String] = {println("1"); None}
scala> def validate2: Option[String] = {println("2"); Some("error")}
scala> def validate3: Option[String] = {println("3"); None}
scala> (validate1.iterator ++ validate2.iterator ++ validate3.iterator).next
1
2
res5: String = error
于 2010-11-15T12:54:33.350 回答
0

我认为您可能会从使用 Lift 的Box中受益,它具有Full(ie Some)、Empty(ie None) 和Failure(anEmpty以及它是空的并且可以链接的原因)。David Pollak 有一篇很好的博客文章介绍了它。简而言之,你可能会做这样的事情(未经测试):

def validate1: Box[String]
def validate2: Box[String]
def validate3: Box[String]
val validation = for (
  validation1 <- validate1 ?~ "error message 1"
  validation2 <- validate2 ?~ "error message 2"
  validation3 <- validate3 ?~ "error message 3"
) yield "overall success message"

这并不比原始示例短,但在我看来,它更合乎逻辑,结果是在 a 中成功验证,FullFailure.

但是,我们可以变得更小。首先,由于我们的验证函数 return Box[String],它们可以Failure自己返回 s ,我们不需要转换EmptyFailure自己:

val validation = for (
  validation1 <- validate1
  validation2 <- validate2
  validation3 <- validate3
) yield "overall success message"

但是,Box也有一个方法,如果是,则or返回相同,否则返回另一个。这会给我们:BoxFullBox

val 验证 = validate1 或 validate2 或 validate3

但是,该行在第一次验证成功时停止,而不是在第一次失败时停止。制作另一种方法来做你想要的(可能称为unless?)可能是有意义的,尽管我不能说它真的比 for 理解方法有用得多。

但是,这里有一个小库拉皮条可以做到这一点:

scala> class Unless[T](a: Box[T]) {
     | def unless(b: Box[T]) = {
     | if (a.isEmpty) { a }
     | else b
     | }
     | }
defined class Unless

scala> implicit def b2U[T](b: Box[T]): Unless[T] = new Unless(b)
b2U: [T](b: net.liftweb.common.Box[T])Unless[T]

scala> val a = Full("yes")                                      
a: net.liftweb.common.Full[java.lang.String] = Full(yes)

scala> val b = Failure("no")                                    
b: net.liftweb.common.Failure = Failure(no,Empty,Empty)

scala> val c = Full("yes2")                                     
c: net.liftweb.common.Full[java.lang.String] = Full(yes2)

scala> a unless b
res1: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)

scala> a unless b unless c
res2: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)

scala> a unless c unless b
res3: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)

scala> a unless c
res4: net.liftweb.common.Box[java.lang.String] = Full(yes2)

这是基于我对 Scala 类型系统的有限理解的快速破解,您可以在以下错误中看到:

scala> b unless a
<console>:13: error: type mismatch;
 found   : net.liftweb.common.Full[java.lang.String]
 required: net.liftweb.common.Box[T]
       b unless a
                ^

但是,这应该足以让您走上正轨。

当然Lift ScalaDocs有更多关于Box的信息。

于 2010-11-15T14:10:49.973 回答