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

Tags:

Haskell