且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

将数字列表转换为字符串范围

更新时间:2023-02-03 08:02:46

一种方法可能是逐个吃"输入序列并存储部分范围的结果,直到获得全部结果为止:

One approach could be "eating" piece by piece the input sequence and store the partial range results untill you've got them all:

def formatter(start, end, step):
    return '{}-{}:{}'.format(start, end, step)
    # return '{}-{}:{}'.format(start, end + step, step)

def helper(lst):
    if len(lst) == 1:
        return str(lst[0]), []
    if len(lst) == 2:
        return ','.join(map(str,lst)), []

    step = lst[1] - lst[0]
    for i,x,y in zip(itertools.count(1), lst[1:], lst[2:]):
        if y-x != step:
            if i > 1:
                return formatter(lst[0], lst[i], step), lst[i+1:]
            else:
                return str(lst[0]), lst[1:]
    return formatter(lst[0], lst[-1], step), []

def re_range(lst):
    result = []
    while lst:
        partial,lst = helper(lst)
        result.append(partial)
    return ','.join(result)

我用一堆单元测试对其进行了测试,并且全部通过了测试,它也可以处理负数,但是它们看起来很难看(这实际上是任何人的错).

I test it with a bunch of unit tests and it passed them all, it can handle negative numbers too, but they'll look kind of ugly (it's really anybody's fault).

示例:

>>> re_range([1,  4,5,6, 10, 15,16,17,18, 22, 25,26,27,28])
'1,4-6:1,10,15-18:1,22,25-28:1'
>>> re_range([1, 3, 5, 7, 8, 9, 10, 11, 13, 15, 17])
'1-7:2,8-11:1,13-17:2'

注意::我为Python 3编写了代码.

Note: I wrote the code for Python 3.

在上述解决方案中,我没有付出任何性能上的努力.特别是,每次使用切片重新构建列表时,如果输入列表具有特定形状,则可能要花费一些时间.因此,第一个简单的改进就是使用 itertools.islice() 可能.

I didn't put any performance effort in the solution above. In particular, every time a list get re-builded with slicing, it might take some time if the input list has a particular shape. So, the first simple improvement would be using itertools.islice() where possible.

无论如何,这是同一算法的另一种实现,它使用scan索引而不是切片来扫描输入列表:

Anyway here's another implementation of the same algorithm, that scan through the input list with a scan index instead of slicing:

def re_range(lst):
    n = len(lst)
    result = []
    scan = 0
    while n - scan > 2:
        step = lst[scan + 1] - lst[scan]
        if lst[scan + 2] - lst[scan + 1] != step:
            result.append(str(lst[scan]))
            scan += 1
            continue

        for j in range(scan+2, n-1):
            if lst[j+1] - lst[j] != step:
                result.append(formatter(lst[scan], lst[j], step))
                scan = j+1
                break
        else:
            result.append(formatter(lst[scan], lst[-1], step))
            return ','.join(result)

    if n - scan == 1:
        result.append(str(lst[scan]))
    elif n - scan == 2:
        result.append(','.join(map(str, lst[scan:])))

    return ','.join(result)

一旦它比以前的***解决方案快〜65%,我就停止了工作,这似乎足够:)

无论如何,我想说可能仍有改进的空间(尤其是在中间的循环中).

Anyway I'd say that there might still be room for improvement (expecially in the middle for-loop).