2010-08-20 2 views
8

Для развития лифта мне иногда нужно использовать match - case заявления, подобные следующим. (Переписано на простой scala для более легкого понимания.) Одно примечание к ним: на самом деле это разные частичные функции, определенные в разных частях кода, поэтому важно, чтобы оператор case терпел неудачу в или перед охраной, чтобы иметь другой частичный (если совпадение не выполняется, то есть).Сфера действия переменных в отчете о защите корпуса 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 
} 

Теперь для того, чтобы знать, что case заявление успешно, я должен запросить базу данных с findInDb и проверить, является ли результат справедлив. Впоследствии я должен снова позвонить ему, чтобы использовать значение.

Делать что-то вроде

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

не работает, поскольку объем res затем ограничивается внутри фигурных скобок.

Я могу, конечно, определить var res = _ снаружи и назначить ему, но я не чувствую себя хорошо, делая это.

Можно ли каким-либо образом объявить переменную внутри охранника? Если можно сделать case [email protected](…), почему бы не case [email protected]() if [email protected](r.isDefined)?

ответ

0

Вы попробовали 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

Я не совсем понимаю ваш ответ. Поскольку я написал (или, по крайней мере, надеялся, что люди поверят, что я на самом деле пробовал это), я попытался использовать 'if res @ (что-то)', и это недопустимый синтаксис. – Debilski

9

Вы на самом деле очень близко. Недостатком ключевого элемента является использование экстрактора, а не выражения защиты.

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 
} 

Экстракторов не обязательны для возврата только информация уже присутствует в своих аргументах, что это просто обычный случай использования. Фактически вы можете использовать любую частичную функцию, поднять ее до опции и иметь возможность сопоставлять информацию о том, определена ли функция и ее значение.

+0

Хм. Интересный подход. Я знал эту технику, но не думал, что смогу использовать ее в этом случае. Я подумаю, полезно ли это в моем случае. – Debilski

+0

Это очень запутанно, конечно, когда я хочу использовать не только 'req', но и' id', как в случае case req @ Req (\ 'path \' :: id :: Nil, _, _) ' ... Давайте просто надеемся, что этого не произойдет. – Debilski

+0

Должно быть хорошо, на самом деле. Нет причин, по которым вы не можете использовать @ -папки или привязать новые переменные внутри аргументов к экстрактору. –

1

Что случилось с рефакторингом выражения if внутри оператора 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

Потому что тогда он больше не будет пытаться соответствовать другим утверждениям. И я не могу его реорганизовать, потому что это частичные функции и где-то еще в коде. Итак, все должно быть сделано в соглашении соответствия. – Debilski

1

Вы можете создать немного инфраструктуры, чтобы вы обернуть вар в менее выставленным образом:

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 _ => {} 
} 

В качестве альтернативы, вы можете переместить работу с после => в условную с помощью 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

попробовать это:

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 
    ... 

 Смежные вопросы

  • Нет связанных вопросов^_^