DarkMatter in Cyberspace
  • Home
  • Categories
  • Tags
  • Archives

Converting an imperative algorithm into functional style


Question and Python Solution

The follow is a question I posted on stackoverflow.com and the Python imperative style solution:

I wrote a simple procedure to calculate the average of the test coverage of some specific packages in a Java project. The raw data in a huge html file is like this:

<body> 
package pkg1 <line_coverage>11/111,<branch_coverage>44/444<end> 
package pkg2 <line_coverage>22/222,<branch_coverage>55/555<end> 
package pkg3 <line_coverage>33/333,<branch_coverage>66/666<end> 
... 
</body>

Given the specified packages "pkg1" and "pkg3", for example, the average line coverage is:

(11+33)/(111+333)

and average branch coverage is:

(44+66)/(444+666)

I wrote the follow procedure to get the result and it works well. But how to implement this calculation in a functional style? Something like "(x,y) for x in ... for b in ... if...". I know a little Erlang, Haskell and Clojure, So solutions in these languages are also appreciated. Thanks a lot!

from __future__ import division 
import re 
datafile = ('abc', 'd>11/23d>34/89d', 'e>25/65e>13/25e', 'f>36/92f>19/76') 
core_pkgs = ('d', 'f') 
covered_lines, total_lines, covered_branches, total_branches = 0, 0, 0, 0 
for line in datafile: 
    for pkg in core_pkgs: 
        ptn = re.compile('.*'+pkg+'.*'+'>(\d+)/(\d+).*>(\d+)/(\d+).*') 
        match = ptn.match(line) 
        if match is not None: 
            cvln, tlln, cvbh, tlbh = match.groups() 
            covered_lines += int(cvln) 
            total_lines += int(tlln) 
            covered_branches += int(cvbh) 
            total_branches += int(tlbh) 
print 'Line coverage:', '{:.2%}'.format(covered_lines / total_lines) 
print 'Branch coverage:', '{:.2%}'.format(covered_branches/total_branches)

Functional Style Solution in Clojure

(defn extract-data 
 "extract 4 integer from a string line according to a package name" 
 [pkg line] 
 (map read-string 
  (rest (first 
    (re-seq 
    (re-pattern 
    (str pkg ".*>(\\d+)/(\\d+).*>(\\d+)/(\\d+)")) 
    line))))) 
(defn scan-lines-by-pkg 
 "scan all string lines and extract all data as integer sequences 
 according to package names" 
 [pkgs lines] 
 (filter seq (for [pkg pkgs 
     line lines] 
    (extract-data pkg line)))) 
(defn sum-data 
 "add all data in valid lines together" 
 [pkgs lines] 
 (apply map + (scan-lines-by-pkg pkgs lines))) 
(defn get-percent 
 [covered all] 
 (str (format "%.2f" (float (/ (* covered 100) all))) "%")) 
(defn get-cov 
 [pkgs lines] 
 {:line-cov (apply get-percent (take 2 (sum-data pkgs lines))) 
 :branch-cov (apply get-percent (drop 2 (sum-data pkgs lines)))}) 
(get-cov ["d" "f"] ["abc" "d>11/23d>34/89d" "e>25/65e>13/25e" "f>36/92f>19/76"]) 

clojure solutions with load file and command line args

core-pkgs=...+...+...
clojure get-cov.clj $core-pkgs
(def s (slurp "frame-summary.html"))
(use 'clojure.string)
(def lines (split s #"\n"))


Published

Sep 29, 2013

Last Updated

Sep 29, 2013

Category

Tech

Tags

  • functional programming 9
  • Python 136

Contact

  • Powered by Pelican. Theme: Elegant by Talha Mansoor