Haskell map until first condition met
break specifically separates the list in 2 parts where the first part is all False
, the opposite of span.
break (>5) [1,2,3,8,2,5,1,7,9]
>>> ([1,2,3],[8,2,5,1,7,9])
Then it's just what chi did:
oneTrue f lst = map (const False) a ++ rest b
where (a,b) = break f lst
rest [] = []
rest (x:xs) = True : map (const False) xs
A basic solution:
mapUntil p = onlyOne . map p
where
onlyOne [] = []
onlyOne (x:xs)
| x = True : map (const False) xs
| otherwise = False : onlyOne xs
With library helpers:
mapUntil p = snd . mapAccumL (\x y -> (x||y, not x && y)) False . map p
Above x
is a boolean standing for "have seen a true before?", as a kind-of state. y
is the list element. x||y
is the new state, while not x && y
is the new list element.
Alternatively (using Control.Arrow.second
):
mapUntil p = uncurry (++) . second go . break id . map p
where
go [] = []
go (x:xs) = x : map (const False) xs
I would use the mapAccumL
tool like;
λ> Data.List.mapAccumL (\b n -> if b then (b, (not b)) else (n > 5, n > 5)) False [1,2,3,4,5,6,7,8,9]
(True,[False,False,False,False,False,True,False,False,False])
Here we carry the b
as the state of our interim calculations and in every step decide according to it's previous state. Obviously you need the snd
part of the final result.
Edit : After reading the new comment of @Gord under his question I decided to extend my answer to cover his true problem.
Rephrasing the case event of
branch that starts with pointerPress (x,y)
into...
To start with, you never use x
or y
from the pattern match (x,y)
so lets call it c
. Then...
PointerPress c -> State circleCoords circleColors circleDraggeds c
where
bools = fmap checkMouseOverlaps $ (,) <$> circleCoords <*> [c]
circleDraggeds = snd $ mapAccumL (\a b -> if a then (a, not a)
else (b,b)) False bools
What's happening part;
(,) <$> circleCoords <*> [c]
circleCoords
is a list of coordinates like [c0,c1,c2]
and we fmap
(the infix version (<$>)
here) (,)
function to it and it becomes an applicative of coordinates like [(c0,),(c1,),(c2,)]
. Then we apply it to [c]
aka [(x,y)]
to turn it into [(c0,c),(c1,c),(c2,c)]
.
fmap checkMouseOverlaps $ toAbove
obviously yields to
[checkMouseOverlaps (c0,c), checkMouseOverlaps (c1,c), checkMouseOverlaps (c2,c)]
which is bools :: [Bool]
.
The the rest follows the logic explained at the top of my answer.
circleDraggeds = snd $ mapAccumL (\a b -> if a then (a, not a)
else (b,b)) False bools