When I implemented a custom split function in Haskell I was really amazed to see how easy and clean the resulting code was. I'm sure there is some better and even cleaner way to write it because I'm still a Haskell newbie! Here is it:
split :: String -> Char -> [String]The above code starts by declaring the function's type; this is optional because Haskell's type system is able to automatically deduce it. It then uses pattern matching to specify the algorithm's base and recursive cases. At last, the recursive case is defined by parts, just as you do in mathematics. Oh, and why recursivity? Because iteration does not exist in functional programming in the well-known sense of imperative languages. Also note the lack of variables (except for the input ones) and that everything is an evaluable expression.
split [] delim = [""]
split (c:cs) delim
| c == delim = "" : rest
| otherwise = (c : head rest) : tail rest
where
rest = split cs delim
Let's now compare the above code with two implementations in C++. A first approach to the problem following common imperative programming thinking results in an iterative algorithm:
std::deque< std::string >This is certainly uglier and much more difficult to prove right; iteration is a complex concept in that sense. In this code we have variables that act as acumulators, temporary objects, commands, etc. Be glad that I used C++ and not C to take advantage of STL containers.
split_iterative(const std::string& str, char delim)
{
std::deque< std::string > parts;
std::string word;
for (std::string::const_iterator iter = str.begin();
iter != str.end(); iter++) {
if (*iter == delim) {
parts.push_back(word);
word.clear();
} else
word += *iter;
}
parts.push_back(word);
return parts;
}
OK, to be fair the code should be implemented in a recursive way to be really comparable to the Haskell sample function. Let's attempt it:
std::deque< std::string >This split_recursive function follows the same algorithm as the split written in Haskell. I find that it is still harder to read and more delicate (I had some segmentation fault s until I got it right).
split_recursive(const std::string& str, char delim)
{
std::deque< std::string > parts;
if (!str.empty()) {
std::string str2 = str;
parts = split_recursive(str2.erase(0, 1), delim);
if (str[0] == delim)
parts.push_front("");
else
parts[0] = str[0] + parts[0];
} else
parts.push_front("");
return parts;
}
Of course Haskell is not appropriate for everything (which is true for every language out there). I have yet to write a big and useful program in Haskell to really see its power and to be able to relly compare it to other languages. All I can do at the moment is to compare trivial stuff as the above.