I am trying to fill a tuple consisting of two lists in my function and depending on a condition I want to each fill a different list in this tuple. Code goes as follows:
type One = []
type Two = []
bar :: Int -> Bool
bar ...
foo :: Somelist -> (One, Two)
foo somelist
| null somelist = ([],[])
| bar yesno = {-I want to fill the list "One" here if bar == true-} foo (tail somelist)
| otherwise = {-and the list "Two" here if bar == false-} foo (tail somelist)
What is an elegant and haskelly way to approach this problem :) ?
Thank you for your suggestions.
CodePudding user response:
I have changed the types a little compared to your example to make the code testable.
type One = [Integer]
type Two = [Integer]
bar :: Integer -> Bool
bar x = x > 0
foo :: [Integer] -> (One, Two)
foo [] = ([], [])
foo (x:xs)
| bar x = (x:one, two)
| otherwise = (one, x:two) where
(one, two) = foo xs
A quick test:
λ> foo [-10..10]
([1,2,3,4,5,6,7,8,9,10],[-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0])
The idea is to recurse down up to the end of the list and return a tuple of lists, then while you come up the recursive stack, grab the returning tuple from the function below and add the value of the current element to the list based on your predicate.
Or you can use partition
from Data.List
with the type partition :: (a -> Bool) -> [a] -> ([a], [a])
. This function takes a list and a predicate and returns a tuple of 2 lists: one where the predicate returned true and the other where it returned false.
import Data.List (partition)
foo2 :: [Integer] -> (One, Two)
foo2 xs = partition bar xs
You can eta reduce the above, but I haven't done that for clarity.