F# Array instantiates with 5 items but not with 6 - arrays

I am new to F#, so I am probably missing something trivial but here goes.
This works -
let monthsWith31Days = [| MonthType.January;
MonthType.March;
MonthType.May;
MonthType.July;
MonthType.December |]
But this doesn't
let monthsWith31Days = [| MonthType.January;
MonthType.March;
MonthType.May;
MonthType.July;
MonthType.August;
MonthType.December |]
What I have noted is that it's not the content itself, but the number of items that matter (even if I change the actual items used). The problem starts when number of items exceed 5.
This is the error I get when I run my NUnit tests -
System.ArgumentException: Value does not fall within expected range.
Any ideas what I'm missing?
Edit:
Entire type definition (two types are related so showing both here) -
type public Month(monthType:MonthType, year:Year) =
member public this.Year
with get () = year
member public this.MonthType
with get () = monthType
member public this.GetDaysCount () =
let monthsWith31Days = [| MonthType.January;
MonthType.March;
MonthType.May;
MonthType.July;
MonthType.August;
MonthType.December |]
let has31 = monthsWith31Days |> Array.filter(fun n -> (int)n = (int)this.monthType) |> Array.length
if (has31 > 0)
then 31
// else if (this.MonthType = MonthType.February)
// then (if this.Year.Leap then 29
// else 28)
else 30
and public Year(ad:int) =
member public this.AD
with get() = ad
member public this.Months = Enum.GetValues(typeof<MonthType>).Cast().ToArray()
|> Array.map(fun n -> new Month (n, this))
member public this.GetMonth (index:int) =
(this.Months |> Array.filter(fun p-> (int)p.MonthType = index)).First()
member public this.GetMonth (monthName:string) =
let requiredMonthType = Enum.Parse(typeof<MonthType>, monthName) |> unbox<MonthType>
(this.Months |> Array.filter(fun p-> p.MonthType = requiredMonthType)).First()
member public this.Leap =
if this.AD % 400 = 0 then true
else if this.AD % 100 = 0 then false
else if this.AD % 4 = 0 then true
else false
member this.DaysCount = if this.Leap then 366 else 365

I actually vaguely recall some bug about creating array literals full of enums on some target CLR platform, where if you had more than 5, then some bad code was generated or something. Maybe you're hitting that? Are you targeting x64 and CLR2? You can work around the bug by avoiding array literals, and use e.g. a list and then call List.ToArray.

Related

Create a list of custom type in F# and create two sequences of that list

I have created my own type in F# called Accounts and I have then created objects for each account.
type Account() =
let AccountNumber = ""
let mutable Balance:float = 0.0
Every account has two fields, AccountNumber (string) and Balance (float).
I have then created an object for every account that holds the AccountName and the Balance.
let acc1 = new Account()
acc1.Insert("John",10.0)
let acc2 = new Account()
acc2.Insert("Mike",50.0)
How do I create a list that holds each account (object)? I have tried the following:
let AccountList : Account list = [acc1; acc2 ; acc3; acc4 ; acc5; acc6]
let AccountList : Account obj list = [acc1; acc2 ; acc3; acc4 ; acc5; acc6]
I cannot solve the problem using the above method because I have to create two sequences from the list:
Sequence 1: All accounts with a balance greater or equal to zero and less than 50
Sequence 2: All accounts with a balance above 50
How do I create a list of my custom type in F# and how do I create two sequences of that list?
It is not clear what exactly are you struggling with. However, the following simple example should illustrate most of the key ideas that you probably need to use. First, here is a small version of your Account class (note that I would normally use an immutable record, but I kept it the way you did it):
type Account(balance:float) =
let mutable balance = balance
member x.Balance = balance
member x.Add(difference) =
balance <- balance + difference
I do not see what issue you have with creating the list. The following works just fine:
let acc1 = Account(100.0)
let acc2 = Account(10.0)
let accountList = [acc1; acc2]
Now, to answer the question about finding accounts with balance over 50, you can use the List.filter function to create a new filtered list:
let above50 =
accountList |> List.filter (fun acc ->
acc.Balance > 50.0)
EDIT If you wanted to use a record instead, then you would define the type as:
type Account = { Balance : float }
And create a value using:
let acc1 = { Balance = 100.0 }
So I created this answer for the other one but I was waiting on the comment to see if I would answer. And the homework like aspect of this :)
So if you have criteria that bucket an account and want to do that in a single pass, you might want to look at groupBy. Here I use a boolean because there are only 2 possibilities but numbers or a discriminated union are good candidates.
open System
type Account(accountNumber:string, startingBalance:Int64) =
let mutable balance = startingBalance
member _.Balance = balance
member _.Deposit amount = balance <- balance + amount
member _.Withdraw amount = balance <- balance - amount
override _.ToString() = accountNumber
let allAccounts = [Account("ABC1", 10L); Account("ABC2", 50L)]
let grouped = allAccounts |> List.groupBy (fun a -> a.Balance >= 50L) |> Map.ofList
let under50 = grouped |> Map.tryFind false |> Option.defaultValue []
let overIncl50 = grouped |> Map.tryFind true |> Option.defaultValue []
printfn "Under: %A" under50
printfn "Over: %A" overIncl50

