分类: Python/Ruby
2012-09-23 13:35:06
Code kata为提高编程能力的一系列练习,是关于数据抽取的,其给出了两个编程题目,要求分别实现,然后把共同部分抽象出来,重新实现这两个程序。
题目一: weather.dat为某个地方6月的温度数据,要求输出温差最大的天,Dy为Day,MxT为该日最大温度,MnT为该日最小温度。
weather.dat
题目二:football.dat为某个联赛各个队伍的比赛成绩,要求输出第二列与第三列差异最大的队伍。
football.dat
href="">sunsite.tut.fi/rec/riku/soccer_data/tab/93_94/table.eng0.01_02.html
|
问题一的思路:
1. 遍历文件,如果该行的第一个单词为天数(数字),那么取得天数、最大温度、最小温度并放到返回列表中;
2. 返回列表排序,最大温度-最小温度最大的元素放在最前面;
3. 取得列表头的第一个元素;
问题二的思路:
1. 遍历该文件,如果该行的第一个单词为数字+点,那么取得队伍、P分数、W分数,并放入到返回列表中;
2. 对返回列表进行排序,P分数-W分数绝对值最大的放在最前面;
3. 取得列表头的第一个元素。
显然,两者的共同点在于整体处理流程的相似,而在每个流程中处理细节又能不同,如两者都需要遍历文件,抽取所需数据列,并排序得到最终结果,而它们的排序方法是不同的。
最终得到的通用部分如下:
-module(file_common). -export([process/4, process_stream/4, is_int_string/1]). process(FileName, SortPred, IsDataLine, LineParser) -> {ok, Stream} = file:open(FileName, read), Line = io:get_line(Stream, ''), ResultList = process_stream(Stream, Line, IsDataLine, LineParser), hd(lists:sort(SortPred, ResultList)). process_stream(_Stream, eof, _IsDataLine, _LineParser)-> []; process_stream(Stream, Line, IsDataLine, LineParser) -> NextLine = io:get_line(Stream, ''), case IsDataLine(Line) of false -> process_stream(Stream, NextLine, IsDataLine, LineParser); true -> Result = LineParser(Line), [Result | process_stream(Stream, NextLine, IsDataLine, LineParser)] end. is_int_string(Str) -> try list_to_integer(Str) of _Int -> true catch error:_ -> false end. |
这里非常有趣的是,抽取得到的通用部分分成两种情况,一是整个处理流程,如上面的process,二是一些通用的辅助小函数,如上面的is_int_string,其用来判断某个字符串是否为数字字符串。对于第二种抽象,是非常容易的,这里就不详细说明了。
对于整体处理流程的抽象,必须先定义出解决该问题的通用结构,并通过回调函数如SortPred,IsDataLine,LineParser来实现不通用部分。这意味着,必须先定义出问题的通用处理流程,每个步骤的输入、输出。
在这里,通用处理流程抽象如下:
1. 遍历文件,生成结果列表; 1.1 遍历文件的每一行; 1.2 如果该行是数据行,那么解析该行得到结果tuple,并加入到返回列表中,处理下一行; 1.3 如果改行不是数据行,那么接着处理; 2. 对结果列表进行排序; 2.1 使用排序谓词对列表进行排序; 3. 返回排序后列表的第一个元素; |
在没有对通用部分进行抽象的时候,并没有判断该行是否为数据行的概念,而是把其当做行解析的一部分,在对通用部分进行抽象过后,得到:把判断该行是否未数据行当做单独的函数,而不是耦合在解析函数内。从这个角度上来说,把程序泛化能够改进程序的结构。下面为处理weather.dat第一版的程序和第二版即抽取通用部分后的程序,如下
第一版:解析行的函数包括了判断该行是否为数据行 processLine(Line) -> |
第二版:由于需要通用公共部分,发现判断该行是否是数据行是个通用的功能,不仅仅在题目一中需要用到,在题目二中也需要用到,因此把其抽象出来。 IsDataLine = fun(Line) -> |
本文章的几个程序放在。