more descriptive IndexOutOfRangeException - f#

Is there a way to get more descriptive Index-Out-Of-Range-Exceptions ?
I thought about just overriding the Item Property like this, but this does not work:
type ``[]``<'T> with
override arr.Item
with get(i) =
if i<0 || i >= arr.Length then
failwithf "Index %d is Out of Range on Array(%d): %A" i arr.Length arr
else
arr.[i]
and set (i: int) (value:'T) =
arr.[i] <- value

If this is just for debugging you can configure VS to break on CLR exceptions (Debug > Exceptions...) to see the invalid index. Otherwise, you could do something like this:
let idx = -1
try
arr.[idx] <- x
with :? System.IndexOutOfRangeException ->
failwithf "Index out of range: %d" idx
Another option is to shadow Array.get/Array.set:
module Array =
let get i (arr: 'T[]) =
if i<0 || i >= arr.Length then
failwithf "Index %d is Out of Range on Array(%d): %A" i arr.Length arr
else
arr.[i]

Related

F# Can I use negative indices on Arrays. (like in Python)?

Edit 2021:
Yes you can since F#5.0 see Docs
Original Question:
I would like to use negative indices on Arrays so that i can write myThings.[-2] <- "sth" to set the second last item. Is this possible?
I tried this but it fails to compile with:
Method overrides and interface implementations are not permitted here
type ``[]``<'T> with
/// Allows for negative index too (like python)
override this.Item
with get i = if i<0 then this.[this.Length+i] else this.[i]
and set i v = if i<0 then this.[this.Length+i] <- v else this.[i] <- v
I know, I could use new members like myThings.SetItemNegative(-2,"sth") but this is not as nice as using the index notation:
type ``[]``<'T> with
/// Allows for negative index too (like python)
member this.GetItemNegative (i) =
if i<0 then this.[this.Length+i] else this.[i]
member this.SetItemNegative (i,v) =
if i<0 then this.[this.Length+i] <- v else this.[i] <- v
Unfortunately existing methods in a type have priority over future extension members.
It doesn't make so much sense but that's the way currently is, you can read more about it in this issue: https://github.com/dotnet/fsharp/issues/3692#issuecomment-334297164
That's why if you define such extension it will be ignored, and what's worst: silently ignored !
Anyway there are some proposals to add something similar to negative slices to F#.
Gus explained that existing members of 'T array can not be overwritten.
A workaround is extending 'T seq.
For my F# scripts this is good enough. I am not sure if this is a good idea in general though.
open System
open System.Collections.Generic
open System.Runtime.CompilerServices
//[<assembly:Extension>] do()
/// Converts negative indices to positive ones
/// e.g.: -1 is last item .
let negIdx i len =
let ii = if i<0 then len+i else i
if ii<0 || ii >= len then failwithf "Cannot get index %d of Seq with %d items" i len
ii
let get i (xs:seq<'T>) : 'T =
match xs with
//| :? ('T[]) as xs -> xs.[negIdx i xs.Length] // covered by IList
| :? ('T IList) as xs -> xs.[negIdx i xs.Count]
| :? ('T list) as xs -> List.item (negIdx i (List.length xs)) xs
| _ -> Seq.item (negIdx i (Seq.length xs)) xs
let set i x (xs:seq<_>) :unit =
match xs with
| :? ('T[]) as xs -> xs.[negIdx i xs.Length]<- x // why not covered by IList?
| :? ('T IList) as xs -> xs.[negIdx i xs.Count] <- x
| _ -> failwithf "Cannot set items on this Seq (is it a dict, lazy or immutable ?)"
//[<Extension>]
type Collections.Generic.IEnumerable<'T> with
[<Extension>]
///Allows for negtive indices too (like Python)
member this.Item
with get i = get i this
and set i x = set i x this
///Allows for negative indices too.
///The resulting seq includes the item at slice-ending-index. like F# range expressions include the last integer e.g.: 0..5
[<Extension>]
member this.GetSlice(startIdx,endIdx) : 'T seq = // to use slicing notation e.g. : xs.[ 1 .. -2]
let count = Seq.length this
let st = match startIdx with None -> 0 | Some i -> if i<0 then count+i else i
let len = match endIdx with None -> count-st | Some i -> if i<0 then count+i-st+1 else i-st+1
if st < 0 || st > count-1 then
let err = sprintf "GetSlice: Start index %d is out of range. Allowed values are -%d up to %d for Seq of %d items" startIdx.Value count (count-1) count
raise (IndexOutOfRangeException(err))
if st+len > count then
let err = sprintf "GetSlice: End index %d is out of range. Allowed values are -%d up to %d for Seq of %d items" endIdx.Value count (count-1) count
raise (IndexOutOfRangeException(err))
if len < 0 then
let en = match endIdx with None -> count-1 | Some i -> if i<0 then count+i else i
let err = sprintf "GetSlice: Start index '%A' (= %d) is bigger than end index '%A'(= %d) for Seq of %d items" startIdx st endIdx en count
raise (IndexOutOfRangeException(err))
this |> Seq.skip st |> Seq.take len
// usage :
let modify (xs:seq<_>) =
xs.[-1] <- 0 // set last
xs.[-2] <- 0 // set second last
xs
let slice (xs:seq<_>) =
xs.[-3 .. -1] // last 3 items
modify [|0..9|]
slice [|0..9|]
You cannot extend 'T[], but wouldn't an operator taking _[] as an operand do it?
let (?) (this : _[]) i =
if i<0 then this.[this.Length+i] else this.[i]
// val ( ? ) : this:'a [] -> i:int -> 'a
let (?<-) (this : _[]) i v =
if i<0 then this.[this.Length+i] <- v else this.[i] <- v
// val ( ?<- ) : this:'a [] -> i:int -> v:'a -> unit
[|1..3|]?(-1)
// val it : int = 3
let a = [|1..3|] in a?(-1) <- 0; a
// val it : int [] = [|1; 2; 0|]
Yes you can since F# 5.0 see Docs

Get elements between two elements in an F# collection

I'd like to take a List or Array, and given two elements in the collection, get all elements between them. But I want to do this in a circular fashion, such that given a list [1;2;3;4;5;6] and if I ask for the elements that lie between 4 then 2, I get back [5;6;1]
Being used to imperative programming I can easily do this with loops, but I imagine there may be a nicer idiomatic approach to it in F#.
Edit
Here is an approach I came up with, having found the Array.indexed function
let elementsBetween (first:int) (second:int) (elements: array<'T>) =
let diff = second - first
elements
|> Array.indexed
|> Array.filter (fun (index,element) -> if diff = 0 then false
else if diff > 0 then index > first && index < second
else if diff < 0 then index > first || index < second
else false
This approach will only work with arrays obviously but this seems pretty good. I have a feeling I could clean it up by replacing the if/then/else with pattern matching but am not sure how to do that cleanly.
You should take a look at MSDN, Collections.Seq Module for example.
Let's try to be clever:
let elementsBetween a e1 e2 =
let aa = a |> Seq.append a
let i1 = aa |> Seq.findIndex (fun e -> e = e1)
let i2 = aa |> Seq.skip i1 |> Seq.findIndex (fun e -> e = e2)
aa |> Seq.skip(i1+1) |> Seq.take(i2-1)
I am not on my normal computer with an f# compiler, so I haven't tested it yet. It should look something like this
[Edit] Thank you #FoggyFinder for showing me https://dotnetfiddle.net/. I have now tested the code below with it.
[Edit] This should find the circular range in a single pass.
let x = [1;2;3;4;5]
let findCircRange l first second =
let rec findUpTo (l':int list) f (s:int) : (int list * int list) =
match l' with
| i::tail ->
if i = s then tail, (f [])
else findUpTo tail (fun acc -> f (i::acc)) s
// In case we are passed an empty list.
| _ -> [], (f [])
let remainder, upToStart = findUpTo l id first
// concatenate the list after start with the list before start.
let newBuffer = remainder#upToStart
snd <| findUpTo newBuffer id second
let values = findCircRange x 4 2
printf "%A" values
findUpTo takes a list (l'), a function for creating a remainder list (f) and a value to look for (s). We recurse through it (tail recursion) to find the list up to the given value and the list after the given value. Wrap the buffer around by appending the end to the remainder. Pass it to the findUpTo again to find up to the end. Return the buffer up to the end.
We pass a function for accumulating found items. This technique allows us to append to the end of the list as the function calls unwind.
Of course, there is no error checking here. We are assuming that start and end do actually exist. That will be left to an exercise for the reader.
Here is a variation using your idea of diff with list and list slicing
<some list.[x .. y]
let between (first : int) (second : int) (l : 'a list) : 'a list =
if first < 0 then
failwith "first cannot be less than zero"
if second < 0 then
failwith "second cannot be less than zero"
if first > (l.Length * 2) then
failwith "first cannot be greater than length of list times 2"
if second > (l.Length * 2) then
failwith "second cannot be greater than length of list times 2"
let diff = second - first
match diff with
| 0 -> []
| _ when diff > 0 && (abs diff) < l.Length -> l.[(first + 1) .. (second - 1)]
| _ when diff > 0 -> (l#l).[(first + 1) .. (second - 1)]
| _ when diff < 0 && (abs diff) < l.Length -> l.[(second + 1) .. (second + first - 1)]
| _ when diff < 0 -> (l#l).[(second + 1) .. (second + first - 1)]

Type mismatch expecting bool list but get int list

I am going filter an int list based on the 2 boundaries. To do it i used the List.filter in this way:
let CountFilesInRange ele lowerRang upperRange=
match ele with
| ele when lowerRang< ele< upperRange -> true
| _ ->false
List.filter (CountFilesInRange 0 5000) listOfSizes
In the last line I have this error with listOfSizes:
Type mismatch expecting bool list but get int list
How can i fix it?
As say #ildjarn - not valid syntax
You have wrong order of parameters.
Probably, you wanted to do this:
Example:
let listOfSizes = [1;2;3;4]
let betweenExclusive lowerBound upperBound value =
match value with
| value when lowerBound < value && value < upperBound -> true
| _ -> false
List.filter (betweenExclusive 1 4) listOfSizes |> printfn "%A"
Print:
[2; 3]
Link:
https://dotnetfiddle.net/3179jr
Edit:
Thank you #Sehnsucht for the comment. This function can be rewritten as:
let betweenExclusive lowerBound upperBound value =
lowerBound < value && value < upperBound

What's wrong with this F# Code

let compareDiagonal p x y =
System.Math.Abs((int)(x - (fst p))) <> System.Math.Abs((int)(y - (snd p)));;
let isAllowed p = function
| [] -> true
| list -> List.forall (fun (x, y) -> fst p <> x && snd p <> y && (compareDiagonal p x y)) list;;
let rec solve col list =
let solCount : int = 0
match col with
| col when col < 8 ->
for row in [0 .. 7] do
solCount = solCount + if isAllowed (row, col) list then solve (col + 1) ((row, col) :: list) else 0
solCount
| _ -> 1;;
let solCount = solve 0 [];;
solCount;;
I am getting the error
solCount = solCount + if isAllowed (row, col) list then (solve (col + 1) ((row, col) :: list)) else 0
------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stdin(335,13): warning FS0020: This expression should have type 'unit', but has type 'bool'. If assigning to a property use the syntax 'obj.Prop <- expr'.
Why am I not able to return the number?
There are two related problems.
By default in F# variable are immutable. If you want a mutable variable, you have to declare it, like this:
let mutable solCount : int = 0
And then when you assign values to it instead of using = you have to use <- like this:
solCount <- solCount + if isAllowed (row, col) list then solve (col + 1) ((row, col) :: list) else 0
A complete example followed.
HOWEVER, this is not the correct functional way to do something like this. Instead of using a loop to add up values, use a recursive function to return the cumulative value as you go. Using F# the way functional programs are designed to be used will almost always yield better results, although it takes some getting used to.
Your original example with mutable, not the "functional way":
let compareDiagonal p x y =
System.Math.Abs((int)(x - (fst p))) <> System.Math.Abs((int)(y - (snd p)));;
let isAllowed p = function
| [] -> true
| list -> List.forall (fun (x, y) -> fst p <> x && snd p <> y && (compareDiagonal p x y)) list;;
let rec solve col list =
let mutable solCount : int = 0
match col with
| col when col < 8 ->
for row in [0 .. 7] do
solCount <- solCount + if isAllowed (row, col) list then solve (col + 1) ((row, col) :: list) else 0
solCount
| _ -> 1;;
let solCount = solve 0 [];;
solCount;;

How to get min value from array in F#?

This is F# code as close as I can write to find the min value of an array:
let temp = 0
let getArrayMinValue (a : Array) =
if a.Length > 0 then (
for i = 0 to a.Length do
let temp = ( if temp > a.[i] then a.[i] else temp ) in ()
)
I have two questions:
First, a.[i] has compiling error: The field, constructor or member 'Item' is not defined.
Second, the in () is requried, otherwise, there is compiling error "Block following this let is unfinished. Expect an expression". I do not quite understand this part.
Thanks in advance for your help.
Can't you use Array.min to find the minimum element, something like below:
let numbers =[|1..10|]
printfn "%A" (numbers |> Array.min)
Coming to you program, you have to use something like:
let getArrayMinValue (a : int[]) =
and not a: Array You can even do a: 'T[] but since you are comparig the value of the elements against temp it will probably constraint it to int.
Coming to the other error, make temp mutable: let mutable temp = 0 and assign value using <- operator. Here is your whole code:
let mutable temp = 0
let getArrayMinValue (a : 'T[]) =
if a.Length > 0 then (
for i = 0 to a.Length do
temp <- ( if temp > a.[i] then a.[i] else temp )
)

Resources