Swift: Array map or reduce, enumerate with index

I have an array of values [CGFloat] and array of days [CGFloat] with which each value is associated (the time is also important so the days array is a decimal value).
Each day has between 0 and n values. (n typically less than 5 or 6)
I want to find the mean value for each day, so after the calculations I would like to have an array of means [CGFloat] and an array of days [CGFloat], or a dictionary of the two combined or an array of [CGPoint]. I am fairly certain this can be done with either a mapping or reducing or filter function, but I'm having trouble doing so.
For instance
the third day might look like [2.45, 2.75, 2.9]
with associated values [145.0, 150.0, 160.0]
And I would like to end with day[2] = 2.7
and value[2] = 151.7
or
[CGPoint(2.7, 151.7)] or [2.7 : 151.7]
Can anyone offer guidance?
Some code:
let xValues : [CGFloat] = dates.map{round((CGFloat($0.timeIntervalSinceDate(dateZero))/(60*60*24))*100)/100}
let yValues : [CGFloat] = valueDoubles.map{CGFloat($0)}
//xValues and yValues are the same length
var dailyMeans = [CGFloat]()
var xVals = [CGFloat]()
let index = Int(ceil(xValues.last!))
for i in 0..<index{
let thisDay = xValues.enumerate().filter{$0.element >= CGFloat(i) && $0.element < CGFloat(i+1)}
if thisDay.count > 0{
var sum : CGFloat = 0
var day : CGFloat = 0
for i in thisDay{
sum += yValues[i.index]
day += xValues[i.index]
}
dailyMeans.append(sum/CGFloat(thisDay.count))
xVals.append(day/CGFloat(thisDay.count))
}
}
The above code works, but also has to do that enumerate.filter function values.count * days.last times. So for 40 days and 160 readings.. like 6500 times. And I'm already using way too much processing power as it is. Is there a better way to do this?
edit: forgot a line of code defining index as the ceiling of xValues.last
This has been seen 1000 times so I thought I would update with my final solution:
var daySets = [Int: [CGPoint]]()
// points is the full array of (x: dayTimeInDecimal, y: value)
for i in points {
let day = Int(i.x)
daySets[day] = (daySets[day] ?? []) + [i]
}
let meanPointsEachDay = daySets.map{ (key, value) -> CGPoint in
let count = CGFloat(value.count)
let sumPoint = value.reduce(CGPoint.zero, {CGPoint(x: $0.x + $1.x, y: $0.y + $1.y)})
return CGPoint(x: sumPoint.x/count, y: sumPoint.y/count)
}
// must be sorted by 'day'!!!
let arrA0 = [2.45, 2.75, 2.9, 3.1, 3.2, 3.3]
// associated values
let arrA1 = [145.0, 150.0, 160.0, 245.0, 250.0, 260.0]
let arr = Array(zip(arrA0, arrA1))
// now i have an array of tuples, where tuple.0 is key and tuple.1 etc. is associated value
// you can expand the tuple for as much associated values, as you want
print(arr)
// [(2.45, 145.0), (2.75, 150.0), (2.9, 160.0), (3.1, 245.0), (3.2, 250.0), (3.3, 260.0)]
// now i can perform my 'calculations' the most effective way
var res:[Int:(Double,Double)] = [:]
// sorted set of Int 'day' values
let set = Set(arr.map {Int($0.0)}).sort()
// for two int values the sort is redundant, but
// don't be depend on that!
print(set)
// [2, 3]
var g = 0
var j = 0
set.forEach { (i) -> () in
var sum1 = 0.0
var sum2 = 0.0
var t = true
while t && g < arr.count {
let v1 = arr[g].0
let v2 = arr[g].1
t = i == Int(v1)
if t {
g++
j++
} else {
break
}
sum1 += v1
sum2 += v2
}
res[i] = (sum1 / Double(j), sum2 / Double(j))
j = 0
}
print(res)
// [2: (2.7, 151.666666666667), 3: (3.2, 251.666666666667)]
see, that every element of your data is process only once in the 'calculation', independent from size of 'key' set size
use Swift's Double instead of CGFloat! this increase the speed too :-)
and finally what you are looking for
if let (day, value) = res[2] {
print(day, value) // 2.7 151.666666666667
}

