Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# Equivalent of ++ operator

Tags:

f#

I'm converting an array to a record type. Something like:

let value = [|"1";"2";"3";"Not a number";"5"|]
type ValueRecord = {
  One: int32
  Two: int32
  Three: int32
  Four: string
  Five: int32 }

let convertArrayToRecord (x: string array) =
  { One = x.[0] |> Int.Parse
    Two = x.[1] |> Int.Parse
    Three = x.[2] |> Int.Parse
    Four = x.[3]
    Five = x.[4] |> Int.Parse }

let recordValue = convertArrayToRecord value

This works, but has the drawback that adding a value to the middle of the array results in manual editing of all index references thereafter like this:

let value = [|"1";"Not a number - 6";"2";"3";"Not a number";"5"|]
type ValueRecord = {
  One: int32
  Six: string
  Two: int32
  Three: int32
  Four: string
  Five: int32 }

let convertArrayToRecord (x: string array) =
  { One = x.[0] |> Int.Parse
    Six = x.[1]
    Two = x.[2] |> Int.Parse //<--updated index
    Three = x.[3] |> Int.Parse //<--updated index
    Four = x.[4] //<--updated index
    Five = x.[5] |> Int.Parse } //<--updated index

let recordValue = convertArrayToRecord value

Additionally, its easy to accidentally get the indexes wrong.

The solution I came up with is:

let convertArrayToRecord (x: string array) =  
    let index = ref 0
    let getIndex () =
        let result = !index
        index := result + 1
        result
    { One = x.[getIndex ()] |> Int.Parse
      Six = x.[getIndex ()]
      Two = x.[getIndex ()] |> Int.Parse
      Three = x.[getIndex ()] |> Int.Parse
      Four = x.[getIndex ()]
      Five = x.[getIndex ()] |> Int.Parse }

This works, but I really dislike the ref cell for something which isn't concurrent. Is there a better/cleaner way to accomplish this?

like image 230
N_A Avatar asked Feb 05 '23 21:02

N_A


2 Answers

You could use pattern matching.

let convertArrayToRecord = function
    | [|one; two; three; four; five|] ->
        {
            One = int one
            Two = int two
            Three = int three
            Four = four
            Five = int five
        }
    | _ ->
        failwith "How do you want to deal with arrays of a different length"

When adding another entry to the array you'd adjust it by editing the first match to [|one; six; two; three; four; five|].

By the way, for a mutable index like the one you're using in your current example, you can avoid ref by using the mutable keyword instead, like so;

let mutable index = -1
let getIndex =
    index <- index + 1
    index

And if we hide the mutable inside the getIndex function

let getIndex =
    let mutable index = -1
    fun () ->
        index <- index + 1
        index
like image 195
hlo Avatar answered Feb 07 '23 09:02

hlo


You could let the indexes be handled with pattern matching, and add an active pattern, like this:

  let (|PInt32|_|) (s:string) =
    let ok, i = Int32.TryParse(s)
    if ok then Some(PInt32(s)) else None

  let foo() =
    match [|"1"; "2"; "Some str"|] with
    | [|PInt32(x); PInt32(y); mystr|] ->
      printfn "Yup"
    | _ -> printfn "Nope"
like image 25
Isak Avatar answered Feb 07 '23 11:02

Isak