On 12/07/10 12:36, Henning Thielemann wrote:
> 
> Noah Easterly wrote:
>> Somebody suggested I post this here if I wanted feedback.
>>
>> So I was thinking about the ReverseState monad I saw mentioned on
>> r/haskell a couple days ago, and playing around with the concept of
>> information flowing two directions when I came up with this function:
>>
>> bifold :: (l -> a -> r -> (r,l)) -> (l,r) -> [a] -> (r,l)
>> bifold _ (l,r) [] = (r,l)
>> bifold f (l,r) (a:as) = (ra,las)
>>  where (ras,las) = bifold f (la,r) as
>>          (ra,la) = f l a ras
>>
>> (I'm sure someone else has come up with this before, so I'll just say
>> I discovered it, not invented it).
>>
>> Basically, it's a simultaneous left and right fold, passing one value
>> from the start of the list toward the end, and one from the end toward
>> the start.
> 
> I also needed a bidirectional fold in the past. See foldl'r in
>   http://code.haskell.org/~thielema/utility/src/Data/List/HT/Private.hs
> 
> You can express it using foldr alone, since 'foldl' can be written as
> 'foldr':
>    http://www.haskell.org/haskellwiki/Foldl_as_foldr
> 
> You may add your example to the Wiki.
Hi Henning,

I tried to understand the Foldl_as_foldr method by actually
manually tracing the the execution.  The result is in the
attachment 1(Bifold.Thielemann.txt).

Then I tried to implement the equivalent of the foldl'r in
your Private.hs with the if_recur mentioned in my other
post:

  http://www.mail-archive.com/haskell-cafe@haskell.org/msg84793.html

This code is in attachment 2(HutBacFoldlr.hs).  It uses a small
help module to make explicit the associativity of the folded values.
This help module is in attachment 3(Assoc.hs).

The emacs eshell output shows:

--{--eshell output--
/home/evansl/prog_dev/haskell/my-code $ ghci HutBacFoldlr.hs
GHCi, version 6.12.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 2] Compiling Assoc            ( Assoc.hs, interpreted )
[2 of 2] Compiling HutBacFoldlr     ( HutBacFoldlr.hs, interpreted )
Ok, modules loaded: HutBacFoldlr, Assoc.
*HutBacFoldlr> test
Loading package array-0.3.0.0 ... linking ... done.
Loading package containers-0.3.0.0 ... linking ... done.
Loading package mtl-1.1.0.2 ... linking ... done.
Loading package fgl-5.4.2.2 ... linking ... done.
***test_inp:
[(1,'a'),(2,'b'),(3,'c')]
***foldl'r AsLeft AsNull AsRight AsNull test_inp:
(AsLeft (AsLeft (AsLeft AsNull 1) 2) 3,AsRight 'a' (AsRight 'b' (AsRight
'c' AsNull)))
***if_recur_foldlr AsLeft AsNull AsRight AsNull test_inp:
(AsLeft (AsLeft (AsLeft AsNull 1) 2) 3,AsRight 'a' (AsRight 'b' (AsRight
'c' AsNull)))
---foldl'r_fst:
AsLeft (AsLeft (AsLeft AsNull 1) 2) 3
---foldl'r_snd:
AsRight 'a' (AsRight 'b' (AsRight 'c' AsNull))
*HutBacFoldlr>
--}--eshell output--

The two outputs preceded by the *** lines show both foldl'r and
if_recur_foldlr compute the same result.

The two outputs preceded by the --- lines show that part of the
result of foldl'r output is a tuple whose fst is a function
and whose 2nd is an already calculated value.  The manual trace
in attachment 1 shows how this fst function is calculated.
I think the last step in foldl'r, the mapFst call, corresponds
to applying the last argument in the foldl_as_foldr method
as shown at (1.1.rhs.8) of attachment 1.  IOW, the application
of v in the rhs of (1.1) in attachment 1 performs the same
role (as far as the foldl calculation is concerned) the

  mapFst ($b0) .

in foldl'r rhs.

What's also interesting is that with if_recur_foldlr, the
foldr calculations (i.e. calls to fr argument) are delayed until
going up the call stack; whereas in foldl'r the foldl
calculations (i.e. calls to fl argument) are delayed
by composing a sequence of functions with the:

  \b -> k $! fl b a

