8

リフト開発ではmatch、次のような –<code>case ステートメントを使用する必要がある場合があります。(理解しやすいように単純な scala に書き直しました。) 彼らへの 1 つの注意: これらは実際には異なる部分関数であり、コードの異なる部分で定義されているため、case ステートメントがガード内またはガードの前で失敗することが重要です関数が評価されます (マッチングが失敗した場合、つまり)。

// 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)ですか?

4

5 に答える 5

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 にリフトし、関数が定義されているかどうかとその値の両方に関する情報を一致させることができます。

于 2010-08-20T16:05:17.870 に答える
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(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")
}
于 2010-08-20T18:50:22.163 に答える
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
}
于 2010-08-20T17:37:28.073 に答える
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
于 2010-08-20T15:58:51.910 に答える