2
votes

I'm writing small functions to chop a string in packets of given length but I am a beginner in Haskell and I think I could simplify my functions. Here they are :

packetAux _ [] = []
packetAux 0 ls = [] 
packetAux n l@(x:xs) = if n > (length l) then [] else x : packetAux (n - 1) xs

packet _ [] = []
packet 0 l = []
packet n l@(x:xs) = [x | x <- ((packetAux n l) : (packet n xs)), x /= ""]

Ex : packet 2 "12345" gives ["12","23","34","45"]

How could I avoid 1) repetitions in packetAux and packet 2) filtering the result in packet with x /= "" ?

2

2 Answers

5
votes

Your function is

import Data.List (tails)

packets :: Int -> [a] -> [[a]]
packets n xs = filter ((==n).length) $ map (take n) $ tails xs

Another way to write it is

packets n xs = foldr (zipWith (:)) (repeat []) $ take n $ tails xs

Both variants will actually work on infinite lists too, in a productive manner.

$ operator is just for easy grouping without parentheses:

f $ g $ x = f ( g ( x ) ) 
2
votes

A general trick is to use Hoogle or Hayoo! to check if a similar function does not exist. The signature of packet is:

Int -> [Char] -> [[Char]]

If you fill in Hoogle with this above, among the first results you will have the function take which has a very similar signature:

Int -> [a] -> [a]

It takes the "n" first elements of a given list.

This will help us to reduce the main part of the function:

packet n (x:xs) = [[x] ++ take (n-1) xs] ++ packet n xs

Now, since you do not want the function to return a value when the length of the list is smaller than the n value, we can use guards to keep things simple:

packet n l@(x:xs)
    | length l < n = []
    | otherwise    = [[x] ++ take (n-1) xs] ++ packet n xs

Note that there are probably ways to improve the speed by reducing the number of calls to the length function. Instead of calling it each time, we could keep an index. But then, it would obviously mean a bit more code...