Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encapsulating a list of functions in haskell in a single one

Someone said that I might not be "getting" how to proper code in Haskell. That someone must be totally right, as I feel all my haskell code but the simpler functions is really ugly (at least compared to my OOP code in a "standard" language such as Java or C++):

mev = matrixExpValues 5 4 3
cs = canonicalSt 4 3

cs_t1 = map (foldl (++) "") (map (map show) cs)
cs_t2 = map (++ ":") cs_t1
mev_t1 = intXxsToStringXxs mev
mev_t2 = map (map (++ "\t")) mev_t1
mev_t3 = map (foldl (++) "") mev_t2
res1 = zipWith (++) (map (++ "\t") cs_t2) mev_t3
res2 = map (++ "\n") res1
final_result = foldl (++) "" res2

with mev and cs of:

*Main> mev
[[2,-2,-2,-6],[4,2,0,-2],[2,2,4,4],[6,4,2,2],[6,4,2,6]]
*Main> cs
[[0,0,4],[0,1,3],[0,2,2],[1,1,2]]

(those values were hand typed, I will need this to work for arbitrary mev and cs!) I initially have a 2D matrix to which I applied a sequence of operations until I got the desired result.

This works, but now I'd like to encapsulate all this logic in a single function (let's call it matrix_transf). The current code is tied to what matrixExpValues and canonicalSt return, and I'd like to have something like

matrix_transf mev cs = 
    ...all those transformations
    ...until I get to final_result

All kind of criticism is welcome (I need it so I can improve!) I believe good Haskell coders will probably approach this in a total different way and that is what I am looking for to know!

like image 752
devoured elysium Avatar asked Dec 03 '10 09:12

devoured elysium


2 Answers

  1. Know the library. For example, foldl (++) "" x can be replaced by concat, the ++ "\t" stuff etc. can be done by Data.List.intercalate, etc.

  2. You could use where and let to define local 'variables'.

Assume you want to convert the 2 lists into the form

004:    2    -2   -2   -6
013:    4    2    0    -2
...

then I would write

import Data.List

matrix_transf mev cs = 
   unlines $ zipWith processEntry mev cs
   where processEntry mev_entry cs_entry = 
            concatMap show cs_entry ++ ":\t" ++ 
            intercalate "\t" (map show mev_entry)
*Main> putStrLn $ matrix_transf [[2,-2,-2,-6],[4,2,0,-2],[2,2,4,4],[6,4,2,2],[6,4,2,6]] [[0,0,4],[0,1,3],[0,2,2],[1,1,2]]
004:    2   -2  -2  -6
013:    4   2   0   -2
022:    2   2   4   4
112:    6   4   2   2

(Note that this is different from your function where the trailing tabs do not exist.)

like image 189
kennytm Avatar answered Nov 06 '22 07:11

kennytm


First I would like to say that what I am going to show you isn't optimal (for instance KennyTM's code looks a whole lot better.) But I would like to show you how your code would look if you change intXxsToStringXxs to map (map show) and continuously apply the rule :

  • map f (map g xs) ==> map (f.g) xs

while inlining definitions when possible. Also, to make it look better, I have applied these rules:

  • foldl (++) "" ==> concat
  • concat (map f xs) ==> concatMap f xs
  • concatMap (++ "\n") ==> unlines

Afer quite a lof of rewriting, it will give you this:

cs_t3  = map ((++ ":\t") . concatMap show) cs 
mev_t3 = map (concatMap ((++"\t") . show)) mev 
final_result = unlines (zipWith (++) cs_t3 mev_t3) 

I know it doesn't look much better, but it shouldn't take you too long now to figure out that you can write matrix_transf like this:

matrix_transf mev cs = unlines (zipWith (++) (starts cs) (endings mev))

starts  = map ((++ ":\t") . (concatMap show)) 
endings = map (concatMap ((++"\t") . show))     

or even like this:

matrix_transf mev cs = unlines . zipWith (++) starts $ endings
    where starts  = map ((++ ":\t") . (concatMap show)) cs 
          endings = map (concatMap ((++"\t") . show)) mev 
like image 29
HaskellElephant Avatar answered Nov 06 '22 07:11

HaskellElephant