HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Hello Functional World: Counting occurrences of each letter

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
countingeachoccurrencesletterworldfunctionalhello

Problem

Coming from a OO (C#) background, I am trying to learn some FP. To help me transition, I am trying to learn F#. I am taking baby steps.

I set myself a simple challenge to count the instances of each letter in a sentence, ordered alphabetically. ("Hello World" -> "[D, 1]; [E, 1]; [H, 1]; [L, 3]; [O, 2]; [R, 1]; [W, 1]; ").

My solution works but I can't help but thinking that I am not able to break out of an imperative style (my solution is based on Linq type thinking).

open System

let getLetterCount xs = 
    xs |>
    Seq.filter Char.IsLetter |>
    Seq.map Char.ToUpper |> 
    Seq.groupBy id |>
    Seq.sortBy fst |>
    Seq.map (fun (k,v) -> (k, Seq.length v))

let tuplesToString = Seq.fold (fun a (k,v) -> a + sprintf "[%c, %i]; " k v) ""

printfn "%s" (tuplesToString (getLetterCount "Hello World"))


Does anyone know of a good source of challenges to help me get my teeth into learning this?

Solution

As @Caridorc says, this is functional code, not imperative. It's basically pretty good.

It would be a good idea to include type annotations — for example, (xs:string). If you wanted to (and I'm not saying it would be better to do so), you could drop xs as an explicit parameter and change the pipeline into a composition of functions. Also, you could sort before grouping for a slight simplification.

I find the fold in tuplesToString a bit hard to follow. In my opinion, using String.concat would be clearer, and it would also eliminate the extra space at the end of the line.

I'd rename both functions to have "counts" in their name. You can see the coherence in getLetterCounts |> countsToString.

open System

let getLetterCounts:(string -> seq) =
    Seq.filter Char.IsLetter >>
    Seq.map Char.ToUpper >>
    Seq.sort >>
    Seq.groupBy id >>
    Seq.map (fun (k, v) -> (k, Seq.length v))

let countsToString (counts:seq) =
    [for k, v in counts -> sprintf "[%c, %i];" k v] |> String.concat " "

"Hello World" |> getLetterCounts |> countsToString |> printfn "%s"

Code Snippets

open System

let getLetterCounts:(string -> seq<char * int>) =
    Seq.filter Char.IsLetter >>
    Seq.map Char.ToUpper >>
    Seq.sort >>
    Seq.groupBy id >>
    Seq.map (fun (k, v) -> (k, Seq.length v))

let countsToString (counts:seq<char * int>) =
    [for k, v in counts -> sprintf "[%c, %i];" k v] |> String.concat " "

"Hello World" |> getLetterCounts |> countsToString |> printfn "%s"

Context

StackExchange Code Review Q#100366, answer score: 3

Revisions (0)

No revisions yet.