-- 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#.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.