F #, implement fold3, fold4, fold_n

I'm interested in implementing fold3, fold4, etc. similar to List.fold and List.fold2. eg.

// TESTCASE
let polynomial (x:double) a b c = a*x + b*x*x + c*x*x*x
let A = [2.0; 3.0; 4.0; 5.0]
let B = [1.5; 1.0; 0.5; 0.2]
let C = [0.8; 0.01; 0.001; 0.0001]

let result = fold3 polynomial 0.7 A B C
// 2.0 * (0.7   ) + 1.5 * (0.7   )^2 + 0.8    * (0.7   )^3 -> 2.4094
// 3.0 * (2.4094) + 1.0 * (2.4094)^2 + 0.01   * (2.4094)^3 -> 13.173
// 4.0 * (13.173) + 0.5 * (13.173)^2 + 0.001  * (13.173)^3 -> 141.75
// 5.0 * (141.75) + 0.2 * (141.75)^2 + 0.0001 * (141.75)^3 -> 5011.964
//
// Output: result = 5011.964

      

My first method groups 3 lists A, B, C into a list of tuples and then applies list.fold

let fold3 f x A B C =
    List.map3 (fun a b c -> (a,b,c)) A B C
       |> List.fold (fun acc (a,b,c) -> f acc a b c) x

// e.g. creates [(2.0,1.5,0.8);  (3.0,1.0,0.01); ......]

      

My second method is to declare mutable data and use List.map3

let mutable result = 0.7
List.map3 (fun a b c ->
               result <- polynomial result a b c  // Change mutable data
               // Output intermediate data
               result) A B C
// Output from List.map3: [2.4094; 13.17327905; 141.7467853; 5011.963942]
// result mutable: 5011.963942

      

I would like to know if there are other ways to solve this problem. Thank.

+3


source to share


4 answers


For fold3

you can simply do zip3

and then fold

:

let polynomial (x:double) (a, b, c) = a*x + b*x*x + c*x*x*x
List.zip3 A B C |> List.fold polynomial 0.7

      


But if you want this for the general case, you need what we call "applicative functors".

First, imagine you have a list of functions and a list of values. Let's assume they are now the same size:

let fs = [ (fun x -> x+1); (fun x -> x+2); (fun x -> x+3) ]
let xs = [3;5;7]

      

And what you would like to do (natural only) is apply each function to each value. This can be easily done with List.map2

:

let apply fs xs = List.map2 (fun f x -> f x) fs xs

apply fs xs  // Result = [4;7;10]

      

This "applies" operation is why they are called "applicative functors". Not only some functors, but also applicative ones. (the reason why they are "functors" is a little more complicated)

So far so good. But wait! What if every function in my function list returns a different function?

let f1s = [ (fun x -> fun y -> x+y); (fun x -> fun y -> x-y); (fun x -> fun y -> x*y) ]

      

Or, if I remember that fun x -> fun y -> ...

you can write in short formfun x y -> ...

let f1s = [ (fun x y -> x+y); (fun x y -> x-y); (fun x y -> x*y) ]

      

What if I have apply

such a list of functions for my values? Well, naturally I'll get another list of functions:

let f2s = apply f1s xs
// f2s = [ (fun y -> 3+y); (fun y -> 5+y); (fun y -> 7+y) ]

      

Hey, here's the idea! Since f2s

it is also a list of functions, can I apply it again? Well, of course I can!

let ys = [1;2;3]
apply f2s ys  // Result: [4;7;10]

      



Wait what? What just happened?
First, I applied the first list of functions to xs

and ended up with another list of functions. And then I applied this result to ys

and got a list of numbers.
We could rewrite this without an intermediate variable f2s

:

let f1s = [ (fun x y -> x+y); (fun x y -> x-y); (fun x y -> x*y) ]
let xs = [3;5;7]
let ys = [1;2;3]
apply (apply f1s xs) ys  // Result: [4;7;10]

      

For added convenience, this operation is apply

usually expressed as an operator:

let (<*>) = apply
f1s <*> xs <*> ys

      

See what I did there? With this operator, it now looks a lot like just calling a function with two arguments. Well appointed.

