简体   繁体   中英

F# negative indices in array

In my application there is a need to precompute and keep trigonometric function values for some particular angle parameters, the range varies from -90 to 180 degree. I can create arrays(one for each sine, cos etc) which will store value for -90 angle on 0th index and while retrieving I can subtract 90 from the index.

but is there any other way in F# to specify range of index, if we want to use [-90 .. 180] so that I can have more meaningful implementation.

considering alternate solution, will usage of dictionary be as fast as usage of simple 2D arrays.

If I understand well your problem you would need to retrieve precomputed values by the key/index which is a given angle going from -90 to 180. Something like this ?

let value = precomputed.[-90]

You could use Map for that. F# maps are implemented as immutable AVL trees, an efficient data structure which forms a self-balancing binary tree. This can be very efficient if you have a precomputed data and you need to look up by key frequently. Its immutability in this case ensures that the static data cannot be modified by mistake and has little impact to performance as you never need to mutate it once initialized. However if you need to modify it frequently I would advice you to use a regular .NET Dictionary because they are based on hashtable which has a better performance than AVL trees.

You could turn the list into the map where the key would be the angle and the value would be the precomputed one :

let precomputedValus f =
    [for i in -90..180 ->
            i, f(i)]
            |> Map.ofList

Where f is the function doing the precomputation. So you obtain your precomputed map for every angle something like that.

let sinValues = precomputedValus (fun e -> sin (float e))

And you can access the procomputed sin value like that

> sinValues.[-90];;
val it : float = -0.8939966636

A little index arithmetic will be of use:

let inline idx i = (i + 270) % 270

since it's inline the overhead is going to be very, very small. And you can just use myArray.[idx -90] . (you might have to write different modulo values, but you get the picture)

The easiest way is to simply make some function which given some i returns a pre-computed value of sin i :

let array_table = 
  let a = Array.init 271 (fun i -> sin <| float (i-90))
  fun i -> a.[i+90]

To lookup the sine of, say, 42, you simply do table 42 .

anushri and Tomasz both mention using Maps instead of Arrays, but in my experience, these are not good candidates for storing precomputed values, as they are much slower than Arrays. Let's try:

let map_table = 
  let m = Seq.init 271 (fun i -> i-90, sin <| float i) |> Map.ofSeq
  fun i -> Map.find i m 

let no_table = 
  fun i -> sin (float i)

// Benchmarking code omitted (100000 lookups of each value in -90..270) 

When I run this, array_table is roughly 8 times faster than no_table and 22 times faster than map_table :

> fsharpi --optimize+ memo.fsx 
map_table
Real: 00:00:02.922, CPU: 00:00:02.924, GC gen0: 4, gen1: 0
no_table
Real: 00:00:01.091, CPU: 00:00:01.091, GC gen0: 3, gen1: 0
array_table
Real: 00:00:00.130, CPU: 00:00:00.130, GC gen0: 3, gen1: 0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM