压缩遍历

时间:2016-07-29 14:55:17

标签: haskell lens

我有一个带洞的Traversable - 想象一下这个二叉树:

         /       \
       /    \     Nothing
 Just 1    /  \  
    Nothing   Just 3

我还有一个值列表,用 - [2, 4]填补漏洞 - 导致:

         /       \
       /    \     Just 4
 Just 1    /  \  
     Just 2   Just 3

我认为可以使用lens索引遍历来遍历Nothing并将其替换为列表中相应索引处的值。

但是必须可以在不使用指数的情况下直接做更多的事情吗?

奖励积分 - 此主题的一些变体:

  1. (我的用例)值列表必须与遍历中的孔具有完全相同数量的元素。失败以Maybe表示。
  2. 该列表必须至少包含 元素,因此我们也可以传入[2, 4, 6][2, 4, ..]等。
  3. 该列表可以包含任意数量的元素,并且我们可以使用我们给出的元素填充尽可能多的孔。此操作不会失败,只能填充任意数量的孔。

3 个答案:

答案 0 :(得分:5)

如果没有足够的元素,这是一个返回Left的版本。

您可以使用Traversable monad中的mapM State来填补漏洞:

 import qualified Data.Traversable as T
 import Control.Monad.State.Strict
 import Control.Error

 import qualified Data.Tree as Tree
 import Data.Tree (Tree(..))

 visit :: Maybe a -> ExceptT String (State [a]) a
 visit (Just x) = return x
 visit Nothing = do xs <- lift get
                    case xs of
                      (a:as) -> do lift (put as); return a
                      []     -> throwE "out of elements"

 fill :: T.Traversable t => t (Maybe a) -> [a] -> Either String (t a)
 fill t vals  = evalState (runExceptT (T.mapM visit t) ) vals

 tree = Node Nothing [ Node (Just "2") [], Node Nothing [] ]

 ex1 =  print $ fill tree ["a", "b", "c", "d" ] -- Right ...
 ex2 =  print $ fill tree ["a" ]                -- Left "out of elements"

如果您想确保使用所有元素,请将fill更改为:

fill :: T.Traversable t => t (Maybe a) -> [a] -> Either String (t a)
fill t vals = evalState (runExceptT doit) vals
  where doit = do t' <- T.mapM visit t
                  xs <- lift get
                  case xs of
                    [] -> return t'
                    _  -> throwE "not all elements were used"

答案 1 :(得分:5)

利用mapAccumL的简单(但部分)解决方案:

import qualified Data.Traversable as T

fill :: forall a t. T.Traversable t => t (Maybe a) -> [a] -> t a
fill t fillers = snd $ T.mapAccumL go fillers t
    where
      go :: [a] -> Maybe a -> ([a], a)
      go fs     (Just x) = (fs, x)
      go (f:fs) Nothing  = (fs, f)
      go []     Nothing  = error "not enough fillers!"

完全替代方案:

fill2 :: forall a t. T.Traversable t => 
        t (Maybe a) -> [a] -> Maybe (t a)
fill2 t fillers  = sequenceA . snd $ T.mapAccumL go fillers t
    where
      go :: [a] -> Maybe a -> ([a], Maybe a)
      go (f:fs) Nothing  = (fs, Just f)
      go fs     x        = (fs, x)

答案 2 :(得分:3)

这是一个紧凑的:

var onClick = function() {
  var cars = ["Saab", "Volvo", "BMW"];
  var rows = "";
  rows += "<tr><td> " + cars[0] + " </td></tr>";
  $(rows).appendTo("#list tbody");
};

$('#button').click(onClick);