Saturday, February 6, 2016

Item and Slice F#

"Slice, I say. Pauca, pauca. Slice! That's my humour."
-- Shakespeare, The Merry Wives of Windsor
Act I, Scene I, Line 125

I've been working my way through Dave Fancher's excellent Book of F# and found an interesting example of indexing and slicing arrays in F#.  Since programming is a full contact sport, here is my take on indexing and slicing in F#.

namespace OO
type Words(sentence : string) =
let words = sentence.Split ' '
member x.Item(i) = words.[i]
member x.GetSlice(s : int option, e : int option) =
match s, e with
| Some(s), Some(e) -> if e > words.Length then words.[s..] else words.[s..e]
| Some(s), None -> words.[s..]
| None, Some(e) -> if e > words.Length then words else words.[..e]
| _ -> Array.empty<string>
module Tests =
open Xunit
open FsUnit.Xunit
[<Theory>]
[<InlineData("quick brown fox")>]
[<InlineData("1 2 3")>]
[<InlineData("one two")>]
let ``given a sentence it must be able to get the 2nd word`` (sentence : string) =
let ``2nd`` = 1
Words(sentence).[``2nd``] |> should equal <| sentence.Split(' ').[``2nd``]
[<Theory>]
[<InlineData("nope yep yep nope")>]
[<InlineData("1 2 3")>]
let ``given a sentence it must be able to get the 2nd and 3rd words`` (sentence : string) =
let ``2nd`` = 1
let ``3rd`` = 2
Words(sentence).[``2nd``..``3rd``] |> should equal <| sentence.Split(' ').[``2nd``..``3rd``]
[<Theory>]
[<InlineData("nope yep yep yep")>]
[<InlineData("1 2 3 4 5 6 7")>]
[<InlineData("one two")>]
let ``given a sentence it must be able to cutoff the 1st word`` (sentence : string) =
let ``2nd`` = 1
Words(sentence).[``2nd``..] |> should equal <| sentence.Split(' ').[``2nd``..]
[<Theory>]
[<InlineData("yep yep nope")>]
[<InlineData("1 2 3 4 5 6 7")>]
[<InlineData("one two three")>]
let ``given a sentence it must be able to get the 1st and 2nd words`` (sentence : string) =
let ``2nd`` = 1
Words(sentence).[..``2nd``] |> should equal <| sentence.Split(' ').[..``2nd``]
[<Theory>]
[<InlineData("it is not size")>]
[<InlineData("that matters")>]
let ``given a sentence it must be able to slice if length is longer than number of words`` (sentence : string) =
let longer = 100
Words(sentence).[0..longer] |> should equal <| sentence.Split ' '
Words(sentence).[..longer] |> should equal <| sentence.Split ' '
[<Theory>]
[<InlineData("a b c")>]
[<InlineData("1 2 3 4 5 6 7 8")>]
let ``given a sentence it must be able to slice if length is 1 beyond the end`` (sentence : string) =
Words(sentence).[..sentence.Length] |> should equal <| sentence.Split ' '


We have a constructor for a type called Word which takes a string and separates it into words.

type Words(sentence : string)

We implement an index with the Item member.

member x.Item(i) = words.[i]

This allows us to access the words of the string given.

Words(sentence).[1]

We also implement an array slice with the GetSlice member.

member x.GetSlice(s : int option, e : int option)

This allows us to slice the array (substring it) of words in the string given.

Words(sentence).[1..2]

We can go one better than built in slice by allow for range in the slice to be bigger than the number of words.

let longer = 100
Words(sentence).[0..longer]

In order to allow for this we have to check to see if the end is greater than the number of words.

if e > words.Length then words.[s..] else words.[s..e]

and

if e > words.Length then words else words.[..e]

I hope I have shown that F# has some very nice support for objects.