Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ranges A to B where A > B in F#

Tags:

f#

I've just found something I'd call a quirk in F# and would like to know whether it's by design or by mistake and if it's by design, why is it so... If you write any range expression where the first term is greater than the second term the returned sequence is empty. A look at reflector suggests this is by design, but I can't really find a reason why it would have to be so. An example to reproduce it is:


[1..10] |> List.length
[10..1] |> List.length

The first will print out 10 while the second will print out 0. Tests were made in F# CTP 1.9.6.2.

EDIT: thanks for suggesting expliciting the range, but there's still one case (which is what inspired me to ask this question) that won't be covered. What if A and B are variables and none is constantly greater than the other although they're always different? Considering that the range expression does not seem to get optimized at compiled time anyway, is there any good reason for the code which determines the step (not explicitly specified) in case A and B are ints not to allow negative steps?

like image 670
em70 Avatar asked May 18 '09 16:05

em70


People also ask

How do you make the range from A to B?

Calculate the rangeSubtract the lowest value from the highest value.

What does f mean in domain and range?

The domain of a function is the set of values that we are allowed to plug into our function. This set is the x values in a function such as f(x). The range of a function is the set of values that the function assumes.

What is the range of f in interval notation?

In interval notation, we write the solution:(−∞,2)∪(2,∞). In interval form, the domain of f is (−∞,2)∪(2,∞).


3 Answers

As suggested by other answers, you can do

[10 .. -1 .. 1] |> List.iter (printfn "%A")

e.g.

[start .. step .. stop]
like image 130
Brian Avatar answered Nov 18 '22 09:11

Brian


Adam Wright - But you should be able to change the binding for types you're interested in to behave in any way you like (including counting down if x > y).

Taking Adam's suggestion into code:

let (..) a b =
    if a < b then seq { a .. b }
             else seq { a .. -1 .. b }

printfn "%A" (seq { 1 .. 10 })
printfn "%A" (seq { 10 .. 1 })

This works for int ranges. Have a look at the source code for (..): you may be able to use that to work over other types of ranges, but not sure how you would get the right value of -1 for your specific type.

like image 39
James Hugard Avatar answered Nov 18 '22 07:11

James Hugard


What "should" happen is, of course, subjective. Normal range notation in my mind defines [x..y] as the set of all elements greater than or equal to x AND less than or equal to y; an empty set if y < x. In this case, we need to appeal to the F# spec.

Range expressions expr1 .. expr2 are evaluated as a call to the overloaded operator (..), whose default binding is defined in Microsoft.FSharp.Core.Operators. This generates an IEnumerable<_> for the range of values between the given start (expr1) and finish (expr2) values, using an increment of 1. The operator requires the existence of a static member (..) (long name GetRange) on the static type of expr1 with an appropriate signature.

Range expressions expr1 .. expr2 .. expr3 are evaluated as a call to the overloaded operator (.. ..), whose default binding is defined in Microsoft.FSharp.Core.Operators. This generates an IEnumerable<_> for the range of values between the given start (expr1) and finish (expr3) values, using an increment of expr2. The operator requires the existence of a static member (..) (long name GetRange) on the static type of expr1 with an appropriate signature.

The standard doesn't seem to define the .. operator (a least, that I can find). But you should be able to change the binding for types you're interested in to behave in any way you like (including counting down if x > y).

like image 22
Adam Wright Avatar answered Nov 18 '22 09:11

Adam Wright