对于电梯开发,我有时需要使用match
- case
语句,如下所示。 (为了便于理解,重写为普通的scala。)给他们一个注意事项:这些实际上是不同的部分函数,在代码的不同部分定义,所以重要的是case语句在guard中或之前失败以使其他部分评估的函数(如果匹配失败,那就是)。
// The incoming request
case class Req(path: List[String], requestType: Int)
// Does some heavy database action (not shown here)
def findInDb(req: Req):Option[Int] =
if(req.path.length > 3) Some(2) else None
Req("a"::"b"::Nil, 3) match {
case r@Req(`path` :: _ :: Nil, 3) if findInDb(r).isDefined =>
doSomethingWith(findInDb(r))
case r@Req(`path` :: _ :: Nil, _) => doDefault
case _ => doNothing
}
现在,为了知道case
语句成功,我必须用findInDb
查询数据库并检查结果是否有效。之后,我必须再次调用它来使用该值。
做类似
的事情case r@Req(path, 3) if {val res = findInDb(r); res.isDefined} =>
不起作用,因为res
的范围仅限于括号内。
我当然可以在外面定义一个var res = _
并分配给它,但我感觉不太好。
是否可以通过任何方式在守卫内声明变量?如果可以case r@Req(…)
为什么不case r@Req() if res@(r.isDefined)
?
答案 0 :(得分:9)
你真的很亲密。缺少的关键部分是使用提取器而不是保护表达式。
object FindInDb{
def unapply(r:Req):Option[Int]= findInDb(r)
}
Req("a"::"b"::Nil, 3) match {
case dbResult@FindInDb(Req(`path` :: _ :: Nil, 3))=> doSomethingWith(dbResult)
case Req(`path` :: _ :: Nil, _) => doDefault
case _ => doNothing
}
实际上并不要求提取器只返回其参数中已存在的信息,这只是常见的用例。您实际上可以使用任何部分函数,将其提升为Option,并且能够匹配有关函数是否已定义及其值的信息。
答案 1 :(得分:3)
试试这个:
object FindInDB {
def unapply(req:Req) => {
//Your sophisticated logic
if(req.path.length > 3) Some((req, 2)) else None
}
然后在你的案例陈述中你可以这样做:
Req("a"::"b"::Nil, 3) match {
case FindInDb(r, n) => //Now you can see both the Req, and the Int
...
答案 2 :(得分:1)
重构if表达式在case语句中有什么问题?
Req("a"::"b"::Nil, 3) match {
case r@Req(`path` :: _ :: Nil, 3) =>
val res=findInDb(r)
if(res.isDefined) doSomethingWith(res)
else doDefault
case r@Req(`path` :: _ :: Nil, _) => doDefault
case _ => doNothing
}
答案 3 :(得分:1)
您可以创建一些基础结构,以便以较少暴露的方式包装var:
class Memory[M] {
// Could throw exceptions or whatnot if you tried to assign twice
private[this] var mem: Option[M] = None
def apply(om: Option[M]) = { mem = om; mem }
def apply(m: M) = { mem = Some(m); mem }
def apply() = { mem }
}
// Need to create an object that memorizes the correct type
object MemorizeInt {
def unapply[A](a: A) = Some((a,new Memory[Int]))
}
case class Req(path: List[String], requestType: Int)
def findInDb(req: Req) = if(req.path.length > 0) Some(2) else None
def doSomethingWith(oi: Option[Int]) {
println(oi)
}
Req("a"::"b"::Nil, 3) match {
case MemorizeInt(r@Req(path :: _ :: Nil, 3),m) if m(findInDb(r)).isDefined =>
doSomethingWith(m())
case r@Req(path :: _ :: Nil, _) => {}
case _ => {}
}
或者,您可以使用=>
将作品从map
移到条件中:
case class Req(path: List[String], requestType: Int)
def findInDb(req: Req) = if(req.path.length > 0) Some(2) else None
def doSomethingWith(i: Int) { println(i) }
Req("a"::"b"::Nil, 3) match {
case r@Req(path :: _ :: Nil, 3) if
findInDb(r).map(m => { doSomethingWith(m); m }).isDefined => {}
case r@Req(path :: _ :: Nil, _) => println("default")
case _ => println("messed up")
}
答案 4 :(得分:0)
你试过case r @ Req() if res@(r.isDefined)
吗?
scala> val t3 =(1, "one", 1.0)
t3: (Int, java.lang.String, Double) = (1,one,1.0)
scala> t3 match { case t @ (1, s, d) if t._3 < 2.0 => println("OK"); case _ => println("No-Go") }
OK