How is map implemented internally? - haskell

Ok so i want to implement my custom map that receives a replication factor and a target list.
Inputs: Int -> [Int]
Output: [[Int]]
E.g.: 2 [1,2] -----> [[1,1],[2,2]]
f [1,2,3] -> map -> [f(1),f(2),f(3)]
What is supposed to happen with f(1) when map goes to the next element of the list?How should i replace 1 with f(1) ?
P.S: This was my initial solution but it does replicate the initial list,not every element.
replicate::Int->[Int]->[[Int]]
replicate 1 x=x
replicate factor (x:xs)= go factor [] (x:xs) where
go factor ls (x:xs) =go factor (repl factor x):ls xs
repl 1 nr=nr
repl times nr=nr:repl (times-1) nr

There are two issues, that prevent your code from compiling:
null function has the type [a0] -> Bool, but you're applying it
on an element of a list, hence you're expecting it to be Int -> Bool
The result f x shouldn't be pushed into the tail of the input, it
should be pushed into the result of recursive call of the function:
f x: (mymap f xs) instead of f x: xs
As a result the following code should work:
mymap :: (Int -> [Int]) -> [Int]-> [[Int]]
mymap f (x:xs) = if null xs then [] else f x : (mymap f xs)
By the way, the Standard Library provides much readable (and also polymorphic) implementation using pattern-matching:
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs

custom map that receives a replication factor and a target list
It is a little unclear to me what you're asking for.
Does mymap receive the replication factor, or does f?
E.g.: 2 [1,2] -----> [[1,1],[2,2]]
If you want mymap 2 [1,2] to give [[1,1],[2,2]], then:
mymap :: Int -> [a] -> [[a]]
mymap = map . replicate
However,
mymap :: (Int -> [Int]) -> [Int] -> [[Int]]
How is this function any different from the built-in map :: (a -> b) -> [a] -> [b] with a as Int and b as [Int]? Here, mymap does not have any Int argument itself, so you must mean that f's argument is the replication factor; but if f 2 3 == [3,3], then f is replicate and you can use the solution above.
You can write this using your own recursive definitions, if you like:
mymap :: Int -> [a] -> [[a]]
mymap _ [] = []
mymap n (x:xs) = myreplicate n x : mymap n xs
myreplicate :: Int -> a -> [a]
myreplicate 0 _ = []
myreplicate n x = x : myreplicate (n-1) x
Or you can use list comprehension instead of a map:
mymap :: Int -> [a] -> [[a]]
mymap n xs = [ replicate n x | x <- xs ]
I'd probably call mymap for replicateMany or something like that.

Related

Haskell: for every even appearance in an array, concatenate an int to the final list

