【问题标题】:Generate list of range tuples with given boundaries in python在python中生成具有给定边界的范围元组列表
【发布时间】:2012-12-27 02:35:33
【问题描述】:

[edit] 我不确定这是否对 codereview 更好,如果是,请迁移 :) 谢谢!

所以我们坐在这里,研究一个半学术问题。

给定一个startstopstep,生成一个范围元组列表,这样

gen_range(100, 140, 10)

会产生

[(100, 110), (110, 120), (120, 130), (130, 140)]

另外,考虑到它应该可以工作,比如说,以 100 的步长迭代 500M 整数,而不是永远。

我想出的实现如下:

def gen_range(start, stop, step):
    llist = range(start, stop+step, step)
    batch_list = []

    if llist[-1] > stop:
        llist[-1] = stop

    for a, b in enumerate(llist[:-1]):
        batch_list.append((llist[a], llist[a+1]))

    print batch_list

gen_range(100000000,600000000,100)

但我不能停止认为它可以以一种更有效(也是代码长度方面)的方式来完成。有什么建议吗?

[编辑]

有一件事我忘了指出。如果范围边界不等于步长,即您有以下情况:

gen_range(100, 143, 10)

上限应该是 143,而不是 150,因为这里的一些答案会产生,因为 range() 内部。

【问题讨论】:

  • 去掉递归?我很确定函数调用比在 for 循环中运行额外的值更昂贵。
  • 嗯?我可能很愚蠢,但我无法为此提出一个“for-loop”解决方案,请指出我忽略了什么?
  • 伪代码:for i = start, i < end, i += step do batch_list.append([i, i + step]) forend。看起来很简单,我错过了什么吗?
  • 会很酷,如果 python for 循环以这种方式工作;)
  • 你不能使用for x in xrange(start,stop,step) 构造得到那个结果吗?

标签: python list range tuples


【解决方案1】:

一条很短的路:

ranges = [(n, min(n+step, stop)) for n in xrange(start, stop, step)]

更详细的方式:

也许通过生成器?

def gen_range(start, stop, step):
    current = start
    while current < stop:
        next_current = current + step
        if next_current < stop:
            yield (current, next_current)
        else:
            yield (current, stop)
        current = next_current

调用此函数会为您提供一个生成器对象,该对象将按顺序生成每个元组。你可以这样使用它:

for block in gen_range(100000000,600000000,100):
    print block

会输出...

(100000000,100000100)
(100000100,100000200)
(100000200,100000300)
...
(599999900,600000000)

如果您始终确定 stop-startstep 的偶数倍,则可以使用生成器表达式更简单地做到这一点:

ranges = ((n, n+step) for n in xrange(start, stop, step))

# Usage
for block in ranges:
    print block

另外请注意,如果你想把一个生成器变成一个列表,将整个结果保存在内存中,你可以简单地将它传递给list()

all_ranges = list(gen_range(100000000,600000000,100))

【讨论】:

  • 再一次,在这两种情况下,边缘都从步骤偏移,所以,假设gen_range(100, 143, 10) 它将产生一个有趣的序列,其中(140, 150), (140, 143) 作为最后 2 个块。
  • @favoretti 已修复。很好的收获。
  • 你知道什么是奇怪的部分吗?在提供的 100M 到 600M 的示例(以 100 步)上,此解决方案比我的问题中的原始 kludge 慢 400ms :)
  • @favoretti 如果您要遍历整个列表并/或将其全部保存在内存中,那么生成器不会给您带来太多好处(尤其是对于元组和整数等基本 Python 类型)。跨度>
  • 但是,请看我刚刚编辑的代码长度优化版本。至少它很漂亮。
【解决方案2】:

如果(b - a) % c == 0:

def gen_range(a, b, c):
    return [(i, i + c) for i in range(a, b, c)]

def gen_range_2(a, b, c):
    s = range(a, b + c, c)
    return zip(s, s[1:])

http://ideone.com/VhY215

如果(b - a) % c != 0:

def gen_range(a, b, c):
    return [(i, i + c) for i in range(a, b - ((b - a) % c), c)]

def gen_range_2(a, b, c):
    s = range(a, b - ((b - a) % c) + c, c)
    return zip(s, s[1:])

http://ideone.com/i3Pq69

【讨论】:

  • 这个问题的一个问题是范围本身。重点是,如果将其应用于gen_range(100, 143, 10)range 将显示 150 作为上边界,其中 143 实际上是可取的。对不起,我没有在帖子中说清楚,我将其添加到问题中。
  • @favoretti 已修复,或尝试过 - 不幸的是,最终没有帮助计算字符数
【解决方案3】:

我不确定我是否正确理解了这个问题,但这可以通过列表理解来完成:

start = 100
stop = 140
step = 10
[ range(s,s+step+1,step) for s in range(start,stop,step)]

如果您一次只需要迭代一个列表元素,我强烈建议您改用生成器理解:

res = ( range(s,s+step+1,step) for s in range(start,stop,step))

你可以循环遍历它:

for range_piece in res:
    do_something with it

【讨论】:

    【解决方案4】:

    类似这样的:

    In [48]: it=iter(xrange(100,200,10))
    
    In [49]: it1=iter(xrange(110,210,10))
    
    In [50]: [(next(it),next(it1)) for _ in range(10)]
    Out[50]: 
    [(100, 110),
     (110, 120),
     (120, 130),
     (130, 140),
     (140, 150),
     (150, 160),
     (160, 170),
     (170, 180),
     (180, 190),
     (190, 200)]
    

    或使用zip():

    In [55]: it=iter(xrange(100,200,10))
    
    In [56]: it1=iter(xrange(110,210,10))
    
    In [57]: [(x,y) for x,y in zip(it,it1)]
    Out[57]: 
    [(100, 110),
     (110, 120),
     (120, 130),
     (130, 140),
     (140, 150),
     (150, 160),
     (160, 170),
     (170, 180),
     (180, 190),
     (190, 200)]
    

    【讨论】:

    • 我也尝试过基于 zip() 的方法,尽管不是在 xrange 迭代器上,但在 2 个列表上,它比提供的 enumerate() 方法慢了大约 2 倍。
    【解决方案5】:

    我会把它做成一个生成器,这样它就可以处理很大的范围。

    def gen_range(start, stop, step):
        a, b = start, start+step
        while b <= stop:
            yield a, b
            a, b = b, b+step
    
    print list(gen_range(100, 140, 10))
    

    【讨论】:

      【解决方案6】:

      我会使用生成器方法,但是如果您要将事物解压缩到显式列表中,另一种看待它的方式是您的元组元素之间的关系非常简单,而您需要做的就是适当地调整长度范围。

      def gen_range(start, stop, step):
         items = ((stop-start) // step)
         return [(start+(n*step), start+(n+1)*step) for n in range(items)]
      

      我认为值得发布这篇文章的唯一原因是,我的第一个想法(以及评论查询)是否可以在没有生成批次。如果你不能,那我就不会走这个角度。

      【讨论】:

        【解决方案7】:

        我认为生成器是一个很好的尝试:

        def gen_range(start, stop, step):
            while start < stop:
                yield (start, min(start + step, stop))
                start = start + step
        
        if __name__ == '__main__':
            print [x for x in gen_range(100, 143, 10)]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-08-17
          • 1970-01-01
          • 2020-03-01
          • 2019-08-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多