Reputation: 3076
I am looking for a haskell function or pattern that is a sort of "foldWhile" except instead of folding over a list it uses a functions output..some code will probably explain it better.
Simplified and pseudo:
nums :: [Integer]
nums = [1]
cond :: v -> [Integer] -> Bool
cond v ls = elem v ls
func :: x -> ls -> [Integer]
func x ls = x `some_op` ls
And I need a pattern of application like:
(cond 1 num) && func x num -> num'
(cond 1 num') && func x num' -> num''
(cond 1 num'') && func x num'' -> num'''
...
Once cond returns False, yield the last num.
Any help would be appreciated, thanks in advance.
Upvotes: 4
Views: 561
Reputation: 54068
I think you want a combination of iterate
and takeWhile
:
iterate :: (a -> a) -> a -> [a]
takeWhile (a -> Bool) -> [a] -> [a]
iterateWhile :: (a -> a) -> (a -> Bool) -> a -> [a]
iterateWhile func cond = takeWhile cond . iterate func
And in your case you'd want to use it as
lastWhere :: (a -> a) -> (a -> Bool) -> a -> a
lastWhere func cond = last . iterateWhile func cond
main = do
let x = lastWhere (+1) (<10) 1
print x
-- prints "9"
You can probably do this with a fold, but why bother when you have this solution already? If evaluating the condition is separate from generating the values, then separate those two concerns, rather than trying to tie them together. This is what iterateWhile
does. Since it's lazily evaluated, this only generates values until it finds one that doesn't meet the condition, and it only needs a single loop to do so.
Since iterateWhile
produces a list of elements all satisfying that condition, you can then simply take the last element. If you need the first element that fails, I would do
firstWhere :: (a -> a) -> (a -> Bool) -> a -> a
firstWhere func cond = head . dropWhile cond . iterate func
Upvotes: 6