Chinaunix首页 | 论坛 | 博客
  • 博客访问: 101596
  • 博文数量: 18
  • 博客积分: 681
  • 博客等级: 中士
  • 技术积分: 295
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-17 13:33
文章分类
文章存档

2012年(8)

2011年(10)

分类: Python/Ruby

2012-09-23 13:35:06

Code kata为提高编程能力的一系列练习,是关于数据抽取的,其给出了两个编程题目,要求分别实现,然后把共同部分抽象出来,重新实现这两个程序。

题目一: weather.dat为某个地方6月的温度数据,要求输出温差最大的天,Dy为Day,MxT为该日最大温度,MnT为该日最小温度。

weather.dat

(Unofficial, Preliminary Data). Source:

href="">

 MMU June 2002

  Dy MxT   MnT   AvT   HDDay  AvDP 1HrP TPcpn WxType PDir AvSp Dir MxS SkyC MxR MnR AvSLP

   1  88    59    74          53.8       0.00 F       280  9.6 270  17  1.6  93 23 1004.5

   2  79    63    71          46.5       0.00         330  8.7 340  23  3.3  70 28 1004.5

   3  77    55    66          39.6       0.00         350  5.0 350   9  2.8  59 24 1016.8

   4  77    59    68          51.1       0.00         110  9.1 130  12  8.6  62 40 1021.1

   5  90    66    78          68.3       0.00 TFH     220  8.3 260  12  6.9  84 55 1014.4

   6  81    61    71          63.7       0.00 RFH     030  6.2 030  13  9.7  93 60 1012.7

   7  73    57    65          53.0       0.00 RF      050  9.5 050  17  5.3  90 48 1021.8

   8  75    54    65          50.0       0.00 FH      160  4.2 150  10  2.6  93 41 1026.3

   9  86    32*   59       6  61.5       0.00         240  7.6 220  12  6.0  78 46 1018.6

  10  84    64    74          57.5       0.00 F       210  6.6 050   9  3.4  84 40 1019.0

  11  91    59    75          66.3       0.00 H       250  7.1 230  12  2.5  93 45 1012.6

  12  88    73    81          68.7       0.00 RTH     250  8.1 270  21  7.9  94 51 1007.0

  13  70    59    65          55.0       0.00 H       150  3.0 150   8 10.0  83 59 1012.6

  14  61    59    60       5  55.9       0.00 RF      060  6.7 080   9 10.0  93 87 1008.6

  15  64    55    60       5  54.9       0.00 F       040  4.3 200   7  9.6  96 70 1006.1

  16  79    59    69          56.7       0.00 F       250  7.6 240  21  7.8  87 44 1007.0

  17  81    57    69          51.7       0.00 T       260  9.1 270  29* 5.2  90 34 1012.5

  18  82    52    67          52.6       0.00         230  4.0 190  12  5.0  93 34 1021.3

  19  81    61    71          58.9       0.00 H       250  5.2 230  12  5.3  87 44 1028.5

  20  84    57    71          58.9       0.00 FH      150  6.3 160  13  3.6  90 43 1032.5

  21  86    59    73          57.7       0.00 F       240  6.1 250  12  1.0  87 35 1030.7

  22  90    64    77          61.1       0.00 H       250  6.4 230   9  0.2  78 38 1026.4

  23  90    68    79          63.1       0.00 H       240  8.3 230  12  0.2  68 42 1021.3

  24  90    77    84          67.5       0.00 H       350  8.5 010  14  6.9  74 48 1018.2

  25  90    72    81          61.3       0.00         190  4.9 230   9  5.6  81 29 1019.6

  26  97*   64    81          70.4       0.00 H       050  5.1 200  12  4.0 107 45 1014.9

  27  91    72    82          69.7       0.00 RTH     250 12.1 230  17  7.1  90 47 1009.0

  28  84    68    76          65.6       0.00 RTFH    280  7.6 340  16  7.0 100 51 1011.0

  29  88    66    77          59.7       0.00         040  5.4 020   9  5.3  84 33 1020.6

  30  90    45    68          63.6       0.00 H       240  6.0 220  17  4.8 200 41 1022.7

  mo  82.9  60.5  71.7    16  58.8       0.00              6.9          5.3

题目二:football.dat为某个联赛各个队伍的比赛成绩,要求输出第二列与第三列差异最大的队伍。

football.dat

Source

href="">sunsite.tut.fi/rec/riku/soccer_data/tab/93_94/table.eng0.01_02.html

       Team            P     W    L   D    F      A     Pts

    1. Arsenal         38    26   9   3    79  -  36    87

    2. Liverpool       38    24   8   6    67  -  30    80

    3. Manchester_U    38    24   5   9    87  -  45    77

    4. Newcastle       38    21   8   9    74  -  52    71

    5. Leeds           38    18  12   8    53  -  37    66

    6. Chelsea         38    17  13   8    66  -  38    64

    7. West_Ham        38    15   8  15    48  -  57    53

    8. Aston_Villa     38    12  14  12    46  -  47    50

    9. Tottenham       38    14   8  16    49  -  53    50

   10. Blackburn       38    12  10  16    55  -  51    46

   11. Southampton     38    12   9  17    46  -  54    45

   12. Middlesbrough   38    12   9  17    35  -  47    45

   13. Fulham          38    10  14  14    36  -  44    44

   14. Charlton        38    10  14  14    38  -  49    44

   15. Everton         38    11  10  17    45  -  57    43

   16. Bolton          38     9  13  16    44  -  62    40

   17. Sunderland      38    10  10  18    29  -  51    40

   -------------------------------------------------------

   18. Ipswich         38     9   9  20    41  -  64    36

   19. Derby           38     8   6  24    33  -  63    30

   20. Leicester       38     5  13  20    30  -  64    28

问题一的思路:

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) ->
   DayStr = string:sub_word(Line, 1),
   case is_int_string(DayStr) of
       false ->
           nil;
       true ->
           Day = list_to_integer(DayStr),
           MxT = to_temp(string:sub_word(Line, 2)),
           MnT = to_temp(string:sub_word(Line, 3)),
           {Day, MxT, MnT}
   end.

第二版:由于需要通用公共部分,发现判断该行是否是数据行是个通用的功能,不仅仅在题目一中需要用到,在题目二中也需要用到,因此把其抽象出来。

IsDataLine = fun(Line) -> 
       file_common:is_int_string(string:sub_word(Line, 1))
end,
LineParser = fun(Line) ->
       DayStr = string:sub_word(Line, 1),
       Day = list_to_integer(DayStr),
       MxT = to_temp(string:sub_word(Line, 2)),
       MnT = to_temp(string:sub_word(Line, 3)),
       {Day, MxT, MnT}
end,

本文章的几个程序放在

阅读(2218) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~