Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I refactor this Haskell code to make it more elegant?

Tags:

string

haskell

As an exercise I wrote a short Haskell function that returns the first four characters of a string, concatenated. I had great trouble converting the chars to strings and resorted to an ugly replicate hack. What's the best way to improve this function? (I'm guessing that both the pattern and the output can be improved.)

concatFirstFour :: [Char] -> [Char]
concatFirstFour (a:b:c:d:_) = (replicate 1 a) ++ (replicate 1 b) ++ (replicate 1 c) ++ (replicate 1 d)
concatFirstFour xs = error "Need at least four characters."

Update: Thank you so much, everyone. I learned several things from all the answers and comments. I understand types much better.

Here's the code I ended up using:

initFirstFour :: [a] -> [a]
initFirstFour str 
               | length str > 3 = take 4 str
               | otherwise      = error "Need at least four characters."

Update 2: Changed the second pattern from xs to _ per ptival's comment. Lazy eval FTW.

Update 3: Cleaner guards from tew88's comment.

like image 894
Michael Avatar asked Apr 01 '11 21:04

Michael


2 Answers

concatFirstFour (a:b:c:d:_) = [a,b,c,d]
concatFirstFour _           = error "Need at least four characters."

or

concatFirstFour = take 4

but this last one doesn't fail on short lists...


Also note you don't need to specify the type is a [Char] (or String), since you never use this assumption in the code. Let it be [a] -> [a].

like image 183
Ptival Avatar answered Nov 15 '22 11:11

Ptival


Strings are just lists of characters, so you don't need to convert characters to string and then concatenate the strings. There are a few different ways to do this.

First off, if you want a list with just one element, you can use [x]. So:

concatFirstFour (a:b:c:d:_) = [a] ++ [b] ++ [c] ++ [d]

But this isn't really necessary. You could just do this:

concatFirstFour (a:b:c:d:_) = [a, b, c, d]

Or this:

concatFirstFour (a:b:c:d:_) = a:b:c:d:[]

Or, my preferred way:

concatFirstFour str = take 4 str

Since str is just a list, you can take the first four characters to get a new "string."

like image 34
Ian Henry Avatar answered Nov 15 '22 11:11

Ian Henry