9

我有多个Option。我想检查它们是否具有价值。如果OptionNone,我想就此回复用户。否则继续。

这就是我所做的:

val name:Option[String]
val email:Option[String]
val pass:Option[String]
val i = List(name,email,pass).find(x => x match{
  case None => true
  case _ => false
})
i match{
  case Some(x) => Ok("Bad Request")
  case None => {
    //move forward
  }
}

上面我可以用 替换findcontains但这是一种非常肮脏的方式。我怎样才能让它优雅和单子?

编辑:我也想知道什么元素是None.

4

8 回答 8

9

另一种方法是为了理解:

val outcome = for {
  nm <- name
  em <- email
  pwd <- pass
  result = doSomething(nm, em, pwd) // where def doSomething(name: String, email: String, password: String): ResultType = ???
} yield (result)

这将生outcome成为Some(result),您可以通过各种方式查询它(集合类可用的所有方法:map、filter、foreach 等)。例如:

outcome.map(Ok(result)).orElse(Ok("Bad Request"))
于 2013-08-17T10:58:56.280 回答
7
  val ok = Seq(name, email, pass).forall(_.isDefined)
于 2013-08-17T09:45:56.377 回答
4
val response = for {
  n <- name
  e <- email
  p <- pass
} yield {
  /* do something with n, e, p */
}
response getOrElse { /* bad request /* }

或者,使用 Scalaz:

val response = (name |@| email |@| pass) { (n, e, p) =>
  /* do something with n, e, p */
}
response getOrElse { /* bad request /* }
于 2013-08-17T08:30:29.680 回答
4

如果你想重用代码,你可以这样做

def allFieldValueProvided(fields: Option[_]*): Boolean = fields.forall(_.isDefined)

如果您想知道所有缺失值,那么您可以找到所有缺失值,如果没有,那么您就可以开始了。

def findMissingValues(v: (String, Option[_])*) = v.collect { 
  case (name, None) => name
}

val missingValues = findMissingValues(("name1", option1), ("name2", option2), ...)

if(missingValues.isEmpty) {
  Ok(...)
} else {
  BadRequest("Missing values for " + missingValues.mkString(", ")))
}
于 2013-08-17T13:27:01.753 回答
3
if ((name :: email :: pass :: Nil) forall(!_.isEmpty)) {
} else {
   // bad request
}
于 2013-08-17T10:30:20.833 回答
2

我认为最直接的方法是:

(name,email,pass) match {
  case ((Some(name), Some(email), Some(pass)) => // proceed
  case _ => // Bad request
}
于 2013-08-17T09:20:12.857 回答
1

带有石刀和熊皮的版本:

import util._

object Test extends App {

  val zero: Either[List[Int], Tuple3[String,String,String]] = Right((null,null,null))
  def verify(fields: List[Option[String]]) = {
    (zero /: fields.zipWithIndex) { (acc, v) => v match {
        case (Some(s), i) => acc match {
          case Left(_)  => acc
          case Right(t) =>
            val u = i match {
              case 0 => t copy (_1 = s)
              case 1 => t copy (_2 = s)
              case 2 => t copy (_3 = s)
            }
            Right(u)
        }
        case (None, i) =>
          val fails = acc match {
            case Left(f)  => f
            case Right(_) => Nil
          }
          Left(i :: fails)
      }
    }
  }
  def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
  def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)

  val name:Option[String] = Some("Bob")
  val email:Option[String]= None
  val pass:Option[String] = Some("boB")

  val res = verify(List(name,email,pass))
  res.fold(fail, (consume _).tupled)
  val res2 = verify(List(name, Some("bob@bob.org"),pass))
  res2.fold(fail, (consume _).tupled)
}

同样的事情,使用反射来概括元组副本。

缺点是你必须告诉它期望返回什么元组。在这种形式下,反射就像石器时代的进步之一,它们是如此神奇,以至于在推特上流行了一万年。

  def verify[A <: Product](fields: List[Option[String]]) = {
    import scala.reflect.runtime._
    import universe._
    val MaxTupleArity = 22
    def tuple = {
      require (fields.length <= MaxTupleArity)
      val n = fields.length
      val tupleN = typeOf[Tuple2[_,_]].typeSymbol.owner.typeSignature member TypeName(s"Tuple$n")
      val init = tupleN.typeSignature member nme.CONSTRUCTOR
      val ctor = currentMirror reflectClass tupleN.asClass reflectConstructor init.asMethod
      val vs = Seq.fill(n)(null.asInstanceOf[String])
      ctor(vs: _*).asInstanceOf[Product]
    }
    def zero: Either[List[Int], Product] = Right(tuple)
    def nextProduct(p: Product, i: Int, s: String) = {
      val im = currentMirror reflect p
      val ts = im.symbol.typeSignature
      val copy = (ts member TermName("copy")).asMethod
      val args = copy.paramss.flatten map { x =>
        val name = TermName(s"_$i")
        if (x.name == name) s
        else (im reflectMethod (ts member x.name).asMethod)()
      }
      (im reflectMethod copy)(args: _*).asInstanceOf[Product]
    }
    (zero /: fields.zipWithIndex) { (acc, v) => v match {
        case (Some(s), i) => acc match {
          case Left(_)  => acc
          case Right(t) => Right(nextProduct(t, i + 1, s))
        }
        case (None, i) =>
          val fails = acc match {
            case Left(f)  => f
            case Right(_) => Nil
          }
          Left(i :: fails)
      }
    }.asInstanceOf[Either[List[Int], A]]
  }

  def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
  def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)

  val name:Option[String] = Some("Bob")
  val email:Option[String]= None
  val pass:Option[String] = Some("boB")

  type T3 = Tuple3[String,String,String]
  val res = verify[T3](List(name,email,pass))
  res.fold(fail, (consume _).tupled)
  val res2 = verify[T3](List(name, Some("bob@bob.org"),pass))
  res2.fold(fail, (consume _).tupled)
于 2013-08-18T03:20:57.923 回答
0

我知道这不能很好地扩展,但这就足够了吗?

(name, email, pass) match {
  case (None, _, _) => "name"
  case (_, None, _) => "email"
  case (_, _, None) => "pass"
  case _ => "Nothing to see here"
}
于 2013-08-18T09:16:00.190 回答