F# concatenate int array option to string

I have a data contract (WCF) with a field defined as:
[<DataContract(Namespace = _Namespace.ws)>]
type CommitRequest =
{
// Excluded for brevity
...
[<field: DataMember(Name="ExcludeList", IsRequired=false) >]
ExcludeList : int array option
}
I want to from the entries in the ExcludeList, create a comma separated string (to reduce the number of network hops to the database to update the status). I have tried the following 2 approaches, neither of which create the desired string, both are empty:
// Logic to determine if we need to execute this block works correctly
try
// Use F# concat
let strList = request.ExcludeList.Value |> Array.map string
let idString = String.concat ",", strList
// Next try using .NET Join
let idList = String.Join ((",", (request.ExcludeList.Value.Select (fun f -> f)).Distinct).ToString ())
with | ex ->
...
Both compile and execute but neither give me anything in the string. Would greatly appreciate someone pointing out what I am doing wrong here.
let intoArray : int array option = Some [| 1; 23; 16 |]
let strList = intoArray.Value |> Array.map string
let idString = String.concat "," strList // don't need comma between params
// Next try using .NET Join
let idList = System.String.Join (",", strList) // that also works
Output:
>
val intoArray : int array option = Some [|1; 23; 16|]
val strList : string [] = [|"1"; "23"; "16"|]
val idString : string = "1,23,16"
val idList : string = "1,23,16"

F#: Writing a function that takes any kind of array as input

