【问题标题】:Concise way to conditionally update map in State monad在状态单子中有条件地更新地图的简洁方法
【发布时间】:2017-11-14 20:23:20
【问题描述】:

下面是来自an answer regarding memoization 的代码,显示了在 State monad 中使用的记忆函数,如果键不在映射中,则使用传递函数的结果更新状态。

type MyMemo a b = State (Map.Map a b) b

myMemo :: Ord a => (a -> MyMemo a b) -> a -> MyMemo a b
myMemo f x = do
  map <- get
  case Map.lookup x map of
    Just y  -> return y
    Nothing -> do
      y <- f x
      modify $ \map' -> Map.insert x y map'
      return y

它看起来不像是惯用的 Haskell:感觉非常必要,每行并没有那么多内容。

有没有办法以更简洁/实用的风格完成上述操作?我查看了http://hackage.haskell.org/package/transformers-0.5.4.0/docs/Control-Monad-Trans-State-Lazy.html#v:state 提供的功能,但似乎没有任何帮助。

【问题讨论】:

    标签: haskell functional-programming monads memoization


    【解决方案1】:

    这是在https://stackoverflow.com/a/44515364/1319998 上扩展的替代方案,本质上是去除 do-notation 的糖分

    myMemo f x = gets (Map.lookup x) >>= maybe y' return
      where
        y' = f x >>= \y -> modify (Map.insert x y) >> return y
    

    【讨论】:

      【解决方案2】:

      这是在 https://stackoverflow.com/a/44515364/1319998 上扩展的替代方法,使用更多 &gt;&gt;= 来避免所有的 do 表示法

      myMemo :: Ord a => (a -> MyMemo a b) -> a -> MyMemo a b
      myMemo f x = gets (Map.lookup x) >>= maybe y' return
        where
          y' = f x >>= \y -> state $ \map -> (y, Map.insert x y map)
      

      【讨论】:

        【解决方案3】:

        这是使用mapState 以及https://stackoverflow.com/a/44515364/1319998 中的&gt;&gt;=maybe 的替代方法,避免了所有的do 表示法

        myMemo f x = gets (Map.lookup x) >>= maybe y' return
          where
            y' = mapState (\(y, map) -> (y, Map.insert x y map)) $ f x 
        

        【讨论】:

          【解决方案4】:

          我认为你的代码是函数式的,但你可以稍微简化一下。

          myMemo f x = maybe work return =<< gets (Map.lookup x)
            where
              work = do
                  y <- f x
                  modify $ Map.insert x y
                  return y
          

          【讨论】:

          • 为什么是=&lt;&lt; 而不是&gt;&gt;=(参数颠倒了)?
          • @MichalCharemza,我通常更喜欢=&lt;&lt; 而不是&gt;&gt;=,它更类似于应用程序。例如。 ($) :: (a -&gt; b) -&gt; a -&gt; b 类似于 (=&lt;&lt;) :: (a -&gt; m b) -&gt; m a -&gt; m b。然后数据始终从右向左流动,而不是根据它是否是单子来切换方向。
          • @MichalCharemza 我认为这取决于代码风格。我选择最方便的运营商。在这种情况下,这两个运算符都很方便,所以我将使用另一个规则,luqui 写过。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-06-06
          • 1970-01-01
          • 2022-01-03
          • 1970-01-01
          • 2021-07-25
          • 2013-09-19
          • 1970-01-01
          相关资源
          最近更新 更多