Reducing this Haskell function - haskell

I want to double every second element of a list. Here is the code-
doubleSec n [] = []
doubleSec n (x:xs)
| n==1 = x*2 : doubleSec 0 xs
| otherwise = x : doubleSec 1 xs
doubleSecond xs =
doubleSec 0 xs
How can I compact this logic in a single function?

You can match a pattern on the list like this
doubleSec :: [Int] -> [Int]
doubleSec [] = []
doubleSec [x] = [x]
doubleSec (x : y : xs) = x : 2* y : doubleSec xs
letting you do specific things to the second element

How about this
doubleSecond xs = map (\(x,i) -> if odd i then x*2 else x) (zip xs [0..])

This method will preserve the O(n) running time:
doubleSecond xs =
[ if isOddStep then 2 * x else x |
(isOddStep, x) <- zip (cycle [False, True]) xs ]
An more succinct version by #DavidFletcher:
doubleSecond = zipWith ($) (cycle [id, (2*)])
or:
doubleSecond = zipWith id (cycle [id, (2*)])
as suggested by #Carl.

Related

Is there a way to use "<=" for pattern matching in Haskell?

I have the following code, that drops every nth element in a list.
dropEvery :: [a] -> Int -> [a]
dropEvery xs n = f xs n ++ dropEvery (drop n xs) n
where
f ys 0 = []
f ys 1 = []
f [] m = []
f (y:ys) n = y : (f ys (n-1))
I would like to make it a bit shorter and was wondering if there is a way to use "<=" in pattern matching. I tried doing this using a where clause, which did not work, why?
f ys m = []
where
m <= 1 || ys == []
How can I shirk this redundancy? Is there a nice way to use "less or equal" in pattern matching?
EDIT: I tried this using guards
where
f ys m
| m <= 1 || null ys = []
| otherwise = (head ys) : (f (tail ys) (n-1))
You can work with a guard:
dropEvery :: [a] -> Int -> [a]
dropEvery xs n = f xs n ++ dropEvery (drop n xs) n
where
f ys i | i <= 1 = []
f [] _ = []
f (y:ys) n = y : (f ys (n-1))
If the condition in the guard is satisfied, then that clause "fires" and thus in this case will return an empty list [].
You will however get stuck in an infinite loop, since you write f xs n ++ dropEvery (n xs) n but drop 3 [] will return [], and thus it will keep calling dropEvery with an empty list.
You can make use of recursion where we each time decrement n until it reaches 0, and then we make two hops, so:
dropEvery :: Int -> [a] -> [a]
dropEvery n = go (n-1)
where go _ [] = []
go i (x:xs)
| i <= 0 = go (n-1) xs
| otherwise = x : go (i-1) xs
We can also work with splitAt :: [a] -> ([a], [a]) and with a pattern guard:
dropEvery n [] = []
dropEvery n ds
| (_:ys) <- sb = sa ++ dropEvery n ys
| otherwise = sa
where (sa, sb) = splitAt (n-1) ds

How to split a list of numbers into a set of list with all the numbers in Haskell

