Last active
December 13, 2019 18:03
-
-
Save pragdave/68f56f8d9e36605ce44a7b0e1cb22203 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
/// <summary> | |
/// Builds a map where the keys are word signatures and | |
/// each value is the list of words that share that signature. | |
/// The signature of a word is simply a string containing | |
/// the word's letters, sorted. Thus "dog" and "god" will | |
/// both have the signature "dgo", and the entry in the map | |
/// with that key will be those two words. | |
/// | |
/// This let's us quickly find anagrams | |
/// </summary> | |
module AnagramMap = | |
type T = Map<string, string list> | |
let empty : T = Map.empty | |
let signatureOf (word:string) = | |
word.ToLower() | |
|> Seq.sort | |
|> System.String.Concat | |
let addOrUpdate (anagrams:T) word : T = | |
let signature = signatureOf word | |
let wordsWithSignature = | |
match anagrams.TryFind(signature) with | |
| Some words -> word :: words | |
| None -> [ word ] | |
in | |
Map.add signature wordsWithSignature anagrams | |
let buildDictionary wordList = | |
Seq.fold addOrUpdate empty wordList | |
let anagramsOf word (anagrams:T) = | |
Map.tryFind (signatureOf word) anagrams | |
let otherAnagramsOf word = | |
Map.tryFind (signatureOf word) |
Overall, I wouldn't worry about all the different possible implementations that you could do. It's easy to get caught up in over refinement and being too clever. :)
Your original version is fine -- it's clear and understandable and that's the most important thing.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Looks fine to me! I don't mind a few type annotations :)
The only change I would make would be to separate the public interface (
buildDictionary
,anagramsOf
) from the rest of the code, or you could putaddOrUpdate
insidebuildDictionary
The first
buildDictionary
is based on your original, with the folder function moved inside as a inner helper function.The second
buildDictionary2
is purely functional, using the collection functions. There are lots of collection functions in F#, and it's useful to know what's available. Here's a post I wrote that might help.Next, I wouldn't object to using a mutable dictionary during the building process rather than being pure. No one else will see it, and it makes the building logic simpler. :) In the code below, I use
System.Collections.Generic.Dictionary<_,_>
to build it and then cast it into aIReadOnlyDictionary
so that it can't be mutated accidentally.Finally, if you wanted to hide the implementation of the dictionary/map completely, you could wrap the dictionary in a new, distinct type with private data, like this:
By defining it this way, the type itself is public and can be used anywhere, but the data inside it is private and inaccessible.
Here's the full code for that version
By making the inner type private, the consumer is forced to use only the public factory function (
buildDictionary
) and the public access function (anagramsOf
)Again, I added a purely functional builder as well (
buildDictionary2
) but this would be somewhat slower with large sets (>10K) of words because of the multiple iterations through the list.