Reputation: 163
Let's imagine a population (of humans). Each human has things, that have length (left arm, right arm, left leg, right leg). Each human has things, that may have width (chest, waist). There are much more parameters that a human may have.
All measurement types are
allMeasurementTypes = [Width, Length, Diameter, Weight] -- and similar things.
For statistics I want to get for all population all possible pairs of measurements.
Something like that:
Pair1_LeftLeg Pair1_Waist Pair2_LeftLeg Pair2_Chest Pair3_RightLeg Pair3_Waist...
102 70 102 90 101 70 ...
115 60 115 85 116 60 ...
... ... ... ... ... ... ...
Don't tell me, that there are duplications here. I know. But it is easier for me to have it like that to process further in statistical applications.
So I construct this in the following way
allMeasurements :: [Human] -> Seq (Int, [(String, [Double])])
allMeasurements population = mapWithIndex (,) measures
where
measures = do
mTypes <- fromList $ choose allMeasurementTypes 2
rec1 <- recordsForPeople population $ head mTypes
rec2 <- recordsForPeople population $ mTypes !! 1
return [rec1, rec2]
And now I explain it line by line.
allMeasurements :: [Human] -> Seq (Int, [(String, [Double])])
Here Int
is the number of the pair. If it is Pair1
, then String
would be "LeftLeg"
and then "Waist"
. [Double]
is the array of values of each measurement on all population.
allMeasurements population = mapWithIndex (,) measures
where
measures = do
mTypes <- fromList $ choose allMeasurementTypes 2
This iterates through all possible pairs of measurement types. choose
gets all possible pairs like [Width, Length]
, [Width, Diameter]
...
rec1 <- recordsForPeople population $ head mTypes
Assuming, that first mType is Length
, rec1
iterates through all possible lengths. rec1
iterates through ("LeftLeg", [102, 115, ...])
, ("RightLeg", [101, 116, ...])
...
rec2 <- recordsForPeople population $ mTypes !! 1
Assuming, that second mType is Width
, rec2
iterates through all possible widths. rec2
iterates through ("Waist", [70, 60, ...])
, ("Chest", [90, 85, ...])
...
return [rec1, rec2]
And finally I collect together all everything. It gives full Seq
of all possible pairs of individual measurements for all population. mapWithIndex
above just runs through this full list and injects actual Int
number of pair inside. I chose Seq
actually only because it has this mapWithIndex
. Then it all goes out for rendering to csv
or json
.
Maybe I might do it better and smarter. Uff.
Here comes the real question.
Besides table of pairs, I also want to generate table of trinities.
allTriMeasurements :: [Human] -> Seq (Int, [(String, [Double])])
allTriMeasurements population = mapWithIndex (,) measures
where
measures = do
mTypes <- fromList $ choose allMeasurementTypes 3
rec1 <- recordsForPeople population $ head mTypes
rec2 <- recordsForPeople population $ mTypes !! 1
rec3 <- recordsForPeople population $ mTypes !! 2
return [rec1, rec2, rec3]
Is it anyhow possible to generalize these two functions allMeasurements
and allTriMeasurements
by parameter n
, defining if it is 2 for pairs or 3 for trinities.
I already dropped the ideas about fmap
and similar, because here for (monadic) list comprehension all rec_i <- ...
should go one after another. And any kind of doSomething <$> [0 .. (n-1)]
breaks the structure.
It is pretty possible, that I did it all wrong. If anyone besides me is able to understand, what is going on here, I would greatly appreciate any ideas. As it seems to me the ugliest place in my code.
Upvotes: 1
Views: 94
Reputation: 163
allMeasurements :: Int -> [Human] -> Seq (Int, [(String, [Double])])
allMeasurements k population = mapWithIndex (,) measures
where
measures = mapM (recordsForPeople population) =<< fromList (choose allMeasurementTypes k)
Soo easy. I just overlooked mapM
superpower.
Upvotes: 1