I'm currently trying to write a function that takes as arguments an Int and an array of Ints and for every even value in the array, it concatenates the Int to the final array.
So, something like this:
f 3 [1,2,3,4,5,6] = [1,2,3,3,4,3,5,6,3]
This is the code I imagined would work (I'm just beginning so sorry if it's bad):
f :: Int -> [Int] -> [Int]
f(x,[]) = []
f(x,y)
|even head(y) = (head(y) ++ [x] ++ f(x,drop 1 y)
|otherwise = head(y) ++ f(x,(drop 1 y))
The error I'm getting is "Couldn't match expected type of 'Int' with actual type (a3, [[a3]])'. I understand the parameters types are mismatched, but I'm not sure how a proper syntax would look like here
You use (x, []), so that means the input type would be a tuple, so f :: (Int, [Int]) -> [Int].
I would also use pattern matching instead of head and tail, so:
f :: Int -> [Int] -> [Int]
f _ [] = []
f x (y:ys)
| even y = y : x : f x ys
| otherwise = y : f x ys
You can also generalize the type signature, and work with an inner function to avoid passing the x each time:
f :: Integral a => a -> [a] -> [a]
f x = go
where go [] = []
go (y:ys)
| even y = y : x : go ys
| otherwise = y : go ys
Another way of looking at this would be using a right fold to insert the desired element after even numbers.
f :: Int -> [Int] -> [Int]
f x lst = foldr (\y i -> if even y then y:x:i else y:i) [] lst
Which we can simplify to:
f :: Int -> [Int] -> [Int]
f x = foldr (\y i -> if even y then y:x:i else y:i) []
Note that without specifying the type, the more general inferred type of f would be:
f :: (Foldable t, Integral a) => a -> t a -> [a]

How do I put a element n times in a list without using the replicate or repeat function?

I have a function rep which requires a list with ints. My task now is to put each of these numbers in separate lists with n times the number. For example
[1,3,5] to [[1],[3,3,3],[5,5,5,5,5]] So far I can put each int in a separate list but I don't really now how to put them n times in the list without using replicate or repeat.
This is the code I have so far:
rep [] = []
rep (x1:xs) = [[x1]] ++ (rep xs)
As this is a homework/learning exercise - just want to give some hints
Split the task in two parts a function
myReplicate :: Int -> a -> [a] which replicates a single element as many times as the Int tells it to.
myReplicate n x | n > 0 = ...
| n == 0 = ...
| n < 0 = error "Error myReplicate: no negative arguments allowed"
a second function myMap :: (a -> b) -> [a] -> [b] that applies a function to every element of a given list.
myMap :: (a -> b) -> [a] -> [b]
myMap f [] = []
myMap f (x:xs) = ...
then rep = myMap (\x -> myReplicate x x)
Here a solution without repeat or replicate:
rep = map (\ n -> foldr (const (n :)) [] [1..n])

Haskell - replicate elements in a list

I want to write a Haskell program that replicates the elements of a list a given number of times.
Here's my code:
repli :: [a] -> a -> [a]
repli xs n = foldl1 (\x -> take n (repeat x)) xs
My problem is I get the following errors when compiling:
'take' is applied to too many arguments
couldn't match expected type '[a]->[a]' with actual type '[[a]]'
The type signature for foldl1 is:
foldl1 :: (a -> a -> a) -> [a] -> a
Hence, the first argument needs to be a function of two arguments. The lambda expression you're passing only takes one argument. You probably meant to do something like this:
repli :: [a] -> Int -> [a]
repli xs n = concat $ map (\x -> take n (repeat x)) xs
Or, to do it better, you can use the replicate function:
repli :: [a] -> Int -> [a]
repli xs n = concat $ map (replicate n) xs
Or, to do it even betterer, you can use the [] monad:
repli :: [a] -> Int -> [a]
repli xs n = xs >>= replicate n
How about this very simple line:
repli :: [a] -> Int -> [a]
repli xs n = concat (replicate n xs)
Consider Prelude replicate before rolling out your own function: http://zvon.org/other/haskell/Outputprelude/replicate_f.html
repli xs n = foldr (++) [] (replicate n xs)
you can use "take" and "repeat". You also need to provide a base case where the list is empty.
repli [] _ = []
repli (x:xs) y = take y (repeat x) ++ repli xs y
A simple solution that worked for me
f :: Int -> [Int] -> [Int]
f n arr = concatMap (\num -> replicate n num) arr

Can mapEvery be implemented with foldr

For a function that maps a function to every nth element in a list:
mapEvery :: Int -> (a -> a) -> [a] -> [a]
mapEvery n f = zipWith ($) (drop 1 . cycle . take n $ f : repeat id)
Is it possible to implement this with foldr like ordinary map?
EDIT: In the title, changed 'folder' to 'foldr'. Autocorrect...
Here's one solution
mapEvery :: Int -> (a -> a) -> [a] -> [a]
mapEvery n f as = foldr go (const []) as 1 where
go a as m
| m == n = f a : as 1
| otherwise = a : as (m+1)
This uses the "foldl as foldr" trick to pass state from the left to the right along the list as you fold. Essentially, if we read the type of foldr as (a -> r -> r) -> r -> [a] -> r then we instantiate r as Int -> [a] where the passed integer is the current number of elements we've passed without calling the function.
Yes, it can:
mapEvery :: Int -> (a -> a) -> [a] -> [a]
mapEvery n f xs
= foldr (\y ys -> g y : ys) []
$ zip [1..] xs
where
g (i, y) = if i `mod` n == 0 then f y else y
And since it's possible to implement zip in terms of foldr, you could get even more fold-y if you really wanted. This even works on infinite lists:
> take 20 $ mapEvery 5 (+1) $ repeat 1
[1,1,1,1,2,1,1,1,1,2,1,1,1,1,2,1,1,1,1,2]
This is what it looks like with even more foldr and inlining g:
mapEvery :: Int -> (a -> a) -> [a] -> [a]
mapEvery _ _ [] = []
mapEvery n f xs
= foldr (\(i, y) ys -> (if i `mod` n == 0 then f y else y) : ys) []
$ foldr step (const []) [1..] xs
where
step _ _ [] = []
step x zipsfn (y:ys) = (x, y) : zipsfn ys
Now, would I recommend writing it this way? Absolutely not. This is about as obfuscated as you can get while still writing "readable" code. But it does demonstrate that this is possible to use the very powerful foldr to implement relatively complex functions.

Adding predicate to a map function

Completely new to Haskell and learning through Learn Haskell the greater good.
I am looking at the map function
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
is it possible to add a predicate to this? for example, to only map to every other element in the list?
You can code your own version of map to apply f only to even (or odd) positions as follows. (Below indices start from 0)
mapEven :: (a->a) -> [a] -> [a]
mapEven f [] = []
mapEven f (x:xs) = f x : mapOdd f xs
mapOdd :: (a->a) -> [a] -> [a]
mapOdd f [] = []
mapOdd f (x:xs) = x : mapEven f xs
If instead you want to exploit the library functions, you can do something like
mapEven :: (a->a) -> [a] -> [a]
mapEven f = map (\(flag,x) -> if flag then f x else x) . zip (cycle [True,False])
or even
mapEven :: (a->a) -> [a] -> [a]
mapEven f = map (uncurry (\flag -> if flag then f else id)) . zip (cycle [True,False])
If you want to filter using an arbitrary predicate on the index, then:
mapPred :: (Int -> Bool) -> (a->a) -> [a] -> [a]
mapPred p f = map (\(i,x) -> if p i then f x else x) . zip [0..]
A more direct solution can be reached using zipWith (as #amalloy suggests).
mapEven :: (a->a) -> [a] -> [a]
mapEven f = zipWith (\flag x -> if flag then f x else x) (cycle [True,False])
This can be further refined as follows
mapEven :: (a->a) -> [a] -> [a]
mapEven f = zipWith ($) (cycle [f,id])
The "canonical" way to perform filtering based on positions is to zip the sequence with the naturals, so as to append an index to each element:
> zip [1, 1, 2, 3, 5, 8, 13] [0..]
[(1,0),(1,1),(2,2),(3,3),(5,4),(8,5),(13,6)]
This way you can filter the whole thing using the second part of the tuples, and then map a function which discards the indices:
indexedFilterMap p f xs = (map (\(x,_) -> f x)) . (filter (\(_,y) -> p y)) $ (zip xs [0..])
oddFibsPlusOne = indexedFilterMap odd (+1) [1, 1, 2, 3, 5, 8, 13]
To be specific to you question, one might simply put
mapEveryOther f = indexedFilterMap odd f
You can map with a function (a lambda is also possible):
plusIfOdd :: Int -> Int
plusIfOdd a
| odd a = a
| otherwise = a + 100
map plusIfOdd [1..5]
As a first step, write the function for what you want to do to the individual element of the list:
applytoOdd :: Integral a => (a -> a) -> a -> a
applytoOdd f x = if odd x
then (f x)
else x
So applytoOdd function will apply the function f to the element if the element is odd or else return the same element if it is even. Now you can apply map to that like this:
λ> let a = [1,2,3,4,5]
λ> map (applytoOdd (+ 100)) a
[101,2,103,4,105]
Or if you want to add 200 to it, then:
λ> map (applytoOdd (+ 200)) a
[201,2,203,4,205]
Looking on the comments, it seems you want to map based on the index position. You can modify your applytoOdd method appropriately for that:
applytoOdd :: Integral a => (b -> b) -> (a, b) -> b
applytoOdd f (x,y) = if odd x
then (f y)
else y
Here, the type variable a corresponds to the index element. If it's odd you are applying the function to the actual element of the list. And then in ghci:
λ> map (applytoOdd (+ 100)) (zip [1..5] [1..])
[101,2,103,4,105]
λ> map (applytoOdd (+ 200)) (zip [1..5] [1..])
[201,2,203,4,205]
Or use a list comprehension:
mapOdd f x = if odd x then f x else x
[ mapOdd (+100) x | x <- [1,2,3,4,5]]
I'm glad that you're taking the time to learn about Haskell. It's an amazing language. However it does require you to develop a certain mindset. So here's what I do when I face a problem in Haskell. Let's start with your problem statement:
Is it possible to add a predicate to the map function? For example, to only map to every other element in the list?
So you have two questions:
Is it possible to add a predicate to the map function?
How to map to every other element in the list?
So the way people think in Haskell is via type signatures. For example, when an engineer is designing a building she visualizes how the building should look for the top (top view), the front (front view) and the side (side view). Similarly when functional programmers write code they visualize their code in terms of type signatures.
Let's start with what we know (i.e. the type signature of the map function):
map :: (a -> b) -> [a] -> [b]
Now you want to add a predicate to the map function. A predicate is a function of the type a -> Bool. Hence a map function with a predicate will be of the type:
mapP :: (a -> Bool) -> (a -> b) -> [a] -> [b]
However, in your case, you also want to keep the unmapped values. For example mapP odd (+100) [1,2,3,4,5] should result in [101,2,103,4,105] and not [101,103,105]. Hence it follows that the type of the input list should match the type of the output list (i.e. a and b must be of the same type). Hence mapP should be of the type:
mapP :: (a -> Bool) -> (a -> a) -> [a] -> [a]
It's easy to implement a function like this:
map :: (a -> Bool) -> (a -> a) -> [a] -> [a]
mapP p f = map (\x -> if p x then f x else x)
Now to answer your second question (i.e. how to map to every other element in the list). You could use zip and unzip as follows:
snd . unzip . mapP (odd . fst) (fmap (+100)) $ zip [1..] [1,2,3,4,5]
Here's what's happening:
We first zip the index of each element with the element itself. Hence zip [1..] [1,2,3,4,5] results in [(1,1),(2,2),(3,3),(4,4),(5,5)] where the fst value of each pair is the index.
For every odd index element we apply the (+100) function to the element. Hence the resulting list is [(1,101),(2,2),(3,103),(4,4),(5,105)].
We unzip the list resulting in two separate lists ([1,2,3,4,5],[101,2,103,4,105]).
We discard the list of indices and keep the list of mapped results using snd.
We can make this function more general. The type signature of the resulting function would be:
mapI :: ((Int, a) -> Bool) -> (a -> a) -> [a] -> [a]
The definition of the mapI function is simple enough:
mapI :: ((Int, a) -> Bool) -> (a -> a) -> [a] -> [a]
mapI p f = snd . unzip . mapP p (fmap f) . zip [1..]
You can use it as follows:
mapI (odd . fst) (+100) [1,2,3,4,5]
Hope that helps.
Is it possible to add a predicate to this? for example, to only map to every other element in the list?
Yes, but functions should ideally do one relatively simple thing only. If you need to do something more complicated, ideally you should try doing it by composing two or more functions.
I'm not 100% sure I understand your question, so I'll show a few examples. First: if what you mean is that you only want to map in cases where a supplied predicate returns true of the input element, but otherwise just leave it alone, then you can do that by reusing the map function:
mapIfTrue :: (a -> Bool) -> (a -> a) -> [a] -> [a]
mapIfTrue pred f xs = map step xs
where step x | pred x = f x
| otherwise = x
If what you mean is that you want to discard list elements that don't satisfy the predicate, and apply the function to the remaining ones, then you can do that by combining map and filter:
filterMap :: (a -> Bool) -> (a -> b) -> [a] -> [b]
filterMap pred f xs = map f (filter pred xs)
Mapping the function over every other element of the list is different from these two, because it's not a predicate over the elements of the list; it's either a structural transformation of the list of a stateful traversal of it.
Also, I'm not clear whether you mean to discard or keep the elements you're not applying the function to, which would imply different answers. If you're discarding them, then you can do it by just discarding alternate list elements and then mapping the function over the remaining ones:
keepEven :: [a] -> [a]
keepEven xs = step True xs
where step _ [] = []
step True (x:xs) = x : step False xs
step False (_:xs) = step True xs
mapEven :: (a -> b) -> [a] -> [b]
mapEven f xs = map f (keepEven xs)
If you're keeping them, one way you could do it is by tagging each list element with its position, filtering the list to keep only the ones in even positions, discard the tags and then map the function:
-- Note: I'm calling the first element of a list index 0, and thus even.
mapEven :: (a -> a) -> [a] -> [a]
mapEven f xs = map aux (filter evenIndex (zip [0..] xs))
where evenIndex (i, _) = even i
aux (_, x) = f x
As another answer mentioned, zip :: [a] -> [b] -> [(a, b)] combines two lists pairwise by position.
But this is the general philosophy: to do a complex thing, use a combination of general-purpose generic functions. If you're familiar with Unix, it's similar to that.
Another simple way to write the last one. It's longer, but keep in mind that evens, odds and interleave all are generic and reusable:
evens, odds :: [a] -> [a]
evens = alternate True
odds = alternate False
alternate :: Bool -> [a] -> [a]
alternate _ [] = []
alternate True (x:xs) = x : alternate False xs
alternate False (_:xs) = alternate True xs
interleave :: [a] -> [a] -> [a]
interleave [] ys = ys
interleave (x:xs) ys = x : interleave ys xs
mapEven :: (a -> a) -> [a] -> [a]
mapEven f xs = interleave (map f (evens xs)) (odds xs)
You can't use a predicate because predicates operate on list values, not their indices.
I quite like this format for what you're trying to do, since it makes the case handling quite clear for the function:
newMap :: (t -> t) -> [t] -> [t]
newMap f [] = [] -- no items in list
newMap f [x] = [f x] -- one item in list
newMap f (x:y:xs) = (f x) : y : newMap f xs -- 2 or more items in list
For example, running:
newMap (\x -> x + 1) [1,2,3,4]
Yields:
[2,2,4,4]

Resources