2016-07-29 32 views
9

İçinde delikli bir Traversable var - bu ikili ağaç düşünün:Zipping dolaşımları

: - - [2, 4] sonuçlanan Ben de deliklerini doldurmak için bir değerler listesi var

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

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

Nothing s numaralarına geçmek için lens dizinli bir geçişi kullanmanın mümkün olduğunu düşünüyorum ve bunları listeden karşılık gelen dizindeki değerle değiştirin.

Ancak endeks kullanmadan daha doğrudan yapılması mümkün olmalıdır?

Bonus noktaları - Bu tema üzerinde birkaç varyasyon:

  1. (benim kullanım durumu) değerlerinin listeyi dolaşırken delikler olarak elementlerin tam aynı sayıda olması gerekir. Başarısızlık bir Maybe ile belirtilmiştir.
  2. liste en az gibi birçok öğeler olmalıdır, bu nedenle de vb
  3. liste elemanlarının herhangi bir sayı olabilir ve biz ile olabildiğince biz gibi birçok delikleri doldurmak, [2, 4, 6], [2, 4, ..] geçirilen olabilirdi verdiğimiz öğeler. Bu işlem başarısız olamaz, sadece herhangi bir sayıda deliği doldurabilir.

cevap

5

Yeterli öğe yoksa Left döndüren bir sürüm.

Sen State monad içinde Traversable 'ın mapM kullanarak delikleri doldurabilirsiniz:

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" 

tüm elemanları kullanılmaktadır emin olmak için, değiştirmek fill için:

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" 
5

A Basit (ama kısmi) çözümün yararlanması :

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!" 

toplam alternatif: Burada

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) 
3

kompakt bir tane:

fill :: Traversable t => t (Maybe a) -> [a] -> Maybe (t a) 
fill = evalStateT . traverse foo where 
    foo x = maybe empty pure x <|> StateT uncons