But wait. How about our initial challenge? In the original requirements, we don't have a feature list, we only have one feature.
Well, it can easily be fixed with another operation, let's call it "apply first". This operation will take a single function (not a list) plus a list of values ​​and apply this function to each value in the list:

let applyFirst f xs = List.map f xs

      

Oh wait. It's simple map

. Silly me :-)
For the added convenience of this operation, you usually also assign an operator name:

let (<|>) = List.map

      

And now I can do things like this:

let f x y = x + y
let xs = [3;5;7]
let ys = [1;2;3]
f <|> xs <*> ys  // Result: [4;7;10]

      

Or that:

let f x y z = (x + y)*z
let xs = [3;5;7]
let ys = [1;2;3]
let zs = [1;-1;100]
f <|> xs <*> ys <*> zs  // Result: [4;-7;1000]

      

Well appointed! I did this to apply arbitrary functions to argument lists right away!

Now, finally, you can apply this to your original problem:

let polynomial a b c (x:double) = a*x + b*x*x + c*x*x*x
let A = [2.0; 3.0; 4.0; 5.0]
let B = [1.5; 1.0; 0.5; 0.2]
let C = [0.8; 0.01; 0.001; 0.0001]

let ps = polynomial <|> A <*> B <*> C
let result = ps |> List.fold (fun x f -> f x) 0.7

      

The list ps

consists of instances polynomial

that are partially applied to the matching elements A

, B

and C

, and still expect a final argument x

. And on the next line, I just add up this list of functions, applying each one to the result of the previous one.

+6


source


You can check the implementation of ideas:

https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/array.fs

  let fold<'T,'State> (f : 'State -> 'T -> 'State) (acc: 'State) (array:'T[]) =
        checkNonNull "array" array
        let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(f)
        let mutable state = acc             
        for i = 0 to array.Length-1 do 
            state <- f.Invoke(state,array.[i])
        state

      

here are some implementations for you:

let fold2<'a,'b,'State> (f : 'State -> 'a -> 'b -> 'State) (acc: 'State) (a:'a array) (b:'b array) =
  let mutable state = acc    
  Array.iter2 (fun x y->state<-f state x y) a b
  state