expression.

I'm not real sure if these observations have any importantance,
but maybe there's a difference of performance between composing
a sequence of functions (as in the foldl'r function) as opposed
to storing values on the call stack (as in the if_record_foldlr
function).  Maybe you have some insight on this.

-regards,
Larry



On 12/07/10 12:36, Henning Thielemann wrote:
> 
> Noah Easterly wrote:
>> Somebody suggested I post this here if I wanted feedback.
>>
>> So I was thinking about the ReverseState monad I saw mentioned on
>> r/haskell a couple days ago, and playing around with the concept of
>> information flowing two directions when I came up with this function:
>>
>> bifold :: (l -> a -> r -> (r,l)) -> (l,r) -> [a] -> (r,l)
>> bifold _ (l,r) [] = (r,l)
>> bifold f (l,r) (a:as) = (ra,las)
>>  where (ras,las) = bifold f (la,r) as
>>          (ra,la) = f l a ras
>>
>> (I'm sure someone else has come up with this before, so I'll just say
>> I discovered it, not invented it).
>>
>> Basically, it's a simultaneous left and right fold, passing one value
>> from the start of the list toward the end, and one from the end toward
>> the start.
> 
> I also needed a bidirectional fold in the past. See foldl'r in
>   http://code.haskell.org/~thielema/utility/src/Data/List/HT/Private.hs
> 
> You can express it using foldr alone, since 'foldl' can be written as
> 'foldr':
>    http://www.haskell.org/haskellwiki/Foldl_as_foldr
> 
> You may add your example to the Wiki.

I found:

  
http://www.haskell.org/haskellwiki/Foldl_as_foldr#Folding_by_concatenating_updates
  
hard to understand.  However, scanning

  http://www.haskell.org/haskellwiki/Foldl_as_foldr#See_also

shows, on page 13(with fold renamed to foldr):

  (1.t):  foldl :: ( b -> a -> b) -> b -> ([a] -> b)
  (1.0):  foldl f v [] = v
  (1.1):  foldl f v xs = foldr (\x g -> (\a -> g (f a x))) id xs v
  
which is much simpler than with wiki page; however, it still looks
like foldr on the rhs takes 4 args instead of 3, until one realizes
that foldr is calculating a function instead of a value and the last
arg on the rhs, v, is being passed to that calculated function.

foldr from p. 2 of the reference( again, with renaming) is:

  (2.t):  foldr :: ( a' -> b' -> b') -> b' -> ([a'] -> b')
  (2.0):  foldr f v []   = v
  (2.1):  foldr f v x:xs = f x (foldr f v xs) 
  
So, the (1.1) rhs( except for v) with xs=[1,2,3] gives:

  (1.1.rhs.0):
  
    foldr (\x g -> (\a -> g (f a x))) id [1,2,3]
    
applying(i.e. replacing lhs with rhs) (2.1) to (1.1.rhs.0) with
(2.1) substitutiions being:

  [ f <- (\x g -> (\a -> g (f a x)))
  , v <- id
  , x <- 1
  , xs <- [2,3]
  ]

gives(after renaming x and g to avoid variable capture):

  (1.1.rhs.1):
  
    (\x0 g0 -> (\a0 -> g0 (f a0 x0))) 
      1 
      (foldr 
        (\x1 g1 -> (\a1 -> g1 (f a1 x1)))
        id
        [2,3]
      )
      
applying substitutions:

   [x0 <- 1] 

in (1.1.rhs.1) gives:

  (1.1.rhs.2):
  
    (\g0 -> (\a0 -> g0 (f a0 1))) 
      (foldr 
        (\x1 g1 -> (\a1 -> g1 (f a1 x1)))
        id
        [2,3]
      )
      
applying subsitutions:

  [ g0 <- 
      (foldr 
        (\x1 g1 -> (\a1 -> g1 (f a1 x1)))
        id
        [2,3]
      )
  ]

in (1.1.rhs.2) gives:

  (1.1.rhs.3):
  
    (\a0 -> 
      (foldr 
        (\x1 g1 -> (\a1 -> g1 (f a1 x1)))
        id
        [2,3]
      )
      (f a0 1)
    )
      
applying (2.1) to (1.1.rhs.3) with (2.1) substitutions
being:

  [ f <- (\x1 g1 -> (\a1 -> g1 (f a1 x1)))
  , v <- id
  , x <- 2
  , xs <- [3]
  ]

gives:

  (1.1.rhs.4):
  
    (\a0 -> 
      (\x1 g1 -> (\a1 -> g1 (f a1 x1)))
        2
        (foldr
           (\x2 g2 -> (\a2 -> g2(f a2 x2)))
           id
           [3]
        )
      )
      (f a0 1)
    )
      
applying substitutions:

   [x1 <- 2] 

in (1.1.rhs.4) gives:

  (1.1.rhs.4):
  
    (\a0 -> 
      (\g1 -> (\a1 -> g1 (f a1 2)))
        (foldr
           (\x2 g2 -> (\a2 -> g2(f a2 x2)))
           id
           [3]
        )
      )
      (f a0 1)
    )
      
  
applying substitutions:

  [ g1 <- 
      (foldr 
        (\x2 g2 -> (\a2 -> g2 (f a2 x2)))
        id
        [3]
      )
  ]

in (1.1.rhs.4) gives:

  (1.1.rhs.5):
  
    (\a0 -> 
      (\a1 ->
        (foldr 
          (\x2 g2 -> (\a2 -> g2 (f a2 x2)))
          id
          [3]
        )
        (f a1 2)
      )
      (f a0 1)
    )
      
applying (2.1) to (1.1.rhs.5) with (2.1) substitutions
being:

  [ f <- (\x2 g2 -> (\a2 -> g2 (f a2 x2)))
  , v <- id
  , x <- 3
  , xs <- []
  ]

gives:

  (1.1.rhs.6):
  
    (\a0 -> 
      (\a1 ->
        (\x2 g2 -> (\a2 -> g2 (f a2 x2)))
          3
          ( foldr
            (\x3 g3 ->(\a3 -> g3 (f a3 x3)))
            id
            []
          )
        )
        (f a1 2)
      )
      (f a0 1)
    )

