【问题标题】:Update values in Haskell?更新 Haskell 中的值?
【发布时间】:2015-05-19 04:49:00
【问题描述】:

我正在尝试编写一个不断接收整数值并输出迄今为止给出的最大值的函数。

这看起来很简单

max a b = maximum [a,b]

但函数应该“记住”过去赋予它的值,并将新值与新值进行比较。

例如:我要开始的初始值是-500。如果我用5 的参数调用我的函数,那么它应该返回5,因为5 大于-500

如果我再次调用该函数,这次使用3 的参数,它仍应返回5,因为即使3 大于-500,它也小于5

这甚至可以通过无副作用的编程实现吗?

【问题讨论】:

  • 无副作用编程有没有可能产生副作用? … 不。 wiki.haskell.org/State_Monad 可能会有所帮助。
  • 但是有没有办法做到这一点,而不会产生副作用
  • 对于初学者的合理答案只能是:不,没有 - 而是将你记住的东西作为你的函数的参数现在(state-monad 只是让它隐含) - btw 为什么你想要类似的东西,当你可以使用(在你的例子中)只是 maximum 和查找最大值的所有事物的列表。代替元素?
  • 嗯,我正在尝试 alpha-beta 修剪,所以我需要一个函数来不断限制 alpha 和 beta 边界。从 (-500, 500) 开始
  • 这真的没问题 - 只需传递你的 alpha/beta 值(并明显返回更新的版本) - 它甚至会让你更好地理解算法! - 当然,在这种情况下它可能会变得乏味,并且 state-monad 可能毕竟不是一个坏主意 - 但我只会在你已经理解这些东西的情况下推荐它 - 如果现在不坚持明确的参数/返回跨度>

标签: haskell


【解决方案1】:

这里是 alpha-beta pseudocode. 的直接翻译我没有测试过,但我希望它能得到翻译算法的通用方法,即使它并不完全正确。

alphaBeta ::
     (state -> Int) -- heuristic
  -> (state -> Bool -> [state]) -- next states
  -> state     
  -> Int   -- depth
  -> Int   -- alpha
  -> Int   -- beta
  -> Bool  -- is max player  
  -> Int   -- score
alphaBeta heu children = go where
  go s d a b p | d == 0 || null ss = heu s
               | otherwise         = score 
    where      
      ss = children s p

      score = fst $ head $ case p of
        True ->
          takeWhile ((<b) . snd) $ scanl step (minBound, a) ss where
            step (v, a) s = (v', max a v') where
              v' = max v (go s (d - 1) a b (not p))
        False ->
          takeWhile ((a<) . snd) $ scanl step (maxBound, b) ss where
            step (v, b) s = (v', min b v') where
              v' = min v (go s (d - 1) a b (not p))

我们使用scanl 处理当前状态的每个子​​状态,同时记住部分结果。我们使用takeWhile 进行剪辑。由于列表是惰性的,因此我们不会在剪切后处理子项。此外,列表融合或垃圾收集(取决于优化级别和 GHC 版本)确保scanl 在此处使用恒定空间。这是懒惰的一个很好的例子,它允许我们组合地定义算法。

或者,我们可以同时进行切割和加工,从而更接近原始呈现。 foldMEither 让我们一个一个地处理孩子,并在每一步决定是停止并返回一个值 Left 还是继续 Right。所以我们使用Left 来模拟命令式伪代码中的break 语句。

import Control.Monad

alphaBeta ::
     (state -> Int) -- heuristic
  -> (state -> Bool -> [state]) -- next states
  -> state     
  -> Int   -- depth
  -> Int   -- alpha
  -> Int   -- beta
  -> Bool  -- is max player  
  -> Int   -- score
alphaBeta heu children = go where
  go s d a b p | d == 0 || null ss = heu s
               | otherwise         = score 
    where      
      ss = children s p

      score = either id fst $ case p of
        True ->
          foldM step (minBound, a) ss where
            step (v, a) s | b <= a'   = Left v'
                          | otherwise = Right (v', a')
              where v' = max v (go s (d - 1) a b (not p))
                    a' = max a v'                    
        False ->
          foldM step (maxBound, b) ss where
            step (v, b) s | b' <= a   = Left v'
                          | otherwise = Right (v', b')
              where v' = min v (go s (d - 1) a b (not p))
                    b' = min b v'

【讨论】:

  • 看起来很有趣,我只是想理解scanl,它似乎带有一个困境,一个值和一个列表,但是在你的代码中它是一个元组和一个列表?
  • scanl 类似于 foldl,但收集部分结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-24
相关资源
最近更新 更多