2010-08-20 19 views
8

Để phát triển thang máy, đôi khi tôi cần sử dụng các câu lệnh match - case như sau. Một lưu ý cho họ: Đây thực sự là một phần chức năng khác nhau, được định nghĩa trong các phần khác nhau của mã, vì vậy điều quan trọng là một tuyên bố trường hợp không thành công trong hoặc trước bảo vệ để có phần khác các hàm được đánh giá (nếu khớp không thành công, nghĩa là).Phạm vi các biến bên trong tuyên bố bảo vệ trường hợp của scala

// 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 [email protected](`path` :: _ :: Nil, 3) if findInDb(r).isDefined => 
    doSomethingWith(findInDb(r)) 
    case [email protected](`path` :: _ :: Nil, _) => doDefault 
    case _ => doNothing 
} 

Bây giờ, để biết rằng tuyên bố case thành công, tôi phải truy vấn cơ sở dữ liệu với findInDb và kiểm tra xem kết quả là hợp lệ. Sau đó, tôi phải gọi lại lần nữa để sử dụng giá trị.

Làm cái gì đó như

case [email protected](path, 3) if {val res = findInDb(r); res.isDefined} => 

không làm việc vì phạm vi res sau đó được giới hạn ở bên trong dấu ngoặc.

Tôi có thể định nghĩa một bên là var res = _ bên ngoài và gán cho nó, nhưng tôi không cảm thấy khỏe khi làm điều này.

Có bằng bất kỳ phương tiện nào có thể khai báo biến trong bảo vệ không? Nếu có thể thực hiện case [email protected](…) tại sao không phải là case [email protected]() if [email protected](r.isDefined)?

Trả lời

0

Bạn đã thử case r @ Req() if [email protected](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 
+0

Tôi hoàn toàn không hiểu câu trả lời của bạn. Như tôi đã viết (hoặc ít nhất là có hy vọng rằng mọi người sẽ tin rằng tôi thực sự đã thử nó), tôi đã thử sử dụng 'if res @ (something)' và nó không phải là cú pháp hợp lệ. – Debilski

9

Bạn thực sự rất thân thiết. Phần quan trọng bị thiếu là sử dụng một bộ giải nén thay vì một biểu thức bảo vệ.

object FindInDb{ 
    def unapply(r:Req):Option[Int]= findInDb(r)  
} 

Req("a"::"b"::Nil, 3) match { 
    case [email protected](Req(`path` :: _ :: Nil, 3))=> doSomethingWith(dbResult) 
    case Req(`path` :: _ :: Nil, _) => doDefault 
    case _ => doNothing 
} 

Trình giải nén không bắt buộc phải trả lại thông tin đã có trong đối số của họ, đó chỉ là trường hợp sử dụng phổ biến. Bạn thực sự có thể sử dụng bất kỳ chức năng một phần nào, nâng nó lên Option, và có thể kết hợp trên thông tin về cả hai cho dù chức năng được xác định và giá trị của nó.

+0

Hmm. Cách tiếp cận thú vị. Tôi biết kỹ thuật nhưng không tin tôi có thể sử dụng nó trong trường hợp đó. Tôi sẽ suy nghĩ về việc liệu nó có hữu ích trong trường hợp của tôi hay không. – Debilski

+0

Nó nhận được khá sai lầm, tất nhiên, khi tôi muốn sử dụng không chỉ 'req' mà còn' id' như trong 'trường hợp req @ Req (\' path \ ':: id :: Nil, _, _)' … Chúng ta hãy hy vọng điều này sẽ không xảy ra. – Debilski

+0

Nên là tốt, thực sự. Không có lý do bạn không thể sử dụng @ -patterns hoặc ràng buộc các biến mới bên trong args vào một bộ giải nén. –

1

Có vấn đề gì với việc tái cấu trúc biểu thức nếu nằm trong câu lệnh case?

Req("a"::"b"::Nil, 3) match { 
    case [email protected](`path` :: _ :: Nil, 3) => 
    val res=findInDb(r) 
    if(res.isDefined) doSomethingWith(res) 
    else doDefault 
    case [email protected](`path` :: _ :: Nil, _) => doDefault 
    case _ => doNothing 
} 
+0

Bởi vì sau đó nó sẽ không cố gắng để phù hợp với các báo cáo khác nữa. Và tôi không thể tái cấu trúc nó, bởi vì đây là một phần chức năng và một nơi khác trong mã. Vì vậy, mọi thứ phải được thực hiện trong câu lệnh phù hợp. – Debilski

1

Bạn có thể tạo một chút cơ sở hạ tầng để cho bạn quấn một var theo một cách ít tiếp xúc:

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([email protected](path :: _ :: Nil, 3),m) if m(findInDb(r)).isDefined => 
    doSomethingWith(m()) 
    case [email protected](path :: _ :: Nil, _) => {} 
    case _ => {} 
} 

Ngoài ra, bạn có thể di chuyển công việc từ sau khi => vào điều kiện bằng cách sử dụng 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 [email protected](path :: _ :: Nil, 3) if 
    findInDb(r).map(m => { doSomethingWith(m); m }).isDefined => {} 
    case [email protected](path :: _ :: Nil, _) => println("default") 
    case _ => println("messed up") 
} 
3

thử điều này:

object FindInDB { 
    def unapply(req:Req) => { 
    //Your sophisticated logic 
    if(req.path.length > 3) Some((req, 2)) else None 
    } 

sau đó trong báo cáo trường hợp của bạn, bạn có thể làm:

Req("a"::"b"::Nil, 3) match { 
    case FindInDb(r, n) => //Now you can see both the Req, and the Int 
    ...