applying (2.0) to (1.1.rhs.6) gives:

  (1.1.rhs.6):
  
    (\a0 -> 
      (\a1 ->
        (\x2 g2 -> (\a2 -> g2 (f a2 x2)))
          3
          id
        )
        (f a1 2)
      )
      (f a0 1)
    )

applying substitutions:

  [ x2 <- 3
  , g2 <- id
  ]

in (1.1.rhs.6) gives:

  (1.1.rhs.7):
  
    (\a0 -> 
      (\a1 ->
        (\a2 -> f a2 3
        )
        (f a1 2)
      )
      (f a0 1)
    )

Then, addding back the tail argument, v, that was left off of
(1.1.rhs.0) gives:

  (1.1.rhs.8):
  
    (\a0 -> 
      (\a1 ->
        (\a2 -> f a2 3
        )
        (f a1 2)
      )
      (f a0 1)
    )
    v

Then, the substition:

  [ a0 <- v
  ]

into (1.1.rhs.8) gives:

  (1.1.rhs.9):

      (\a1 ->
        (\a2 -> f a2 3
        )
        (f a1 2)
      )
      (f v 1)

Then, the substition:

  [ a1 <- (f v 1)
  ]

into (1.1.rhs.9) gives:

  (1.1.rhs.10):

        (\a2 -> f a2 3
        )
        (f (f v 1 ) 2 )

Then, the substition:

  [ a2 <- (f (f v 1 ) 2 )
  ]

into (1.1.rhs.10) gives:

  (1.1.rhs.11):

        f (f (f v 1 ) 2 ) 3

which agrees with:

  foldl f z [x1, x2, ..., xn] == (...((z ‘f‘ x1) ‘f‘ x2) ‘f‘...)
  ‘f‘ xn  
  
from:

  http://www.haskell.org/onlinereport/haskell2010/haskellch9.html#x16-1720009.1
  
except for the use of infix f instead of prefix f and the the use
of z instead of v, and the use of i instead of xi for i=1,2,3.

