任务背景:有 n 个 txt 文件,每个文件都有 i 行,每行的内容就是对应的行号 j。现在需要按照一定的间隔(slice_size),连续切分这 n 个文件,并返回切分点对应位置的总数、切分点在文件中对应行号、从该切分点开始能读取多少数据。
三个示例文件如下,三个文件的长度分别为 12,4 和 5,总共 21 行数据。
返回:[(1, 1, 5), (6, 6, 5), (11, 11, 2), (13, 1, 4), (17, 1, 5)]
文件一:
1 | 1 1 |
文件二:
1 | 1 1 |
文件三:
1 | 1 1 |
我认为,问题处理的难点在于最后一个返回值,我们需要在遍历完文件之后才能知道切分点后又多少数据。比如第一个文件,按照每 5 行切分一次,最后一次切分点在第 11 行,而此时,指针到文件末尾时我才知道从 11 这个切分点开始只剩下了两个数据,我无法提前知道剩下多少个数据。在这个地方我纠结了很久,到底怎样才能保存好这个值呢?我能确定的是,只有当指针到达文件末尾时,最后一次切分才会结束,才会知道在该次切分中剩余多少数据,所以,在 counter_01
中,我加入了一个 if not line_pos:
的判断条件。
接下来就是计算剩下多少数据的问题了,我通过 res = idx % slice_size
得到剩余的数据,比如总数 12 的文件,以 5 来切分,12 % 5 = 2,这样就得到了最后一次需要读取的数据。但是,这种做法存在一个很严重的问题:每次切分时我都是默认切分点后面会有 slice_size
个数据,所以当指针到达 11 行时,我就提前假设后面有了 5 条数据,然而事实上并没有,需要当指针到达最后一行时来修正。为了掩饰那个错误,我用了比较暴力的办法:直接干掉列表的倒数第二个自以为正确的数据。
对于第一个返回值,切分点在三个文件累计位置,只需要在文件遍历的最外层设置 total
计数变量即可,每读完一行数据再加一。这里也有一个需要处理的坑,比如在 if not line_pos:
的判断中,切分点累计位置如果还等于 total
,那最后一个切分点的位置永远会在文件的最后一行,所以需要在 if idx % slice_size == 0:
中先把 total
赋值给 start_num
存下来给接下来的判断用。第二个返回值处理的逻辑也类似,把 line_pos
赋值给 tmp_line_pos
,留给接下来的判断语句中使用。
1 | #!/usr/bin/python |
第一个计数器勉强实现了计数的功能,但是不够漂亮,特别是处理剩余数据的逻辑上有说不出的混乱和复杂,很蹩脚。来看第二个计数器,初始参数有 total
和 slice_counter
两个,对比计数器一,计数器二的思路为每次只切一份小片数据,slice_counter
等于 1,记下 slice_start_pos
、slice_start_num
,当 slice_counter
等于 5,将上一个 slice_start_pos
、slice_start_num
取出来,此时该小片有 5 个数据。
那么问题来了,要是最后一个分片没有 5 行数据咋办?这里的处理逻辑很漂亮,当切完一个片,slice_counter
归零,当指针到达了最后一行,如果最后一个分片不足 5,那么 slice_counter
是不会归零的!while
判断跳出来后,后面加个 if slice_counter > 0:
把剩余的 slice_start_pos
、slice_start_num
、slice_counter
取出来,任务完成。
1 | def counter_02(): |
对比以上两个计数器,显然第二个更优,处理的逻辑干净、思路清晰,每个变量负责的任务很明确,特别是最后的 if slice_counter > 0:
很漂亮。第一个计数器在一开始想干的事情太多了,一开始就想着假设后面有 5 条数据,然后靠后面的暴力删除来掩饰这个错误。对比完这两个计数器,我觉得,在处理的问题的时候,拆解问题的能力很重要,一个逻辑处理一个小问题,别想着存在一个万能逻辑解决多个问题,这是我以上想到的。
继续加强代码能力。。。