簡體   English   中英

與貓一起執行遞歸 monad

[英]Recursive monad execution with cats

有一個簡單的抽象查找服務,可以通過鍵檢索值:

trait LookUp[F[_]] {
  def read(key: String): F[Option[String]]
}

這個服務有一個用例,想法是給已實現的存儲和累加器提供起始鍵,然后從數據庫中請求值,如果結果為無,則停止並返回無,如果找到該值,則將其添加到累加器列表中並查看將下一個值作為上一個調用結果的鍵。 當檢索到的值之前已經找到或沒有檢索到時,執行停止。 然后返回所有 acc 元素的字符串作為結果。

像這樣試過:

def readWhileFound[F[_]: Monad](db: LookUp[F], acc: List[String]): F[Option[String]] = {
  for{ result <- db.read(acc.head)} yield result match {
    case Some(value) if(!acc.contains(value)) => readWhileFound(db, value::acc)
    case _ => acc.mkstring("")      
  }
}

但是我無法正確獲取類型,出現不匹配錯誤,例如:

found   : F[cats.data.OptionT[[_]F[_],String]]
required: cats.data.OptionT[F,String]

方法二:

def readWhileFound[F[_]: Monad](key: String, db: LookUp[F])(implicit m: Monad[F]): F[Option[String]] = {
  m.tailRecM((Option(key), List.empty[String])) { case (currentK, accum) =>
    currentK match {
      case Some(value) if(!accum.contains(value)) => m.pure(Left((db.read(value), value :: accum)))
      case _        => m.pure(Right(Some(accum.mkString(""))))
    }
  }
}

獲取編譯器錯誤:

(Found)  F[Option[String]]
required: Option[String]
case Some(value) if(!accum.contains(value)) => m.pure(Left((db.read(value), value :: accum)))

看起來 db.read(value) 應該以某種方式從 F 中解開

這看起來是fs2 的一個很好的用例:

你應該能夠做這樣的事情:

import fs2.Stream

def readWhileFound[F[_]: Concurrent](db: LookUp[F])(initialKey: String): F[List[String] =
  Stream.unfoldEval(initialKey) { currentKey =>
    db.read(key = currentKey).map(k => (k, k))
  }.compile.toList

您在第一個實現中match了錯誤的表達式。 您應該match result ,而不是整個 for-comprehension 。 下面的實現應該做你所追求的。

def readWhileFound[F[_]: Monad](db: LookUp[F], startKey: String): F[Option[String]] = {
  def loop(currKey: String, seenKeys: Set[String]): F[Option[String]] = {
    db.read(currKey).flatMap {
      case Some(nextKey) if !seenKeys.contains(nextKey) =>
        loop(nextKey, seenKeys + nextKey)

      case _ if seenKeys.nonEmpty => seenKeys.mkString("").some.pure[F]

      case _ => none[String].pure[F]
    }
  }

  loop(startKey, Set.empty)
}

對於累積值,我已將List替換為Set ,因為它的contains方法更有效,但是如果您關心返回結果中的順序,那么您將不得不返回List (效率較低)或使用兩個累加器(一個Set ,另一個List )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM