Position of elements - haskell

the function should give me the position of the searched elements.
e. : index 'b' "dfbhjbd" -> [3,6]
I've got so far:
index b (x:xs) = length ( takeWhile ( /= b) xs )
but takeWhile will immediately stop after finding the matching elements. so following elements will be left out.
i tried something like this:
index b (x:xs) = length ( tak b xs )
where
tak b [] = []
tak b xs
| b /= x == x:tak b xs
| otherwise = [], tak b xs
Beside being unable to run the code because of parse error, it would not show the right results.
any suggestions?

you've already seen the problem with takeWhile - I can understand why you tried this (after all you want to use length) but maybe you should try to find a solution where you don't have to use length in the first place.
A good approach might be to keep track of the indizes while you traverse the input list and then look out for the elements equal to `b
if you want to use recursion then you can go all the way:
index :: (Eq a, Num t) => a -> [a] -> [t]
index b xs = index' 1 xs
where
index' _ [] = []
index' i (x:xs)
| b == x = i : index' (i+1) xs
| otherwise = index' (i+1) xs
example
λ> index 'b' "dfbhjbd"
[3,6]
as you can see I'm using a inner function here that takes care of the indizes and the recursion
another approach would be to use zip to get pairs of indizes and elements and then use filter and map to get the results out:
this corresponds to what you tried earlier
index :: (Eq a, Num t, Enum t) => a -> [a] -> [t]
index b xs =
map fst . filter ((== b) . snd) $ zip [1..] xs

Related

How to move all occurrences of the first element of the list to the end of the list in Haskell?

I want to extract the first element and all of its occurrences from a list and put them in the end of a list in Haskell
What I have now is this.
relegate [] = []
relegate (x:xs) = xs ++ [x]
With input [3 ,1 ,4 ,3 ,5 ,3 ,8] it returns me [1,4,3,5,3,8,3] but I wanted instead to return this list [1 ,4 ,5 ,8 ,3 ,3 ,3].
Right now you're just adding x to the end, which will only move the first occurrence, not all of them. You should instead find all equal elements, and remove them from the list. This can be done with partition in Data.List, which takes a predicate function and gets a list of elements that matches the predicate and a list that doesn't:
import Data.List
relegate :: (Eq a) => [a] -> [a]
relegate [] = []
relegate l#(x:_) = nonMatching ++ matching
where (nonMatching, matching) = partition (/= x) l
Or, more pointfree:
import Data.List
relegate :: (Eq a) => [a] -> [a]
relegate [] = []
relegate l = uncurry (++) $ partition (/= head l) l
This algorithm requires to first determine that first element x, and then enumerate over the list, here we will yield all elements that are different from x, and keep track of the number of times we have found an element that is equal to x, and when we reach the end of the list, we replicate x the number of times we encountered x in the list:
relegate :: Eq a => [a] -> [a]
relegate [] = []
relegate (x:xs) = go xs 1
where go [] n = replicate n x
go (y:ys) m
| x == y = go ys (m+1)
| otherwise = y : go ys m
This of course assumes that if x == y, then x and y are the same element. If that is not the case, you should work with an accumulator that keeps track of the equal elements.

Split a list into non-empty sub-lists in Haskell

I have to split the given list into non-empty sub-lists each of which
is either in strictly ascending order, in strictly descending order, or contains all equal elements. For example, [5,6,7,2,1,1,1] should become [[5,6,7],[2,1],[1,1]].
Here is what I have done so far:
splitSort :: Ord a => [a] -> [[a]]
splitSort ns = foldr k [] ns
where
k a [] = [[a]]
k a ns'#(y:ys) | a <= head y = (a:y):ys
| otherwise = [a]:ns'
I think I am quite close but when I use it it outputs [[5,6,7],[2],[1,1,1]] instead of [[5,6,7],[2,1],[1,1]].
Here is a kinda ugly solution, with three reverse in one line of code :).
addElement :: Ord a => a -> [[a]] -> [[a]]
addElement a [] = [[a]]
addElement a (x:xss) = case x of
(x1:x2:xs)
| any (check a x1 x2) [(==),(<),(>)] -> (a:x1:x2:xs):xss
| otherwise -> [a]:(x:xss)
_ -> (a:x):xss
where
check x1 x2 x3 op = (x1 `op` x2) && (x2 `op` x3)
splitSort xs = reverse $ map reverse $ foldr addElement [] (reverse xs)
You can possibly get rid of all the reversing if you modify addElement a bit.
EDIT:
Here is a less reversing version (even works for infinite lists):
splitSort2 [] = []
splitSort2 [x] = [[x]]
splitSort2 (x:y:xys) = (x:y:map snd here):splitSort2 (map snd later)
where
(here,later) = span ((==c) . uncurry compare) (zip (y:xys) xys)
c = compare x y
EDIT 2:
Finally, here is a solution based on a single decorating/undecorating, that avoids comparing any two values more than once and is probably a lot more efficient.
splitSort xs = go (decorate xs) where
decorate :: Ord a => [a] -> [(Ordering,a)]
decorate xs = zipWith (\x y -> (compare x y,y)) (undefined:xs) xs
go :: [(Ordering,a)] -> [[a]]
go ((_,x):(c,y):xys) = let (here, later) = span ((==c) . fst) xys in
(x : y : map snd here) : go later
go xs = map (return . snd) xs -- Deal with both base cases
Every ordered prefix is already in some order, and you don't care in which, as long as it is the longest:
import Data.List (group, unfoldr)
foo :: Ord t => [t] -> [[t]]
foo = unfoldr f
where
f [] = Nothing
f [x] = Just ([x], [])
f xs = Just $ splitAt (length g + 1) xs
where
(g : _) = group $ zipWith compare xs (tail xs)
length can be fused in to make the splitAt count in unary essentially, and thus not be as strict (unnecessarily, as Jonas Duregård rightly commented):
....
f xs = Just $ foldr c z g xs
where
(g : _) = group $ zipWith compare xs (tail xs)
c _ r (x:xs) = let { (a,b) = r xs } in (x:a, b)
z (x:xs) = ([x], xs)
The initial try turned out to be lengthy probably inefficient but i will keep it striked for the sake of integrity with the comments. You best just skip to the end for the answer.
Nice question... but turns out to be a little hard candy. My approach is in segments, those of each i will explain;
import Data.List (groupBy)
splitSort :: Ord a => [a] -> [[a]]
splitSort (x:xs) = (:) <$> (x :) . head <*> tail $ interim
where
pattern = zipWith compare <$> init <*> tail
tuples = zipWith (,) <$> tail <*> pattern
groups = groupBy (\p c -> snd p == snd c) . tuples $ (x:xs)
interim = groups >>= return . map fst
*Main> splitSort [5,6,7,2,1,1,1]
[[5,6,7],[2,1],[1,1]]
The pattern function (zipWith compare <$> init <*> tail) is of type Ord a => [a] -> [Ordering] when fed with [5,6,7,2,1,1,1] compares the init of it by the tail of it by zipWith. So the result would be [LT,LT,GT,GT,EQ,EQ]. This is the pattern we need.
The tuples function will take the tail of our list and will tuple up it's elements with the corresponding elements from the result of pattern. So we will end up with something like [(6,LT),(7,LT),(2,GT),(1,GT),(1,EQ),(1,EQ)].
The groups function utilizes Data.List.groupBy over the second items of the tuples and generates the required sublists such as [[(6,LT),(7,LT)],[(2,GT),(1,GT)],[(1,EQ),(1,EQ)]]
Interim is where we monadically get rid of the Ordering type values and tuples. The result of interim is [[6,7],[2,1],[1,1]].
Finally at the main function body (:) <$> (x :) . head <*> tail $ interim appends the first item of our list (x) to the sublist at head (it has to be there whatever the case) and gloriously present the solution.
Edit: So investigating the [0,1,0,1] resulting [[0,1],[0],[1]] problem that #Jonas Duregård discovered, we can conclude that in the result there shall be no sub lists with a length of 1 except for the last one when singled out. I mean for an input like [0,1,0,1,0,1,0] the above code produces [[0,1],[0],[1],[0],[1],[0]] while it should [[0,1],[0,1],[0,1],[0]]. So I believe adding a squeeze function at the very last stage should correct the logic.
import Data.List (groupBy)
splitSort :: Ord a => [a] -> [[a]]
splitSort [] = []
splitSort [x] = [[x]]
splitSort (x:xs) = squeeze $ (:) <$> (x :) . head <*> tail $ interim
where
pattern = zipWith compare <$> init <*> tail
tuples = zipWith (,) <$> tail <*> pattern
groups = groupBy (\p c -> snd p == snd c) $ tuples (x:xs)
interim = groups >>= return . map fst
squeeze [] = []
squeeze [y] = [y]
squeeze ([n]:[m]:ys) = [n,m] : squeeze ys
squeeze ([n]:(m1:m2:ms):ys) | compare n m1 == compare m1 m2 = (n:m1:m2:ms) : squeeze ys
| otherwise = [n] : (m1:m2:ms) : squeeze ys
squeeze (y:ys) = y : squeeze s
*Main> splitSort [0,1, 0, 1, 0, 1, 0]
[[0,1],[0,1],[0,1],[0]]
*Main> splitSort [5,6,7,2,1,1,1]
[[5,6,7],[2,1],[1,1]]
*Main> splitSort [0,0,1,0,-1]
[[0,0],[1,0,-1]]
Yes; as you will also agree the code has turned out to be a little too lengthy and possibly not so efficient.
The Answer: I have to trust the back of my head when it keeps telling me i am not on the right track. Sometimes, like in this case, the problem reduces down to a single if then else instruction, much simpler than i had initially anticipated.
runner :: Ord a => Maybe Ordering -> [a] -> [[a]]
runner _ [] = []
runner _ [p] = [[p]]
runner mo (p:q:rs) = let mo' = Just (compare p q)
(s:ss) = runner mo' (q:rs)
in if mo == mo' || mo == Nothing then (p:s):ss
else [p] : runner Nothing (q:rs)
splitSort :: Ord a => [a] -> [[a]]
splitSort = runner Nothing
My test cases
*Main> splitSort [0,1, 0, 1, 0, 1, 0]
[[0,1],[0,1],[0,1],[0]]
*Main> splitSort [5,6,7,2,1,1,1]
[[5,6,7],[2,1],[1,1]]
*Main> splitSort [0,0,1,0,-1]
[[0,0],[1,0,-1]]
*Main> splitSort [1,2,3,5,2,0,0,0,-1,-1,0]
[[1,2,3,5],[2,0],[0,0],[-1,-1],[0]]
For this solution I am making the assumption that you want the "longest rally". By that I mean:
splitSort [0, 1, 0, 1] = [[0,1], [0,1]] -- This is OK
splitSort [0, 1, 0, 1] = [[0,1], [0], [1]] -- This is not OK despite of fitting your requirements
Essentially, There are two pieces:
Firstly, split the list in two parts: (a, b). Part a is the longest rally considering the order of the two first elements. Part b is the rest of the list.
Secondly, apply splitSort on b and put all list into one list of list
Taking the longest rally is surprisingly messy but straight. Given the list x:y:xs: by construction x and y will belong to the rally. The elements in xs belonging to the rally depends on whether or not they follow the Ordering of x and y. To check this point, you zip every element with the Ordering is has compared against its previous element and split the list when the Ordering changes. (edge cases are pattern matched) In code:
import Data.List
import Data.Function
-- This function split the list in two (Longest Rally, Rest of the list)
splitSort' :: Ord a => [a] -> ([a], [a])
splitSort' [] = ([], [])
splitSort' (x:[]) = ([x],[])
splitSort' l#(x:y:xs) = case span ( (o ==) . snd) $ zip (y:xs) relativeOrder of
(f, s) -> (x:map fst f, map fst s)
where relativeOrder = zipWith compare (y:xs) l
o = compare y x
-- This applies the previous recursively
splitSort :: Ord a => [a] -> [[a]]
splitSort [] = []
splitSort (x:[]) = [[x]]
splitSort (x:y:[]) = [[x,y]]
splitSort l#(x:y:xs) = fst sl:splitSort (snd sl)
where sl = splitSort' l
I wonder whether this question can be solve using foldr if splits and groups a list from
[5,6,7,2,1,1,1]
to
[[5,6,7],[2,1],[1,1]]
instead of
[[5,6,7],[2],[1,1,1]]
The problem is in each step of foldr, we only know the sorted sub-list on right-hand side and a number to be processed. e.g. after read [1,1] of [5,6,7,2,1,1,1] and next step, we have
1, [[1, 1]]
There are no enough information to determine whether make a new group of 1 or group 1 to [[1,1]]
And therefore, we may construct required sorted sub-lists by reading elements of list from left to right, and why foldl to be used. Here is a solution without optimization of speed.
EDIT:
As the problems that #Jonas Duregård pointed out on comment, some redundant code has been removed, and beware that it is not a efficient solution.
splitSort::Ord a=>[a]->[[a]]
splitSort numList = foldl step [] numList
where step [] n = [[n]]
step sublists n = groupSublist (init sublists) (last sublists) n
groupSublist sublists [n1] n2 = sublists ++ [[n1, n2]]
groupSublist sublists sortedList#(n1:n2:ns) n3
| isEqual n1 n2 = groupIf (isEqual n2 n3) sortedList n3
| isAscen n1 n2 = groupIfNull isAscen sortedList n3
| isDesce n1 n2 = groupIfNull isDesce sortedList n3
| otherwise = mkNewGroup sortedList n3
where groupIfNull check sublist#(n1:n2:ns) n3
| null ns = groupIf (check n2 n3) [n1, n2] n3
| otherwise = groupIf (check (last ns) n3) sublist n3
groupIf isGroup | isGroup = addToGroup
| otherwise = mkNewGroup
addToGroup gp n = sublists ++ [(gp ++ [n])]
mkNewGroup gp n = sublists ++ [gp] ++ [[n]]
isEqual x y = x == y
isAscen x y = x < y
isDesce x y = x > y
My initial thought looks like:
ordruns :: Ord a => [a] -> [[a]]
ordruns = foldr extend []
where
extend a [ ] = [ [a] ]
extend a ( [b] : runs) = [a,b] : runs
extend a (run#(b:c:etc) : runs)
| compare a b == compare b c = (a:run) : runs
| otherwise = [a] : run : runs
This eagerly fills from the right, while maintaining the Ordering in all neighbouring pairs for each sublist. Thus only the first result can end up with a single item in it.
The thought process is this: an Ordering describes the three types of subsequence we're looking for: ascending LT, equal EQ or descending GT. Keeping it the same every time we add on another item means it will match throughout the subsequence. So we know we need to start a new run whenever the Ordering does not match. Furthermore, it's impossible to compare 0 or 1 items, so every run we create contains at least 1 and if there's only 1 we do add the new item.
We could add more rules, such as a preference for filling left or right. A reasonable optimization is to store the ordering for a sequence instead of comparing the leading two items twice per item. And we could also use more expressive types. I also think this version is inefficient (and inapplicable to infinite lists) due to the way it collects from the right; that was mostly so I could use cons (:) to build the lists.
Second thought: I could collect the lists from the left using plain recursion.
ordruns :: Ord a => [a] -> [[a]]
ordruns [] = []
ordruns [a] = [[a]]
ordruns (a1:a2:as) = run:runs
where
runs = ordruns rest
order = compare a1 a2
run = a1:a2:runcontinuation
(runcontinuation, rest) = collectrun a2 order as
collectrun _ _ [] = ([], [])
collectrun last order (a:as)
| order == compare last a =
let (more,rest) = collectrun a order as
in (a:more, rest)
| otherwise = ([], a:as)
More exercises. What if we build the list of comparisons just once, for use in grouping?
import Data.List
ordruns3 [] = []
ordruns3 [a] = [[a]]
ordruns3 xs = unfoldr collectrun marked
where
pairOrder = zipWith compare xs (tail xs)
marked = zip (head pairOrder : pairOrder) xs
collectrun [] = Nothing
collectrun ((o,x):xs) = Just (x:map snd markedgroup, rest)
where (markedgroup, rest) = span ((o==).fst) xs
And then there's the part where there's a groupBy :: (a -> a -> Bool) -> [a] -> [[a]] but no groupOn :: Eq b => (a -> b) -> [a] -> [[a]]. We can use a wrapper type to handle that.
import Data.List
data Grouped t = Grouped Ordering t
instance Eq (Grouped t) where
(Grouped o1 _) == (Grouped o2 _) = o1 == o2
ordruns4 [] = []
ordruns4 [a] = [[a]]
ordruns4 xs = unmarked
where
pairOrder = zipWith compare xs (tail xs)
marked = group $ zipWith Grouped (head pairOrder : pairOrder) xs
unmarked = map (map (\(Grouped _ t) -> t)) marked
Of course, the wrapper type's test can be converted into a function to use groupBy instead:
import Data.List
ordruns5 [] = []
ordruns5 [a] = [[a]]
ordruns5 xs = map (map snd) marked
where
pairOrder = zipWith compare xs (tail xs)
marked = groupBy (\a b -> fst a == fst b) $
zip (head pairOrder : pairOrder) xs
These marking versions arrive at the same decoration concept Jonas Duregård applied.

Haskell type mismatch Int and [Int]

I'm a beginner to Haskell. I'm trying to create a function which has two parameters: a character and a string.
This function is supposed to go through the string and check if the character given is in the string, and then return a list of integers representing the position of the characters in the string.
My code is:
tegnPose :: Char -> String -> [Int]
tegnPose c [] = []
tegnPose c (x:xs) = [if not (xs !! a == c)
then [a] ++ tegnPose c xs
else tegnPose c xs |a <- [0.. length xs - 1]]
Which is a recursive function with list comprehension.
The error I get:
Uke4.hs:14:7: error:
* Couldn't match expected type `Int' with actual type `[Int]'
* In the expression: [a] ++ tegnPose c xs
In the expression:
if not (xs !! a == c) then [a] ++ tegnPose c xs else tegnPose c xs
In the expression:
[if not (xs !! a == c) then
[a] ++ tegnPose c xs
else
tegnPose c xs |
a <- [0 .. length xs - 1]]
|
14 | then [a] ++ tegnPose c xs
| ^^^^^^^^^^^^^^^^^^^^
Uke4.hs:15:7: error:
* Couldn't match expected type `Int' with actual type `[Int]'
* In the expression: tegnPose c xs
In the expression:
if not (xs !! a == c) then [a] ++ tegnPose c xs else tegnPose c xs
In the expression:
[if not (xs !! a == c) then
[a] ++ tegnPose c xs
else
tegnPose c xs |
a <- [0 .. length xs - 1]]
|
15 | else tegnPose c xs |a <- [0.. length xs - 1]]
I don't understand how the mismatch happens, as the recursive function should just run through.
Here's why the mismatch happens. First, note that a list comprehension that returns a list of type [a] must generate elements of type a, so you need the following to match:
example :: [Int]
-- .-- the final value is "[Int]"
-- |
example = [ 2+x*y | x <- [1..10], y <- [1..5], x < y]
-- ^^^^^
-- |
-- `- therefore, this must be "Int"
In your example, the type signature for tegnPose implies that the list comprehension must return an [Int], but the expression generating list elements, namely:
if ... then [a] ++ tegnPose c xs else tegnPose c cx
is clearly not returning a plain Int the way it's supposed to.
The first error message is indicating that actual type of the subexpression [a] ++ tegnPos c xs which is [Int] does not match the expected type of the result of the entire if .. then .. else expression which should have type Int.
If I understand your question correctly (i.e., return a list of the integer positions of each occurrence of a character in a string so that tegnPose 'a' "abracadabra" returns [0,3,5,7,10], then you should either use recursion or a list comprehension, but not both.
Note that the non-recursive list comprehension:
tegnPose c xs = [a | a <- [0..length xs - 1]
almost does what you want. All that's missing is testing the condition to see if the character at position a is a c. If you don't know about using "guards" in list comprehensions, go look it up.
Alternatively, the recursive function without a list comprehension:
tegnPose c (x:xs) = if (x == c) then ??? : tegnPose c xs
else tegnPose c xs
tegnPose _ [] = []
also almost does what you want, except it's not obvious what to put in place of ??? to return a number indicating the current position. If you write a recursive version with an extra parameter:
tp n c (x:xs) = if (x == c) then n : tp (???) c xs
else tp (???) c xs
tp _ _ [] = []
with the idea that you could define:
tegnPose c xs = tp 0 c xs
then you'd be closer, if only you could figure out what new value for n should go in place of the ???.
More standard Haskell solutions might involve things like zips:
> zip [0..] "abracadabra"
[(0,'a'),(1,'b'),(2,'r'),...]
and filters:
> filter (\(i,c) -> c == 'a') $ zip [0..] "abracadabra"
[(0,'a'),(3,'a'),...]
and maps:
> map fst $ filter (\(i,c) -> c == 'a') $ zip [0..] "abracadabra"
[0,3,5,7,10]
or looking in Data.List for a function that does what you want:
> elemIndices 'a' "abracadabra"
[0,3,5,7,10]
Just for some variety a simpler way of implementing this functionality with a single foldr could be;
import Data.Bool (bool)
charIndices :: Char -> String -> [Int]
charIndices c = foldr (\t r -> bool r (fst t : r) (snd t == c)) [] . zip [0..]
*Main> charIndices 't' "tektronix test and measurement instruments"
[0,3,10,13,29,34,40]
Explanation:
Type of foldr is Foldable t => (a -> b -> b) -> b -> t a -> b
It takes three parameters;
A function which accepts two parameters
An initial value of type b
A traversable data type which hold values of type a
an returns a single value of type b.
In this particular case our type a value is Char type, which makes t a a String type (due to type signature) and type b value is a list of integers [Int].
The provided function as the first parameter is (\t r -> bool r (fst t : r) (snd t == c)) which is very simple if you check Data.bool. bool is a ternary operator of type a -> a -> Bool -> a which takes three arguments. In order they are negative result, positive result and condition. (negative is on the left as usual in Haskell). It checks if the current character is equal to our target character c, if so it returns fst t : r if not r (r means result). And finally t is the current tuple of the fed tuples list. The tuples list is constructed by zip [0..] s where s is not shown in the function definition due to partial application.

Get positions of elements in list of strings in Haskell

my title might be a bit off and i'll try to explain a bit better what i'm trying to achieve.
Basically let's say i have a list:
["1234x4","253x4",2839",2845"]
Now i'd like to add all the positions of the strings which contain element 5 to a new list. On a current example the result list would be:
[1,3]
For that i've done similar function for elem:
myElem [] _ = False
myElem [x] number =
if (firstCheck x) then if digitToInt(x) == number then True else False else False
myElem (x:xs) number =
if (firstCheck x) then (if digitToInt(x) == number then True else myElem xs number) else myElem xs number
where firstCheck x checks that the checked element isn't 'x' or '#'
Now in my current function i get the first element position which contains the element, however my head is stuck around on how to get the full list:
findBlock (x:xs) number arv =
if myElem x number then arv else findBlock xs number arv+1
Where arv is 0 and number is the number i'm looking for.
For example on input:
findBlock ["1234x4","253x4",2839",2845"] 5 0
The result would be 1
Any help would be appreciated.
The function you want already exists in the Data.List module, by the name of findIndices. You can simply use (elem '5') as the predicate.
http://hackage.haskell.org/package/base-4.8.1.0/docs/Data-List.html#v:findIndices
If, for some reason, you're not allowed to use the built-in one, it comes with a very pretty definition (although the one actually used has a more complicated, more efficient one):
findIndices p xs = [ i | (x,i) <- zip xs [0..], p x]
By the way, I found this function by searching Hoogle for the type [a] -> (a -> Bool) -> [Int], which (modulo parameter ordering) is obviously the type such a function must have. The best way to find out of Haskell has something is to think about the type it would need to have and search Hoogle or Hayoo for the type. Hoogle is better IMO because it does slightly fuzzy matching on the type; e.g. Hayoo wouldn't find the function here by the type I've given, because it take the arguments in the reverse order.
An implementation of findIndices, for instructional purposes:
findIndices ok list = f list 0 where
f [] _ = []
f (x:xs) ix
| ok x = ix : f xs (ix+1)
| otherwise = f xs (ix+1)
Use it like findIndices (elem '5') my_list_o_strings
You're trying to work your way through a list, keeping track of where you are in the list. The simplest function for doing this is
mapWithIndex :: (Int -> a -> b) -> [a] -> [b]
mapWithIndex = mwi 0 where
mwi i _f [] = i `seq` []
mwi i f (x:xs) = i `seq` f i x : mwi (i+1) f xs
This takes a function and a list, and applies the function to each index and element. So
mapWithIndex (\i x -> (i, x)) ['a', 'b', 'c'] =
[(0,'a'), (1,'b'),(2,'c')]
Once you've done that, you can filter the list to get just the pairs you want:
filter (elem '5' . snd)
and then map fst over it to get the list of indices.
A more integrated approach is to use foldrWithIndex.
foldrWithIndex :: (Int -> a -> b -> b) -> b -> [a] -> b
foldrWithIndex = fis 0 where
fis i _c n [] = i `seq` n
fis i c n (x:xs) = i `seq` c i x (fis (i+1) c n xs)
This lets you do everything in one step.
It turns out that you can implement foldrWithIndex using foldr pretty neatly, which makes it available for any Foldable container:
foldrWithIndex :: (Foldable f, Integral i) =>
(i -> a -> b -> b) -> b -> f a -> b
foldrWithIndex c n xs = foldr go (`seq` n) xs 0 where
go x r i = i `seq` c i x (r (i + 1))
Anyway,
findIndices p = foldrWithIndex go [] where
go i x r | p x = i : r
| otherwise = r

Haskell list: Replacing elements given their locations in the list

I'm fairly new to Haskell and trying to figure out how I would write a Function to do this and after combing Google for a few hours I'm at a loss on how to do it.
Given the following two lists in Haskell
[(500,False),(400,False),(952,True),(5,False),(42,False)]
[0,2,3]
How would I change the Boolean of the First list at each location given by the second list to a Value of True for an Output of
[(500,True),(400,False),(952,True),(5,True),(42,False)]
This is how I would do it (assumes the list of indexes to replace is sorted).
First we add an index list alongside the list of indexes to replace and the original list.
Then we recurse down the list and when we hit the next index to replace we replace the boolean and recurse on the tail of both all three lists. If this is not an index to
replace we recurse on the entire replacement index list and the tail of the other two lists.
setTrue :: [Int] -> [(a, Bool)] -> [(a, Bool)]
setTrue is xs = go is xs [0..] -- "Index" the list with a list starting at 0.
where
go [] xs _ = xs -- If we're out of indexes to replace return remaining list.
go _ [] _ = [] -- If we run out of list return the empty list.
go indexes#(i:is) (x:xs) (cur:cs)
| i == cur = (fst x, True) : go is xs cs -- At the next index to replace.
| otherwise = x : go indexes xs cs -- Otherwise, keep the current element.
This is basically the same as Andrew's approach, but it doesn't use an additional index list, and is a little bit more inspired by the traditional map. Note that unlike map, the provided function must be a -> a and cannot be a -> b.
restrictedMap :: (a -> a) -> [Int] -> [a] -> [a]
restrictedMap f is xs = go f is xs 0
where
go f [] xs _ = xs
go f _ [] _ = []
go f ind#(i:is) (x:xs) n
| i == n = f x : go f is xs (n+1)
| otherwise = x : go f ind xs (n+1)
setTrue = restrictedMap (\(x,_) -> (x, True))
Straightforward translation from the description will be:
setIndexTrue f a = [(x, p || i `elem` f) | (i, (x,p)) <- zip [0..] a]
Or using the fantastic lens library:
setTrue :: [(a,Bool)] -> Int -> [(a,Bool)]
setTrue xs i = xs & ix i . _2 .~ True
setTrues :: [(a,Bool)] -> [Int] -> [(a,Bool)]
setTrues = foldl setTrue
Since the approach I would use is not listed:
setTrue spots values = let
pattern n = replicate n False ++ [True] ++ Repeat False
toSet = foldl1 (zipWith (||)) $ map pattern spots
in zipWith (\s (v,o) -> (v, o || s)) toSet values

Resources