Have you considered using a `Pipe` instead of a `Fold`?  In other words:

    interesting :: Int -> (a -> Bool) -> Pipe a [a] m r

It seems like a more natural fit for your problem. Basically it uses `await` to get the next element, and it `yield`s each successive stanza that it computes.

On 5/14/14, 5:31 PM, Alex Rozenshteyn wrote:
What I ended up writing (not cleaned up at all, and not exactly solving the problem posed; CRAPLed <http://matt.might.net/articles/crapl/>, if you will) can be found here: http://lpaste.net/104155

I used a monadic fold and the Writer monad, though looking back on it, I feel like Pipes would be a natural fit. I'm keeping a buffer just big enough to know what to output at each step in the consumption of the list.

One problem I had was that I misremembered how scan works; for some reason I thought that `scan list` was `tails` and not `inits`.



On Wed, May 14, 2014 at 7:50 PM, Gabriel Gonzalez <[email protected] <mailto:[email protected]>> wrote:

    Actually, there's not a good way to do this using `Foldl` because
    the fold does not have access to elements ahead of it in the stream.

    However, there's a pretty simple solution in terms of ordinary
    list functions:

        import Data.List (tails)


        takeInteresting :: Int -> (a -> Bool) -> [a] -> [a]
        takeInteresting n pred = go n
          where
            -- go :: Int -> [a] -> [a]
            go n as =
                if n <= 0
                then []
                else let (prefix , suffix ) = break pred as
                         (prefix', suffix') = case suffix of
                             []      -> (prefix, suffix)
                             s:uffix -> (prefix ++ [s], uffix)
                     in  prefix' ++ go (n - 1) suffix'

        scanInteresting :: Int -> (a -> Bool) -> [a] -> [[a]]
        scanInteresting n pred = map (takeInteresting n pred) . tails

    Here's an example usage:

        >>> scanInteresting 2 (== 0) [1, 2, 0, 3, 4, 0, 5, 6, 0]
    
[[1,2,0,3,4,0],[2,0,3,4,0],[0,3,4,0],[3,4,0,5,6,0],[4,0,5,6,0],[0,5,6,0],[5,6,0],[6,0],[0],[]]




    On 5/11/14, 9:40 AM, Alex Rozenshteyn wrote:
    Only tangentially related, but still, close enough that I feel
    like it's a follow-up, and tricky enough that I feel like I
    should ask someone else.

    This is a Foldl question, so if there's a better place to ask it,
    please let me know.

    Some context, which can mostly be safely ignored, but I'm
    providing for motivation in case my explanation is not clear:

    I'm trying to make Anki flashcards to memorize "The Love Song of
    J. Alfred Prufrock". My input format is the text of the poem
    (slightly preprocessed), and my output formate is a newline
    separated file with some formatting on each line. Each line
    defines a flashcard with a hole (a cloze deletion). I would like
    sufficient context; in this case, it's 5 lines or to the
    beginning of the stanza, whichever is longer.

    </context>

    What I want to write is a variant on a take function that takes
    the first n "interesting" elements and any uninteresting ones
    interspersed; that is

    takeInteresting :: Int -> (a -> Bool) -> [a] -> [a]

    such that

    length . filter p . takeInteresting n p == n
    isPrefixOf (takeInteresting n p xs) xs

    If I understand correctly, I can write this as a fold and then
    use a scan to apply it at every starting point.

    My question is how best to write this fold.

    My ad-hoc approach was to use a decorate-doStuff-undecorate
    pattern where I would count the number of interesting elements,
    but it wasn't very compositional; I expect there's a better approach.

    Thank you.


    On Thu, Mar 6, 2014 at 12:43 AM, Gabriel Gonzalez
    <[email protected] <mailto:[email protected]>> wrote:

        The trick is to combine `pipes` with the `foldl` library.  In
        fact, given any `Fold`, you can convert it into a scanning
        pipe like this:

            convert :: Monad m => Fold a b -> Pipe a b m r
            convert = Control.Foldl.purely Pipes.Prelude.scan

        So now you have a smaller problem: write a fold for a moving
        average of a stream of numbers.  It turns out that there is a
        nice way to do this in O(1) space if you do an exponential
        moving average:

        http://lpaste.net/100765

        You can also do an ordinary moving average, too, but that
        requires O(N) space (where N is the window size).  It
        basically involves storing the last N elements as the fold's
        internal state.

        The `foldl` library is easy to learn.  Just read the module
        header here and the documentation for the `Fold` type:

        http://hackage.haskell.org/package/foldl-1.0.2/docs/Control-Foldl.html

        Now, technically, you could do all of this without using the
        `foldl` library at all, just by passing the folding logic
        directly to `Pipes.Prelude.scan`, but by using `foldl` you
        make it really easy to add additional metrics to your `Fold`
        while still passing over the data just once.


        On 3/5/2014 8:01 AM, Alex Rozenshteyn wrote:
        I feel like I must be missing something rather basic, but I
        have been trying to figure out how to use pipes to compute a
        moving average of a stream of numbers. I've written code to
        do the EWMA, but I can't figure out how to do the sliding
        window. It doesn't help that I'm a bit of a pipes beginner.

        Advice is welcome and appreciated.

        Thank you.
-- You received this message because you are subscribed to the
        Google Groups "Haskell Pipes" group.
        To unsubscribe from this group and stop receiving emails
        from it, send an email to
        [email protected]
        <mailto:[email protected]>.

        To post to this group, send email to
        [email protected]
        <mailto:[email protected]>.




-- Alex R




--
          Alex R

--
You received this message because you are subscribed to the Google Groups "Haskell 
Pipes" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].

Reply via email to