{-
  Purpose:
    Compare outputs from the foldl'r function from:
      [THI10]
        http://code.haskell.org/~thielema/utility/src/Data/List/HT/Private.hs
    based on the method used to implement foldl in terms of foldr as described
    on page 13 of:
      [HUT99]
        http://www.cs.nott.ac.uk/~gmh/fold.pdf
    and another function, if_recur_foldlr, using a function, if_recur, modelled 
    after f in section 12.5 of:
      [BAC77]
         http://www.thocp.net/biographies/papers/backus_turingaward_lecture.pdf
-}
module HutBacFoldlr where

import Assoc
import Data.Graph.Inductive.Query.Monad(mapFst) --renamed from [THI10] 

-- {*foldl'r from [THI10]
foldl'r
  :: (b -> a -> b) -> b -- foldl function, start value
  -> (c -> d -> d) -> d -- foldr function, start value
  -> [(a,c)] 
  -> (b,d)

foldl'r 
  fl b0 
  fr d0 
  = mapFst ($b0) .
    foldr 
      (\(a,c) ~(k,d) -> (\b -> k $! fl b a, fr c d)) 
      (id,d0)

-- }*foldl'r from [THI10]

test_inp = [(1,'a'),(2,'b'),(3,'c')]

foldl'r_out = foldl'r AsLeft AsNull AsRight AsNull test_inp

foldl'r_foldr = foldr (\(a,c) ~(k,d) -> (\b -> k $! AsLeft b a, AsRight c d)) (id,AsNull) test_inp

foldl'r_fst = (fst foldl'r_foldr) AsNull
foldl'r_snd = snd foldl'r_foldr

-- {*if_recur from [BAC77]

if_recur 
  :: state_down
  -> (state_down -> Bool) 
  -> (state_down -> state_down)
  -> (state_down -> state_saved)
  -> ((state_saved,state_up) -> state_up) 
  -> (state_down -> state_up)
  -> state_up

if_recur 
  state_now  -- current state
  recur_     -- ::state_down -> Bool (continue recursion?)
  then_down  -- ::state_down -> state_down
  save_state -- ::state_down -> state_saved
  now_up     -- ::((state_saved,state_up)->state_up
  else_      -- ::state_down -> state_up
  {- The following table shows the corresponndence
     between the f in section 12.5 of [BAC77]
     and the arguments to this function:

         [BAC77]       [if_recur]
         =======       ==========
           p           recur_
           g           else_
           j           then_down
           i           save_state
           h           now_up
  -}
  = if recur_ state_now
    then now_up
         ( save_state state_now
         , if_recur (then_down state_now)
                    recur_
                    then_down
                    save_state
                    now_up
                    else_
         )
    else else_ state_now

-- }*if_recur from [BAC77]

if_recur_foldlr
  :: (b -> a -> b) -> b --foldl function, start value
  -> (c -> d -> d) -> d --foldr function, start value
  -> [(a,c)]
  -> (b,d)

if_recur_foldlr
  fl b0
  fr d0
  ac_pairs
  = if_recur
      (ac_pairs,(b0,d0))  --state_now
      (not.null.fst) --recur_
      (\((a0,c0):ac_pairs,(bn,dn)) -> (ac_pairs,(fl bn a0,d0))) --then_down
      (\((a0,c0):ac_pairs,(bn,dn)) -> c0) --save_state
      (\(c0,(bn,dn)) -> (bn,fr c0 dn))--now_up
      (\(ac_pairs,(bn,dn)) -> (bn,dn)) --else_

if_recur_out = if_recur_foldlr AsLeft AsNull AsRight AsNull test_inp

test = sequence_
       [ putStrLn "***test_inp:"
       , print test_inp
       , putStrLn "***foldl'r AsLeft AsNull AsRight AsNull test_inp:"
       , print foldl'r_out
       , putStrLn "***if_recur_foldlr AsLeft AsNull AsRight AsNull test_inp:"
       , print if_recur_out
       , putStrLn "---foldl'r_fst:"
       , print foldl'r_fst
       , putStrLn "---foldl'r_snd:"
       , print foldl'r_snd
       ]
{-
  Purpose:
    A data structure which shows "association" to
    either left or right depending on constructor.
-}

module Assoc where

  data Assoc a 
    = AsNull
    | AsLeft  (Assoc a) a
    | AsRight a (Assoc a)
    deriving(Show)

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to