2

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 Answers 2

5

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 ) ) 
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the solutions, the 1st solution is a lot faster than mine but the 2nd one doesn't work with something where n is 0 : it gives an infinite ...","","","","","",... In fact packets of length 0 are not very useful! Thanks again!
you're welcome. the 2nd variant answers your 2), and the 1st avoids the superfluous list traversals of your 1) (past the first n elements of the list). your length l < n traverses the whole l, but length (take n l) < n would not.
Why not packets n xs = takeWhile ((==n).length) $ map (take n) $ tails xs?
@user3237465 yes, takeWhile is conceptually better, but I thought of filter first for some reason. There's no difference for infinite lists, and for the finite the difference is in the tail only; so if n is not too large (like 2 here), it's negligible. Ultimately, the 2nd variant seems preferable anyway.
2

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...

2 Comments

[x] ++ is the same as x :.
Thanks for the improvement!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.