2014-02-13 2 views
2

Я играю с библиотекой каналов и написал образец кода для извлечения двух чисел (2 & 3), когда они появляются в последовательности. Ниже мой код:Кабель: извлечение последовательных номеров

import Data.Conduit 
import qualified Data.Conduit.List as CL 

source = CL.sourceList [1,2,3,4,5,2,3] :: Source IO Int 

-- Extract the consequent 2 and 3 number 
extract23 :: Conduit Int IO Int 
extract23 = do 
    a <- await 
    b <- await 
    case (a,b) of 
    (Just a,Just b) -> 
     if a == 2 && b == 3 
     then do yield a 
       yield b 
       extract23 
     else extract23 
    _ -> return() 

conduit1 :: Conduit Int IO String 
conduit1 = CL.map show 

sink1 :: Sink String IO() 
sink1 = CL.mapM_ putStrLn 

main :: IO() 
main = source $= (extract23 =$= conduit1) $$ sink1 

Но когда я выполняю функцию main, я не получаю никакого вывода. Я ожидаю, что на самом деле это примерно так:

2 
3 
2 
3 

Любая идея о том, что я делаю неправильно?

ответ

4

Ваш код вызывает await два раза подряд. Что это говорит, «дайте мне следующие два значения в потоке и не верните их в поток». Когда вы делаете это повторно, вы по существу разбиваете поток на 2-значные куски. Используя свой первоначальный список, вы в основном получаете кортежи, которые выглядят как:

[(1,2),(3,4),(5,2)] -- final 3 is lost since it has no pair 

Вопрос заключается в том, что вы 2,3 последовательности всегда падает между два из этих кортежей. Мне кажется, что алгоритм вы действительно хотите:

  • Проверьте, соответствуют ли первые два значения в потоке 2,3.
  • Пройдите вперед в потоке на один элемент и повторите.

В настоящее время вы шагнули вперед два элементы в потоке.

К счастью, есть простое решение этой проблемы: вместо того, чтобы использовать await, чтобы получить второе значение, которое удаляет его из потока в то же время, использовать peek, который будет смотреть на значение и положить его обратно. Если вы замените b <- await на b <- CL.peek, вы должны получить поведение, которое вы ищете.

UPDATE

Просто чтобы дать немного больше информации. Под поверхностью, peek реализуется поверх двух примитивов в трубопроводе: await и leftover, например, так:

peek = do 
    mx <- await 
    case mx of 
     Nothing -> return Nothing 
     Just x -> do 
      leftover x 
      return (Just x) 

Там нет ничего магического об этой способности смотреть вперед на 1 элемент. Вы также можете смотреть вперед 2 элемента. Единственный трюк - сделать остатки в правильном порядке:

peek2 = do 
    mx <- await 
    my <- await 
    maybe (return()) leftover my 
    maybe (return()) leftover mx 
    return (mx, my) 
+1

Спасибо, что решил проблему. Но что, если я захочу увидеть третий элемент в потоке? Как обращаться в этом случае? 'peek', похоже, не работает в этом случае. – Sibi

+1

Я добавил обновление с более подробным объяснением. –

Смежные вопросы