patterncsharpMinor
Write a program to find the longest word made of other words
Viewed 0 times
themadewordsprogramotherwritewordfindlongest
Problem
This is my solution to the problem in the title. The code is pretty straightforward. The one problem is with
```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LongestWordFromWords
{
internal class Program
{
public static string FindLongestWords(string[] listOfWords)
{
if (listOfWords == null) throw new ArgumentException("listOfWords");
var sortedWords = listOfWords.OrderByDescending(word => word.Length).ToList();
var dict = new HashSet(sortedWords);
foreach (var word in sortedWords)
{
if (isMadeOfWords(word, dict))
{
return word;
}
}
return null;
}
private static bool isMadeOfWords(string word, HashSet dict)
{
if (String.IsNullOrEmpty(word)) return false;
if (word.Length == 1)
{
if (dict.Contains(word)) return true;
else return false;
}
foreach (var pair in generatePairs(word))
{
if (dict.Contains(pair.Item1))
{
if (dict.Contains(pair.Item2))
{
return true;
}
else
{
return isMadeOfWords(pair.Item2, dict);
}
}
}
return false;
}
private static List> generatePairs(string word)
{
var output = new List>();
for (int i = 1; i < word.Length; i++)
{
output.Add(Tuple.Create(word.Substring(0, i), word.Substring(i)));
}
return output;
}
private static void Main(string[] args)
{
substring; it's \$O(n)\$ in .Net and creates garbage. Any other improvements?```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LongestWordFromWords
{
internal class Program
{
public static string FindLongestWords(string[] listOfWords)
{
if (listOfWords == null) throw new ArgumentException("listOfWords");
var sortedWords = listOfWords.OrderByDescending(word => word.Length).ToList();
var dict = new HashSet(sortedWords);
foreach (var word in sortedWords)
{
if (isMadeOfWords(word, dict))
{
return word;
}
}
return null;
}
private static bool isMadeOfWords(string word, HashSet dict)
{
if (String.IsNullOrEmpty(word)) return false;
if (word.Length == 1)
{
if (dict.Contains(word)) return true;
else return false;
}
foreach (var pair in generatePairs(word))
{
if (dict.Contains(pair.Item1))
{
if (dict.Contains(pair.Item2))
{
return true;
}
else
{
return isMadeOfWords(pair.Item2, dict);
}
}
}
return false;
}
private static List> generatePairs(string word)
{
var output = new List>();
for (int i = 1; i < word.Length; i++)
{
output.Add(Tuple.Create(word.Substring(0, i), word.Substring(i)));
}
return output;
}
private static void Main(string[] args)
{
Solution
The first thing that strikes me is that you've put everything within the
Let's start with
This guard clause is throwing the wrong exception, it should be an
I see you're using
Now the next thing I see is a lie -
And here we are. You're
Turns into this one-liner (notice
I find that's more readable than the more concise form that ReSharper suggests:
And then
I'd put that logic in its own class, and then... looks good!
Program class, which forces your methods to be static, and any program where everything is static is a program I want to rewrite :)Let's start with
FindLongestWords(string[]):if (listOfWords == null) throw new ArgumentException("listOfWords");This guard clause is throwing the wrong exception, it should be an
ArgumentNullException. Now the goal of a guard clause is to fail fast. Interestingly if you leave it out, the next line would throw an ArgumentNullException all by itself, merely by passing it to the OrderByDescending extension method:var sortedWords = listOfWords.OrderByDescending(word => word.Length).ToList();I see you're using
var - I like that. So sortedWords is a List. I think the method could happily take any IEnumerable instead of an array.Now the next thing I see is a lie -
dict would be an almost-acceptable name for any IDictionary, but it's a HashSet...var dict = new HashSet(sortedWords);And here we are. You're
using System.Linq, so this loop could very well be rewritten with a much shorter Linq-expression:foreach (var word in sortedWords)
{
if (isMadeOfWords(word, dict))
{
return word;
}
}
return null;Turns into this one-liner (notice
dict is gone!):return sortedWords.FirstOrDefault(word => isMadeOfWords(word, sortedWords));isMadeOfWords should be named IsMadeOfWords, and can be happy with some ICollection:private static bool IsMadeOfWords(string word, ICollection dict)
{
if (String.IsNullOrEmpty(word)) return false;
if (word.Length == 1)
{
return dict.Contains(word);
}
foreach (var pair in generatePairs(word).Where(pair => dict.Contains(pair.Item1)))
{
return dict.Contains(pair.Item2) || IsMadeOfWords(pair.Item2, dict);
}
return false;
}I find that's more readable than the more concise form that ReSharper suggests:
if (String.IsNullOrEmpty(word)) return false;
return word.Length == 1
? dict.Contains(word)
: generatePairs(word).Where(pair => dict.Contains(pair.Item1))
.Select(pair => dict.Contains(pair.Item2) || IsMadeOfWords(pair.Item2, dict))
.FirstOrDefault();And then
generatePairs should be renamed to GeneratePairs and can return IEnumerable> instead of List>. The only thing that itches here is the absence of var in for(int i = 1... - if you're going to use var, might as well go all the way!I'd put that logic in its own class, and then... looks good!
Code Snippets
if (listOfWords == null) throw new ArgumentException("listOfWords");var sortedWords = listOfWords.OrderByDescending(word => word.Length).ToList();var dict = new HashSet<String>(sortedWords);foreach (var word in sortedWords)
{
if (isMadeOfWords(word, dict))
{
return word;
}
}
return null;return sortedWords.FirstOrDefault(word => isMadeOfWords(word, sortedWords));Context
StackExchange Code Review Q#33266, answer score: 5
Revisions (0)
No revisions yet.