I have several points that I want to convert to lines. for example [{x=0; y=0}; {x=1; y=1}; {x=2; y=2}; {x=3; y3}] should be converted to [({x=0; y=0}, {x=1; y=1}); ({x=1; y=1}, {x=2; y=2}); ({x=2; y=2}, {x=3; y=3})].
my current approach is like this:
type Point = { x: int; y: int }
type Line = Point * Point
let rec pointsToLines lines points =
if (List.length points) < 2 then
lines
else
let line = points.[1], points.[0]
let lines = line :: lines
pointsToLines lines (List.tail points)
So my question would be, is there one-liner or an idiomatic way to achieve the same thing?
You could write it like this for more idiomatic F#:
let rec pointsToLines points =
let rec loop lines points =
match points with
| x::y::rest -> loop ((x,y)::lines) rest
| _ -> lines
loop [] points
However, this already one of the built in functions as you suspected:
List.pairwise
Both will give you the same results.
Related
I am currently studying F# and at the same time, a bit struggling to get the hang of how discriminated unions and records exactly work.
I'd like to know how I can update some values from a list of type <'T>?
My code
type Position =
| Location of (int * int)
type Ship =
{
Position : Position;
Projectiles : List<Position>;
}
I create an instance of a ship:
let aShip =
{
Position: Location(1,5);
Projectiles: [Location(1,1);Location(2,5)]
}
Now, I tried to loop over the projectiles, but I get this:
for elem in aShip.Projectiles do
printfn "%A" elem
// prints out
Location(1,1)
Location(2,5)
But I only like to get the values (1,1) and (2,5) back, how would I achieve this?
Discriminated unions can be destructured by providing a pattern with some places in it occupied by identifiers. The compiler will then generate code that tries to match this pattern to the data, and bind data points to appropriate identifiers. For example:
let loc = Location (1,2)
let (Location (x,y)) = loc
For the second line, the compiler will generate code to the effect of "make sure this is a Location, then bind the first int to name x, and the second int to name y"
Alternatively, you can use more verbose match syntax:
let x = match loc with Location(x,y) -> x
For your specific case, this is overkill, but for discriminated unions with more than one case, match is the only way to handle them all, for example:
type Position =
| Location of int*int
| Unknown
let xOrZero =
match loc with
| Location(x,y) -> x
| Unknown -> 0
Above examples demonstrate how patterns can appear in let-bindings and within match expressions, but this is not all. In F# pretty much anything that you'd think of as "variable declaration" is actually a pattern. It's just most of the time patterns are trivial, as in let x = 5, but they don't have to be - e.g. let x,y = 5,6
The corollary from the above is that the elem in for elem in ... is also a pattern. Which means that you can destructure the element in place:
for Location(x,y) in aShip.Projectiles do
printfn "%d,%d" x y
Or, if you'd like to extract the pair as a whole, rather than x and y individually, that is also possible:
for Location xy in aShip.Projectiles do
printfn "%A" xy
I have managed to read my text file which contains line by line random numbers. When I output lines using printfn "%A" lines I get seq ["45"; "5435" "34"; ... ] so I assume that lines must be a datatype list.
open System
let readLines filePath = System.IO.File.ReadLines(filePath);;
let lines = readLines #"C:\Users\Dan\Desktop\unsorted.txt"
I am now trying to sort the list by lowest to highest but it does not have the .sortBy() method. Any chance anyone can tell me how to manually do this? I have tried turning it to an array to sort it but it doesn't work.
let array = [||]
let counter = 0
for i in lines do
array.[counter] = i
counter +1
Console.ReadKey <| ignore
Thanks in advance.
If all the lines are integers, you can just use Seq.sortBy int, like so:
open System
let readLines filePath = System.IO.File.ReadLines(filePath)
let lines = readLines #"C:\Users\Dan\Desktop\unsorted.txt"
let sorted = lines |> Seq.sortBy int
If some of the lines may not be valid integers, then you'd need to run through a parsing and validation step. E.g.:
let tryParseInt s =
match System.Int32.TryParse s with
| true, n -> Some n
| false, _ -> None
let readLines filePath = System.IO.File.ReadLines(filePath)
let lines = readLines #"C:\Users\Dan\Desktop\unsorted.txt"
let sorted = lines |> Seq.choose tryParseInt |> Seq.sort
Note that the tryParseInt function I just wrote is returning the int value, so I used Seq.sort instead of Seq.sortBy int, and the output of that function chain is going to be a sequence of ints rather than a sequence of strings. If you really wanted a sequence of strings, but only the strings that could be parsed to ints, you could have done it like this:
let tryParseInt s =
match System.Int32.TryParse s with
| true, _ -> Some s
| false, _ -> None
let readLines filePath = System.IO.File.ReadLines(filePath)
let lines = readLines #"C:\Users\Dan\Desktop\unsorted.txt"
let sorted = lines |> Seq.choose tryParseInt |> Seq.sortBy int
Note how I'm returning s from this version of tryParseInt, so that Seq.choose is keeping the strings (but throwing away any strings that failed to validate through System.Int32.TryParse). There's plenty more possibilities, but that should give you enough to get started.
All the comments are valid but I'm a bit more concerned about your very imperative loop.
So here's an example:
To read all the lines:
open System.IO
let file = #"c:\tmp\sort.csv"
let lines = File.ReadAllLines(file)
To sort the lines:
let sorted = Seq.sort lines
sorted |> Seq.length // to get the number of lines
sorted |> Seq.map (fun x -> x.Length) // to iterate over all lines and get the length of each line
You can also use a list comprehension syntax:
[for l in sorted -> l.ToUpper()]
Seq will work for all kinds of collections but you can replace it with Array (mutable) or List (F# List).
Let's say I have these code:
namespace global
module NumeracyProblemsInTen=
module private Random=
let private a=lazy System.Random()
let getRandom()=a.Force()
let next()=getRandom().Next()
let lessThan exclusiveMax=getRandom().Next exclusiveMax
let pick seq=
assert(not<|Seq.isEmpty seq)
lessThan<|Seq.length seq|>Seq.item<|seq
module UsedNumbers=
let min,max=1,9 // *want to make these data variable*
let numbers={min..max}
let atLeast a=numbers|>Seq.skipWhile((>)a)
let atMost a=numbers|>Seq.takeWhile((>=)a)
module Random=
let private pick=Random.pick
let pickNumber()=pick numbers
let atMost=atMost>>pick
open UsedNumbers
module AdditionInTen=
module Addends=
let max=max-min
let numbers={min..max}
let pick()=Random.pick numbers
open Addends
let quiz()=
let addend=pick()
addend,Random.atMost<|min+max-addend
let calc(addend,another)=addend+another
module MultiplyInTen=
let quiz()=
let multipiler=Random.pickNumber()
multipiler,Random.pick{min..max/multipiler}
let calc(multipiler,another)=multipiler*another
module SubtractionInTen=
let minSubtrahend,minResult=min,min
let minMinuend=minSubtrahend+minResult
let minuends=atLeast minMinuend
let quiz()=
let minuend=Random.pick minuends
minuend,Random.pick{minSubtrahend..minuend-minResult}
let calc(minuend,subtrahend)=minuend-subtrahend
module DeviditionInTen=
let devisible devidend deviser=devidend%deviser=0
let findDevisers devidend=numbers|>Seq.filter(devisible devidend)
let findDeviditions devidend=findDevisers devidend|>Seq.map(fun deviser->devidend,deviser)
let problems=Seq.collect findDeviditions numbers
let quiz()=Random.pick problems
let calc(devidend,deviser)=devidend/deviser
type Problem=Addition of int*int|Subtraction of int*int|Multiply of int*int|Devidition of int*int
let quiz()=
let quizers=[AdditionInTen.quiz>>Addition;SubtractionInTen.quiz>>Subtraction;
MultiplyInTen.quiz>>Multiply;DeviditionInTen.quiz>>Devidition]
quizers|>Random.pick<|()
let calc problem=
match problem with
|Addition(addend,another)->AdditionInTen.calc(addend,another)
|Subtraction(minuend,subtrahend)->SubtractionInTen.calc(minuend,subtrahend)
|Multiply(multipiler,another)->MultiplyInTen.calc(multipiler,another)
|Devidition(devidend,deviser)->DeviditionInTen.calc(devidend,deviser)
module NumeracyProblemsUnderOneHundred=
module UsedNumbers=
let min,max=1,99
// ...
// ...
// ...
// OMG! Do I must copy all the previous code here?
If I use oo/types, I can simply define Max as a property, is there a good way to resolve the same scene without object/types but only modules/immutable bindings way? A bit of more complex scene should be also considered, more configurable data, with more usage in different ways.
So, it seems to me that your code is designed to generate a random mathematical operation which you can then calculate the result of. I found this code quite difficult to decipher, it appears that you're trying to use modules like object oriented classes which contain internal state and that isn't really a good way to think about them.
You can achieve much more granular code reuse by thinking about smaller, composable, units of code.
Here is my attempt at this problem:
type Range = {Min : int; Max : int}
type Problem=
|Addition of int*int
|Subtraction of int*int
|Multiplication of int*int
|Division of int*int
module NumeracyProblems =
let private rnd = System.Random()
let randomInRange range = rnd.Next(range.Min, range.Max+1)
let isInRange range x = x >= range.Min && x <= range.Max
let randomOpGen() =
match randomInRange {Min = 0; Max = 3} with
|0 -> Addition
|1 -> Subtraction
|2 -> Multiplication
|3 -> Division
let calc = function
|Addition (v1, v2) -> Some(v1 + v2)
|Subtraction (v1, v2) -> Some(v1 - v2)
|Multiplication (v1, v2) -> Some(v1 * v2)
|Division (v1, v2) ->
match v1 % v2 = 0 with
|true -> Some(v1 / v2)
|false -> None
let quiz range =
let op = randomOpCtor()
let optionInRange x =
match isInRange range x with
|true -> Some x
|false -> None
Seq.initInfinite (fun _ -> randomInRange range, randomInRange range)
|> Seq.map (op)
|> Seq.find (Option.isSome << Option.bind (optionInRange) << calc)
I've created a Range record to contain the range data I'm going to be working with.
My randomInRange function generates a random number within the specified range.
My isInRange function determines whether a given value is within the supplied range.
My randomOpGen function generates a number in the range of 0-3 and then generates a random type constructor for Problem: Addition when the random value is 1, Subtraction when 2, etc.
(You might wonder why I've defined this function with a unit argument rather than just accepting the tuple, the answer is so that I can get it to generate operators with equal likelihood later.)
My calc function resolves the arithmetic by performing the appropriate operation. I've modified the result of this function so that it handles integer division by returning Some result for cases where the remainder is 0 and None otherwise. All the other computations always return Some result.
quiz is where the magic happens.
First I generate a random operator, this will be the same for every element in the sequence later - hence the importance of the () I mentioned earlier.
I generate an infinite sequence of integer tuples from the supplied range and, using map, generate an operation (the one I created earlier) on each of these tuples.
I then use Seq.find to find the first occurrence of a result that's both within the range that I specified and has a valid result value.
Now let's try this code:
let x = NumeracyProblems.quiz {Min = 1; Max = 9}
x
NumeracyProblems.calc x;;
val x : Problem = Addition (2,7)
val it : int option = Some 9
Now let's change the range
let x = NumeracyProblems.quiz {Min = 1; Max = 99}
x
NumeracyProblems.calc x
val x : Problem = Division (56,2)
val it : int option = Some 28
As you can see, this version of the code is completely agnostic to the integer range.
I am trying to use the Math.NET numerics implementation of the FFT algorithm, but I must be doing something wrong because the output is always unit
The following is the the setup:
open MathNet.Numerics
open MathNet.Numerics.Statistics
open MathNet.Numerics.IntegralTransforms
let rnd = new Random()
let rnddata = Array.init 100 (fun u -> rnd.NextDouble())
let x = rnddata |> Array.Parallel.map (fun d -> MathNet.Numerics.complex.Create(d, 0.0) )
then when I run this:
let tt = MathNet.Numerics.IntegralTransforms.Fourier.BluesteinForward(x, FourierOptions.Default)
I receive an empty output below?
val tt : unit = ()
Any ideas why?
I think the Fourier.BluesteinForward method stores the results in the input array (by overwriting whatever was there originally).
If you do not need the input after running the transform, you can just use x and read the results (this saves some memory copying, which is why Math.NET does that by default). Otherwise, you can clone the array and wrap it in a more functional style code like this:
let bluesteinForward input =
let output = Array.copy input
MathNet.Numerics.IntegralTransforms.Fourier.BluesteinForward
(output, FourierOptions.Default)
output
Very much a newbie with F#, can't seem to get my googler to work for this question.
I have this function
namespace RiskMeasurement
module Basics01 =
let AvailableCapital (nominalAssetValue: float) (provisions: float) (liabilities: float) =
nominalAssetValue - provisions - liabilities
Called with
namespace RiskMeasurement
module Main =
[<EntryPoint>]
let main args =
let floats = Array.map float args
let availableCapital = Basics01.AvailableCapital floats.[0] floats.[1] floats.[2]
printfn "Available Capital: %f" availableCapital
let availableCapital = Basics01.AvailableCapital floats.[0], floats.[1], floats.[2]
// Return 0 to indicate success.
0
When I call the function with the args separated by spaces, availableCapital is a float. That makes sense. When called with commas separating the args, availableCapital is (float -> float -> float) * float * float. I don't get that at all. What am I doing?
Read it like this:
let availableCapital = (Basics01.AvailableCapital floats.[0]), floats.[1], floats.[2]
So the first component of availableCapital is a partially applied function that wants two more arguments.
If you wanted to call it with the arguments separated by commas, you would write
let availableCapital = Basics01.AvailableCapital (floats.[0], floats.[1], floats.[2])
and get a type error.
Basics01.AvailableCapital floats.[0], floats.[1], floats.[2]
is a 3-tuple containing Basics01.AvailableCapital floats.[0] as its first element floats.[1] as the second element and floats.[2] as the third element.
Some other good answers already; briefly, the 'comma operator' has very low precedence when it comes to parsing expressions.
Since you mention you are new to F#, you might want to check out
F# function types: fun with tuples and currying
if you need to learn about the difference between f(x,y) and f x y.