How do i split a list in Haskell, for example, "222 33244" into ["222","33","2","444"] only through recursion and fuctions on the prelude?
My current attempt is:
list xs
|length xs == 0 = ""
|otherwise = listSplit xs
listSplit (x:xs)
|x == head xs = x : ListSplitNext x xs
|otherwise = x:[]
listSplitNext a (x:xs)
|a == x = a : listSplitNext x xs
|otherwise = listSplit xs
So since I don't quite understand your approach and ghci lists 18 compile errors in your code, I'm afraid I can't help you with your attempt at a solution.
As pointed out in a comment, a possible solution would be:
listSplit xs = listSplit' [] (filter (`elem` ['0'..'9']) xs)
listSplit' ws (x:xs) = listSplit' (ws ++ [x : takeWhile (==x) xs]) (dropWhile (==x) xs)
listSplit' ws [] = ws
Filter every element of the string that is not a number (Data.Char.isNumber would do this, too, but the premise was to only use Prelude functions) and call listSplit' on the filtered list.
(ws ++ [x : takeWhile (==x) xs]) collects everything in xs until it reaches a letter that does not equal x, wraps this in a list and appends it to ws.
(dropWhile (==x) xs) removes every letter in xs until it reaches a letter that does not equal x.
Finally, the function calls itself with the updated ws and the reduced xs
If there are no more remaining elements, the function returns ws
If your goal is to use very few pre-defined functions this might give you some ideas:
listSplit :: String -> [String]
listSplit xs =
let (as, a) = foldr go ([], []) numbers
in a : as
where
isNumber x = x `elem` ['0'..'9']
numbers = filter isNumber xs
go cur (res, []) = (res, [cur])
go cur (res, lst#(a:_))
| a == cur = (res, a : lst)
| otherwise = (lst : res, [cur])
Of course you can replace foldr with your own recursion as well:
numberSplit :: String -> [String]
numberSplit xs =
let numbers = filter (`elem` ['0'..'9']) xs
in listSplit numbers
listSplit :: Eq a => [a] -> [[a]]
listSplit =
reverse . go [] []
where
go acc as [] = as : acc
go acc [] (x:xs) = go acc [x] xs
go acc as#(a:_) (x:xs)
| a == x = go acc (a : as) xs
| otherwise = go (as : acc) [x] xs
I had a moment to implement this but I think this is what you're looking for.
listSplit s = go filtered
where filtered = [c | c <- s, elem c ['0'..'9']]
go [] = []
go (x:xs) = (x : takeWhile (== x) xs) : (go $ dropWhile (== x) xs)

Haskell - output a type which takes 2 parametes

I'm trying to make an array of my own type letterCount in form of [('letter',occurance),(),...]. How can I make an output of a type which takes 2 parameters. Here's my code:
type LetterCount = (Char,Int)
letterOccur :: Char->[Char] -> Int
letterOccur c [] = 0
letterOccur c (x:xs) = if (c == x) then ((letterOccur c xs) + 1)
else letterOccur c xs
letterStats :: [Char] -> [LetterCount]
letterStats :: [] = []
letterStats (x:xs) = [x,(letterOccur x (x:xs))] ++ letterStats xs
I'm guessing you're trying to do something like this:
letterStats :: [Char] -> [LetterCount]
letterStats :: [] = []
letterStats (x:xs) = (x, (+1) $ letterOccur x xs) : letterStats xs
All you want to do is add +1 as you are not counting x when checking xs for all x occurances. Also what you want is to return a list of the letterCount type which is a Tuple not a list, therefore I changed
[x, letterOccur x xs] ++ letterStats xs
to
(x, (+1) $ letterOccur x xs) : letterStats xs
Although you could also do this:
[(x, (+1) $ letterOccur x xs)] ++ letterStats xs
But is unnecessary.

Haskell - Get middle numbers of a list

I want to reorder a list in the following way:
[5,6,7,8,9] -> [7,5,9,6,8]
[6,7,8,5,4,3] -> [8,5,6,3,7,4]
It's supposed to get the middle number or numbers of the list and put them in the starting position. After that it should start to get the two outer numbers of the list and add them in and work its way in.
I have the following code to get the middle numbers and put them into the beginning of the list but can't figure out how to start adding the outer numbers into the new list.
-- import Data.List
-- import System.IO
longitude xs = length xs
middle xs = length xs `div` 2
addOne xs = middle xs - 1
oneMore xs = length xs - 1
otherCase xs = oneMore xs `div` 2
valuea xs = xs !! middle xs
valueb xs = xs !! addOne xs
valuec xs = xs !! otherCase xs
modulus xs = longitude xs `mod` 2
order xs = midNums xs
takes xs = take (otherCase xs) xs
oddOne xs = otherCase xs + 1
takeX xs = drop (oddOne xs) xs
value xs = takes xs ++ takeX xs
reorder xs = drop (otherCase xs )(take (middle xs + 1) xs)
valueOdd xs = reorder xs ++ takes xs ++ takeX xs
paruno xs = drop (middle xs + 1) xs
pairTwo xs = take (addOne xs) xs
midPair xs = take (addOne xs)(drop (middle xs -1) xs)
--Get the numbers
midNums xs = if modulus xs == 0 then midPair xs ++ paruno xs ++ pairTwo xs
else valueOdd xs
I want it to work like this: Demo
Try this:
f :: (Num a) => [a] -> [a]
f [] = []
f [x] = [x]
f xs = if len `mod` 2 == 1 then flatten [xs !! half] else flatten [xs !! (half-1), xs !! half]
where len = length xs
half = len `div` 2
firsthalf = take (half-1) xs
secondhalf = (reverse . take half . drop (half+1)) xs
outtoin = zipWith (\x y -> x:y:[]) firsthalf secondhalf
flatten = concat . flip (:) outtoin
Breaking it down:
First get the midpoint(s)
Next get the two halves of the list excluding middle elements
Build the list from outside inwards using zip
Concatenate the zip result to flatten and add to the middle elements list
Demo

Simple haskell splitlist

I have the following function which takes a list and returns two sublists split at a given element n. However, I only need to split it in half, with odd length lists having a larger first sublist
splitlist :: [a] -> Int -> ([a],[a])
splitlist [] = ([],[])
splitlist l#(x : xs) n | n > 0 = (x : ys, zs)
| otherwise = (l, [])
where (ys,zs) = splitlist xs (n - 1)
I know I need to change the signature to [a] -> ([a],[a]), but where in the code should I put something like length(xs) so that I don't break recursion?
In a real program you should probably use
splitlist :: [a] -> ([a], [a])
splitlist xs = splitAt ((length xs + 1) `div` 2) xs
(i.e. something along the lines of dreamcrash's answer.)
But if, for learning purposes, you're looking for an explicitly recursive solution, study this:
splitlist :: [a] -> ([a], [a])
splitlist xs = f xs xs where
f (y : ys) (_ : _ : zs) =
let (as, bs) = f ys zs
in (y : as, bs)
f (y : ys) (_ : []) = (y : [], ys)
f ys [] = ([], ys)
You can do it using take and drop:
splitlist :: [a] -> ([a],[a])
splitlist [] = ([],[])
splitlist l = let half = (length(l) +1)`div` 2
in (take half l, drop half l)
or you can take advantage of the function splitAt:
splitlist list = splitAt ((length (list) + 1) `div` 2) list
You can do this by using the take and drop built-in functions. But if you want something that can be done with all self written functions try this:
dropList :: Int -> [Int] -> [Int]
dropList 0 [] = []
dropList 0 (x:xs) = x:xs
dropList y [] = []
dropList y (x:xs) = dropList (y-1) xs
takeList :: Int -> [Int] -> [Int]
takeList 0 [] = []
takeList 0 (x:xs) = []
takeList y [] = []
takeList y (x:xs)
| y <= length (x:xs) = x : takeList (y-1) xs
| otherwise = []
split :: Int -> [Int] -> ([Int],[Int])
split 0 [] = ([],[])
split y [] = ([],[])
split y (x:xs) = (takeList y (x:xs), dropList y (x:xs))
main = do
print (split 4 [1,2,3,4,5,6])

Resources