let iter3 f (a: 'a[]) (b: 'b[]) (c: 'c[]) = 
  let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt(f)
  if a.Length <> b.Length || a.Length <> c.Length then failwithf "length"
  for i = 0 to a.Length-1 do 
    f.Invoke(a.[i], b.[i], c.[i])

let altIter3 f (a: 'a[]) (b: 'b[]) (c: 'c[]) = 
  if a.Length <> b.Length || a.Length <> c.Length then failwithf "length"
  for i = 0 to a.Length-1 do 
    f (a.[i]) (b.[i]) (c.[i])

let fold3<'a,'b,'State> (f : 'State -> 'a -> 'b -> 'c -> 'State) (acc: 'State) (a:'a array) (b:'b array) (c:'c array) =
  let mutable state = acc    
  iter3 (fun x y z->state<-f state x y z) a b c
  state

      

NB. we don't have iter3, so let's implement it. OptimizedClosures.FSharpFunc only allows up to 5 (or is it 7?) Parameters. There is a finite number of available slots. It makes sense. You can go beyond that, of course, without using the OptimizedClosures stuff.

... Anyway, generally, you don't want to iterate over too many lists / arrays / sequences at once. So I would warn against too high a level.



... the best way in such cases might be to create a record or tuple from the specified lists / arrays. Then you can just use map and iter which are already baked. This is what zip / zip3 is (see: "(Array1. [I], array2. [I], array3. [I])")

    let zip3 (array1: _[]) (array2: _[]) (array3: _[]) = 
        checkNonNull "array1" array1
        checkNonNull "array2" array2
        checkNonNull "array3" array3
        let len1 = array1.Length
        if len1 <> array2.Length || len1 <> array3.Length then invalidArg3ArraysDifferent "array1" "array2" "array3" len1 array2.Length array3.Length
        let res = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked len1 
        for i = 0 to res.Length-1 do 
            res.[i] <- (array1.[i],array2.[i],array3.[i])
        res

      

I am working with arrays at the moment, so my solution dealt with these. Sorry about that. Here's a recursive version for lists.

let fold3 f acc a b c =
  let mutable state = acc
  let rec fold3 f a b c =
    match a,b,c with
    | [],[],[] -> ()
    | [],_,_
    | _,[],_
    | _,_,[] -> failwith "length"
    | ahead::atail, bhead::btail, chead::ctail -> 
        state <- f state ahead bhead chead
        fold3 f atail btail ctail
    fold3 f a b c 

      

i.e. we define a recursive function within a function that acts on / mutates / modifies the external modified acc variable (closure in a functional expression). Finally, it comes back.

It's pretty cool how much type information is inferred from these functions. In the array examples above, basically I was explicitly with 'a' b 'c. This time we will enable I / O. He knows that we are dealing with lists from the :: operator . So neat.

NB. the compiler will probably spin this tail-recursive approach, so it's just a loop behind the scenes. Generally, get the correct answer before optimizing. Just to mention it, though, as food for later thought.

+1


source


I think the existing answers provide great options if you want to summarize folding, which was your original question. However, if I just wanted to call a function polynomial

of the inputs specified in A

, B

and C

I would probably not want to enter my code is rather complex structures, such as applicatives with fancy operators base.

The problem becomes much easier if you wrap the input so that instead of a list [A; B; C]

with lists for individual variables, you have a transposed list with an input to evaluate each polynomial. To do this, we need a transpose function :

let rec transpose = function
  | (_::_)::_ as M -> List.map List.head M :: transpose (List.map List.tail M)
  | _ -> []

      

Now you can create a list with inputs, transfer it and calculate all polynomials, simply using List.map

:

transpose [A; B; C]
|> List.map (function 
  | [a; b; c] -> polynomial 0.7 a b c
  | _ -> failwith "wrong number of arguments") 

      

0


source


There are many ways to solve this problem. Few are referred to as the first of zip3

all three lists and then go over it. Using Applic Functors like Fyodor Soikin means that you can turn any function with any number of arguments into a function that expects a list instead of single arguments. This is a good general solution that works with any number of lists.

While this is a general idea, I am sometimes shocked that there is so little use of lower-level tools. In this case, it is recommended to use recursion and learn more about recursion.

Recursion is the right tool here because we have immutable data types. But you might be thinking about how to implement it with modified lists and loop first if that helps. Stages:

  • You are iterating over the index from 0 to the number of items in the lists.
  • You are checking if each list has an item to index
  • If there is an item in each list, you pass it to your "folder"
  • If at least one list has no element, then you break the loop

The recursive version works the same way. Just that you are not using an index to access the elements. You will chop up the first item from each list and then list the rest of the list.

Otherwise, it List.isEmpty

is a function to check for an empty list. You can chop off the first element with List.head

and you get the remaining list with the first element removed with List.tail

. This way you can simply write:

let rec fold3 f acc l1 l2 l3 =
    let h = List.head
    let t = List.tail
    let empty = List.isEmpty

    if   (empty l1) || (empty l2) && (empty l3)
    then acc
    else fold3 f (f acc (h l1) (h l2) (h l3)) (t l1) (t l2) (t l3)

      

The string if

checks if there is at least one element in each list. If this is true it executes: f acc (h l1) (h l2) (h l3)

. So it executes f

and passes it the first element of each list as an argument. The result is a new drive the next call fold3

.

Now that you've worked on the first element of each list, you should chop off the first element of each list and continue with the rest of the lists. You achieve this with List.tail

or in the example above (t l1) (t l2) (t l3)

. These are the next remaining lists for the next call fold3

.

Creation fold4

, fold5

, fold6

etc. not very difficult and I think it is self-evident. My general advice is to learn a little more about recursion and try to write recursive List functions without pattern matching. Pattern matching isn't always easier.

Some code examples:

fold3 (fun acc x y z -> x + y + z :: acc)   [] [1;2;3] [10;20;30] [100;200;300] // [333;222;111]
fold3 (fun acc x y z -> x :: y :: z :: acc) [] [1;2;3] [10;20;30] [100;200;300] // [3; 30; 300; 2; 20; 200; 1; 10; 100]

      

0


source







All Articles