gpt4 book ai didi

python - 将命令式算法转换为函数式风格

转载 作者:行者123 更新时间:2023-11-28 22:53:45 24 4
gpt4 key购买 nike

我编写了一个简单的程序来计算 Java 项目中某些特定包的平均测试覆盖率。一个巨大的html文件中的原始数据是这样的:

<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>

例如,给定指定的包“pkg1”和“pkg3”,平均行覆盖为:

(11+33)/(111+333)

平均分支覆盖率是:

(44+66)/(444+666)

我编写了以下程序来获得结果并且运行良好。但是如何以函数式的方式实现这个计算呢?类似于“(x,y) for x in ... for b in ... if ...”。我知道一点 Erlang、Haskell 和 Clojure,所以也很欣赏这些语言的解决方案。多谢!

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)

最佳答案

在下方您可以找到我的 Haskell 解决方案。我将尝试解释我在撰写本文时经历的要点。

  1. 首先您会发现我为覆盖率数据创建了一个数据结构。创建数据结构来表示您要处理的任何数据通常是个好主意。这部分是因为当你可以从你正在设计的任何东西的角度思考时,它会更容易设计你的代码——与函数式编程哲学密切相关,部分是因为它可以消除一些你认为你正在做某事但实际上没有做的错误。实际上在做其他事情。

  2. 与之前的观点相关:我做的第一件事是将字符串表示的数据转换为我自己的数据结构。当您进行函数式编程时,您通常是在“扫描”中做事。您没有一个函数可以将数据转换为您的格式、过滤掉不需要的数据汇总结果。对于这些任务中的每一项,您都具有三个不同的函数,并且一次执行一个!

    这是因为函数非常可组合,即如果您有三个不同的函数,您可以根据需要将它们组合在一起形成一个函数。如果你从一个开始,很难把它拆成三个不同的。

    转换函数的实际工作实际上是非常无趣的,除非你专门在做 Haskell。它所做的只是尝试将每个字符串与正则表达式匹配,如果成功,它会将覆盖率数据添加到结果列表中。

  3. 再一次,疯狂的作文即将发生。我不会创建一个函数来遍历覆盖范围列表并将它们相加。我创建了一个函数来对两个覆盖范围求和,因为我知道我可以将它与专门的fold循环(有点像for 类固醇循环)来总结列表中的所有覆盖范围。我无需重新发明轮子并自己创建循环。

    此外,我的 sumCoverages 函数与许多专门的循环一起工作,所以我不必编写大量函数,我只需将我的单个函数粘贴到大量预制库中函数!

  4. main 函数中,您将明白我所说的编程“扫描”或“传递”数据的意思。首先我将它转换为内部格式,然后我过滤掉不需要的数据,然后我总结剩余的数据。这些是完全独立的计算。这就是函数式编程。

    您还会注意到我在那里使用了两个专门的循环,filterfold。这意味着我不必自己编写任何循环,我只需将一个函数添加到那些标准库循环中,然后让它们从那里获取它。


import Data.Maybe (catMaybes)
import Data.List (foldl')
import Text.Printf (printf)
import Text.Regex (matchRegex, mkRegex)

corePkgs = ["d", "f"]

stats = [
"d>11/23d>34/89d",
"e>25/65e>13/25e",
"f>36/92f>19/76"
]

format = mkRegex ".*(\\w+).*>([0-9]+)/([0-9]+).*>([0-9]+)/([0-9]+).*"


-- It might be a good idea to define a datatype for coverage data.
-- A bit of coverage data is defined as the name of the package it
-- came from, the lines covered, the total amount of lines, the
-- branches covered and the total amount of branches.
data Coverage = Coverage String Int Int Int Int


-- Then we need a way to convert the string data into a list of
-- coverage data. We do this by regex. We try to match on each
-- string in the list, and then we choose to keep only the successful
-- matches. Returned is a list of coverage data that was represented
-- by the strings.
convert :: [String] -> [Coverage]
convert = catMaybes . map match
where match line = do
[name, cl, tl, cb, tb] <- matchRegex format line
return $ Coverage name (read cl) (read tl) (read cb) (read tb)


-- We need a way to summarise two coverage data bits. This can of course also
-- be used to summarise entire lists of coverage data, by folding over it.
sumCoverage (Coverage nameA clA tlA cbA tbA) (Coverage nameB clB tlB cbB tbB) =
Coverage (nameA ++ nameB ++ ",") (clA + clB) (tlA + tlB) (cbA + cbB) (tbA + tbB)


main = do
-- First we need to convert the strings to coverage data
let coverageData = convert stats
-- Then we want to filter out only the relevant data
relevantData = filter (\(Coverage name _ _ _ _) -> name `elem` corePkgs) coverageData
-- Then we need to summarise it, but we are only interested in the numbers
Coverage _ cl tl cb tb = foldl' sumCoverage (Coverage "" 0 0 0 0) relevantData

-- So we can finally print them!
printf "Line coverage: %.2f\n" (fromIntegral cl / fromIntegral tl :: Double)
printf "Branch coverage: %.2f\n" (fromIntegral cb / fromIntegral tb :: Double)

关于python - 将命令式算法转换为函数式风格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19076297/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com