代码之家  ›  专栏  ›  技术社区  ›  Savenkov Alexey

Scalaz:如何组合OptionT[State[_],T]类型的单子,这样它就不会终止于None

  •  1
  • Savenkov Alexey  · 技术社区  · 8 年前

    我正在学习monad变形金刚,在排序它们时遇到了问题

    我创建了一个类型 OptionTBitSetState[T]

    我对这种类型的理解是 可能失败的有状态计算

    import scalaz._, Scalaz._
    import scala.collection.immutable.BitSet
    
    type BitSetState[T] = State[BitSet, T]
    type OptionTBitSetState[T] = OptionT[BitSetState, T]
    object OptionTBitSetState {
      def apply[T](option : Option[T]) : OptionT[BitSetState, T] =
        OptionT[BitSetState, T](State[BitSet, Option[T]](_ -> option))
    
      def apply[T](state : State[BitSet, T]) : OptionT[BitSetState, T] =
        OptionT[BitSetState, T](state.map(_.some))
    }
    

    我有一个功能 签名

    def step(i : Int) : OptionTBitSetState[Seq[Int]]
    

    该功能应:

    1. 检查状态内的位集是否包含参数 i
      • 如果不包含:添加 设置位并返回 Seq(i, i*10, i*100)
      • 如果包含:失败 None

    功能的实现 :

    def step(i : Int) : OptionTBitSetState[Seq[Int]] =
      for {
        usedIs <- OptionTBitSetState(get[BitSet])
        res <- OptionTBitSetState(
          Some(Seq(i, i*10, i*100)).filterNot(_ => usedIs.contains(i))
        )
        _ <- OptionTBitSetState(put(usedIs + i))
      } yield res
    

    我想排序一个列表 这样,当我计算这个序列时,我得到了一个选项列表。但签名 sequence 是不同的。我得到了一个列表选项。

    例如

    List(1,2,1,3).map(step).sequence.run(BitSet.empty)
    

    返回 没有一个 ,但我想要的是:

    List(Some(Seq(1, 10, 100)), Some(Seq(2, 20, 200)), None, Some(Seq(3, 30, 300)))
    

    有什么方法可以结合起来吗 OptionBitSetState[T] s、 所以我会得到我需要的行为?

    1 回复  |  直到 8 年前
        1
  •  2
  •   user2297560    8 年前

    在我的拙见中,您使用 OptionT .

    问题 选择 它希望将monad中的值视为存在或不存在,因此当您将单个计算“折叠”到单个状态以运行它时,任何单个失败都必须导致整个计算失败。

    我会用 State[BitSet,Option[Seq[Int]] 这是一个Haskell版本(为了简单起见稍微修改),因为我不太会讲Scala。

    module Main where
    
    import Control.Monad.State
    import Data.IntSet (IntSet)
    import qualified Data.IntSet as IntSet
    import Data.Maybe (isJust)
    
    step :: Int -> State IntSet (Maybe [Int])
    step i = do
        set <- get
        if not (IntSet.member i set)
        then do
            modify $ IntSet.insert i
            return $ Just [i, i*10, i*100]
        else return Nothing
    
    run xs = filter isJust $ flip evalState IntSet.empty $ mapM step xs
    
    main = do
        let result = run [1,2,1,3]
        print result
    

    你真正想要的是 mapM 或者任何Scala的等价物,然后运行状态操作并删除Nothing值。