11
\$\begingroup\$

I have a xxs list:

xxs:: [(([Char], [Char]),([Char], [Char]),[(Double, [Char], [Char])])]
xxs = [(("a11","b11"),("a12","b12"), [(0,"a1","b1"),(1.2,"a2","b2"),(2.3,"a3","b3"),(4.2,"a4","b4")]), (("c11","d11"),("a12","b12"),[(1,"a5","b5"),(1.4,"a6","b6"),(1.9,"a7","b7"),(4.4,"a8","b8")])]

And I need something like:

foo 1            -- 1 is the index
output: [(0.0,0.0,"a1","b1"),(1.2,1.2,"a2","b2"),(2.3,3.5,"a3","b3"),(4.2,7.7,"a4","b4")]

or

foo 2            -- 2 is the index
output: [(1.0,1.0,"a5","b5"),(1.4,2.4,"a6","b6"),(1.9,4.3,"a6","b6"),(4.4,8.7,"a6","b6")]

Where the second element of each tuple is the summation of all the Doubles from the xxs inner list.

I solve it this way:

auxA :: Int -> [(Double, [Char], [Char])]
auxA indexP = aux1 xxs!!(indexP-1)
where aux1 [] = []
      aux1 ((_,_,c):xs) = c:aux1 xs

auxB :: Int -> [Double]
auxB indexP = aux2 (auxA indexP)
where aux2 [] = []
      aux2 ((x,_,_):xs) = x:aux2 xs

integer1 :: Int -> [Double]
integer1 indexP = [ i | (i,_,_) <- auxA indexP]

intSummation :: Int -> [Double]
intSummation indexP = scanl (+) (head (auxB indexP)) (drop 1 (auxB indexP))

string1 :: Int -> [[Char]]
string1 indexP = [ st1 | (_,st1,_) <- auxA indexP]

string2 :: Int -> [[Char]]
string2 indexP = [ st2 | (_,_,st2) <- auxA indexP]


foo :: Int -> [(Double, Double, [Char], [Char])]
foo indexP = zip4 (integer1 indexP) (intSummation indexP) (string1 indexP) (string2 indexP)

--or

fob :: Int -> [(Double, Double, [Char], [Char])]
fob indexP = [(i,intSum,st1,st2) | (((i,intSum),st1),st2) <- zip (zip(zip (integer1 indexP) (intSummation indexP)) (string1 indexP)) (string2 indexP)]

For fob, I've found a similar code here: https://stackoverflow.com/questions/2468226/how-to-zip-multiple-lists-in-haskell

Finally, one question: any suggestion to solve this problem with a different approach/code?

Thanks.

edit: type signatures added.

\$\endgroup\$
  • \$\begingroup\$ @ehird For me, this is not exactly a "Code Review"... is more like: I'm a beginner in Haskell, I solved my homework, but I'm curious about other solutions/approaches to solve it. But maybe your right. \$\endgroup\$ – Nomics Dec 19 '11 at 1:01
7
\$\begingroup\$

Wow! What an ugly data structure. I suggest making small transformations until you arrive at the form you desire; this is in contrast to your current solution that extracts the exact element you desire (performing all needed computations) without building a new representation.

1) Eliminate unused fields

map (\(_,_,lst) -> lst)  -- This is like 'auxA' with the lookup

This gets rid of the first two elements of the tuple, which your transformation didn't seem to use.

The first line simply drops your first two tuples, so instead of:

xxs:: [(([Char], [Char]),([Char], [Char]),[(Double, [Char], [Char])])]

Which didn't seem needed for your computations (you didn't really explain what you want in words, so it's possible I missed the point by skipping your code). Now you have:

xxs:: [[(Double, [Char], [Char])]]

2) Compute the running sum

-- This is similar to your interger1, intSummation, string1, and string2 all rolled in one
drop 1 . scanl (\(_,s,_,_) (n,x,y) -> (n,n+s,x,y)) (0,0,"","")

The "scan" just transforms a list using a binomial and a starting element. In this case, you see the n+s is the second element while the original value, n, is the first element of the output tuples. I call drop 1 to get rid of my initial 4-tuple that starts the scan.

The final code looks like:

import Data.List

tmd i =
  let xxs' = map (\(_,_,lst) -> lst) xxs -- This is like 'auxA' with the lookup
      xxsFinal = map (drop 1 . scanl (\(_,s,_,_) (n,x,y) -> (n,n+s,x,y)) (undefined,0,undefined,undefined)) $ xxs'
  in lookup i (zip [1..] xxsFinal)

With correct results (afaict):

> Just (foo 2) == tmd 2
True
> Just (foo 1) == tmd 1
True

If #2 is too much in one step

  1. Extract the values: let vals = map (\(n,_,_) -> n) input
  2. Compute the sums: let sums = drop 1 . scanl (+) 0 $ vals
  3. Combine the info: zipWith3 (\v s (_,a,b) -> (v,s,a,b)) vals sums input

Comparing to your code (learning to use standard functions)

Mostly, you seem to be of a mind to looking data on demand when it's easier and cleaner to build a list of all results and pull out the desired values. Some specific observations:

  1. Your aux1 and aux2 functions are just maps, for example aux1 = map (\(_,_,c) -> c).
  2. !! is a partial function. I suggest you use lookup index . zip [1..].
  3. Most of your list comprehensions are easier to read as a map function and simple lambda. They really don't deserve to be separate functions.
  4. Way to go, using scanl as a beginner - nice work. However, instead of using 'head' and 'drop' perhaps you could just use your own zero element (like I did with 0 and "" - or I could have used undefined) or use scanl1 (+) . auxB.

P.S. Good work coming in with a complete solution!

\$\endgroup\$
  • 1
    \$\begingroup\$ Excellent post. It sure did help me to understand some functions usage. And, of course, thanks for you advises. For sure that I’ll try to follow yours advises in the future! I really learn a nice couple of things with your answer. \$\endgroup\$ – Nomics Dec 19 '11 at 2:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.