Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Folding in Haskell, using more than one function

I have a list of integers based on letters. For example:

let charlist = map (ord) "ABCDEF"

charlist would then look like the following:

[65,66,67,68,69,70]

I also have a list of three functions: (+), (-) and (*). The list in this example looks like this

let funclist = [(+), (-), (*)]

I want to apply the functions in order between the elements in charlist (if there are more "spaces" in charlist than there are elements in funclist, you start over from the beginning of funclist) and calculate the final value from left to right, like the following:

s = ((((((65) + 66) - 67) * 68) + 69) - 70)

I was thinking about using foldl, but foldl seems to only work with one function. Is there any other way to do this? I would like to summarize this entire process in one function, if possible, though it's not a requirement.

like image 670
Saser Avatar asked Jan 27 '14 13:01

Saser


2 Answers

Whilst foldl may only apply one function, you can have different functions in your data. The trick it to attach the appropriate +, - or * function to your data. You're off to the right start:

funclist = [(+), (-), (*)]

But lets now make a infinite version of the above list, like [(+), (-), (*), (+), (-), (*)...]

infinite_funclist = cycle funclist

Lets assign the numbers we're going to fold over here. first is in this case 65, rest is [66..70]

(first:rest) = [65..70]

Now we zip together rest and infinite_funclist to get [(66,(+)), (67,(-)), (68,(*)), (69,(+)), (70,(-))]. We start with first, and for every new element, we apply the operation that's in the second part of the current tuple to the current value and the first part like so:

result = foldl' (\acc (v, f) -> acc `f` v) first (zip rest infinite_funclist)

If we want to print the result we can do so like so:

main = print result

(Link to code here)

like image 119
Clinton Avatar answered Nov 12 '22 09:11

Clinton


In essence, this is a zip rather than a fold. Nicest would be

> zipWith ($) (cycle funclist) charlist
[(65+), (66-), (67*), (68+), (69-), (70+)]

but because of left-associativity and initial element we need to do it a bit more wordy

> let ptApplied = tail . zipWith (($) . flip) (cycle funclist) $ charlist
ptApplied = [(+66), (subtract 67), (*68), (+69), (subtract 70)]

Only then comes the fold

> foldl' (flip ($)) (head charlist) ptApplied

like image 4
leftaroundabout Avatar answered Nov 12 '22 10:11

leftaroundabout