I am new to programming and F# is my first language.
Here is part of my code:
let splitArrayIntoGroups (inputArray: string[]) (groupSize: int) =
let groups = new LinkedList<string[]>()
let rec splitRecursively currentStartIndex currentEndIndex =
groups.AddLast(inputArray.[currentStartIndex..currentEndIndex]) |> ignore
let newEndIndex = Math.Min((inputArray.Length - 1), (currentEndIndex + groupSize))
if newEndIndex <> currentEndIndex then
splitRecursively (currentStartIndex + groupSize) newEndIndex
splitRecursively 0 (groupSize - 1)
groups
I want this function to be able to accept arrays of any type (including types that I define myself) as input. What changes should I make?
This was already answered but here you have an implementation not using a linked list but just an array of lists
let rec split<'T> (input: 'T array) size =
let rec loopOn (tail : 'T array) grouped =
let lastIndex = Array.length tail - 1
let endindx = min (size - 1) lastIndex
let arrWrapper = (fun e -> [|e|])
let newGroup = tail.[0..endindx]
|> List.ofArray
|> arrWrapper
|> Array.append grouped
match tail with
| [||] -> newGroup
|> Array.filter (fun e -> List.length e > 0)
| _ -> loopOn tail.[endindx + 1..] newGroup
let initialState = [|List.empty<'T>|]
loopOn input initialState
Because this is generic implementation you can call it with different types
type Custom = {Value : int}
let r = split<int> [|1..1000|] 10
let r2 = split<float> [|1.0..1000.0|] 10
let r3 = split<Custom> [|for i in 1..1000 ->
{Value = i}|] 10
replace string[] with _[] in the function signature.

How do I create an F# list containing objects with a common superclass?

I have two functions, horizontal and vertical, for laying out controls. They work like this:
let verticalList = vertical [new TextBlock(Text = "one");
new TextBlock(Text = "two");
new TextBlock(Text = "three")]
Now verticalList is a control that displays the three textblocks vertically:
one
two
three
Here are the definitions:
let horizontal controls =
let wrap = new WrapPanel() in
List.iter (wrap.Children.Add >> ignore) controls ;
wrap
let vertical controls =
let stack = new StackPanel() in
List.iter (stack.Children.Add >> ignore) controls ;
stack
A problem occurs when I combine different types:
let foo = vertical [new TextBlock(Text = "Title"); vertical items]
This complains that the elements of the list are not of the same type. That is true, but they have a common supertype (UIElement).
I know I can use :> UIElement to upcast both items in the list, but this is an ugly solution. Can F# infer the common supertype. If not, why not?
It would be great if the nice looking
vertical [X; Y; Z]
doesn't have to become
vertical [(X :> UIElement); (Y :> UIElement); (Z :> UIElement)]
There are a few ways, including
type Animal() = class end
type Cat() =
inherit Animal()
type Dog() =
inherit Animal()
let animals1 : list<Animal> = [upcast new Cat(); upcast new Dog()]
let animals2 = ([upcast new Cat(); upcast new Dog()] : list<Animal>)
let animals3 = [(new Cat() :> Animal); upcast new Dog()]
animals1: type annotation at the var declaration, upcast each element
animals2: type annotation on the list expression, upcast each element
animals3: explicit type on first element, upcast rest
In a future version of F#, the upcast is likely to become unnecessary.
(See also http://cs.hubfs.net/forums/thread/9953.aspx , but nothing 'new' there.)
If you agree to sacrifice type safety for readability here you are the workaround:
open System
let to_list (tuple: Object) =
let rec list_after_index (n: int) =
let prop = tuple.GetType().GetMethod("get_Item"+n.ToString())
match prop with
| null -> []
| _ -> prop.Invoke(tuple, [||]) :: list_after_index(n+1)
match tuple with
| :? unit -> []
| _ when tuple.GetType().FullName.Contains(".Tuple`") -> list_after_index(1)
| _ -> [tuple]
then you can use it like that:
> to_list ();;
val it : obj list = []
> to_list (1);;
val it : obj list = [1]
> to_list([], 1, "2", 3.0);;
val it : obj list = [[]; 1; "2"; 3.0]
e.g. inside your vertical function.
I like my previous answer better, but building on that, if you have an app where you're constantly creating non-homogenous lists of animals, you can always do something like this:
let Animalize (x:Animal) = x // define a function to upcast
let animals4 = [ Animalize <| new Cat(); Animalize <| new Dog()]
// or even
let (~++) = Animalize // define a prefix operator to upcast (~ makes it prefix)
let animals5 = [ ++ new Cat(); ++ new Dog()]
The last is almost certainly an abuse of operators, unless you're in a very highly specialized domain and you constantly need some coercion like this and you're willing to sacrifice inherent readability for terseness.

Resources