首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将连续数字分组到Python3.2中的范围中

将连续数字分组到Python3.2中的范围中
EN

Code Review用户
提问于 2011-10-05 16:05:07
回答 4查看 21.3K关注 0票数 13

下面是我编写的一个函数,用于在书籍中显示页码。

例如,如果输入列表[1,2,3,6,7,10],它将返回:

代码语言:javascript
复制
1-3,6-7,10

这似乎是一个很好的例子,说明如何在Python中使用字典数据类型。

是否有一种更有效的代码方法来做到这一点?

代码语言:javascript
复制
def formatpagelist (numberlist):
    tempdic={}
    returnstring=''

    for number in numberlist:
        if number-1 in tempdic.keys():
            tempdic[number-1]=number
        elif number-1 in tempdic.values():
            for key in tempdic.keys():
                if number-1==tempdic[key]: foundkey=key
            tempdic[foundkey]=number
        else:
            tempdic[number]=0

    keylist=list(tempdic.keys())
    keylist.sort()

    for key in keylist:
        if tempdic[key]>0:
            returnstring+=(str(key)+'-'+str(tempdic[key])+',')
        else: returnstring+=str(key)+','

    return returnstring
EN

回答 4

Code Review用户

发布于 2011-10-05 17:51:58

不使用字典的稍微短一些的版本:

代码语言:javascript
复制
def formatpagelist(numberlist):
    prev_number = min(numberlist) if numberlist else None
    pagelist = list()

    for number in sorted(numberlist):
        if number != prev_number+1:
            pagelist.append([number])
        elif len(pagelist[-1]) > 1:
            pagelist[-1][-1] = number
        else:
            pagelist[-1].append(number)
        prev_number = number

    return ','.join(['-'.join(map(str,page)) for page in pagelist])
票数 4
EN

Code Review用户

发布于 2011-10-06 00:07:54

代码语言:javascript
复制
def formatpagelist (numberlist):

python样式指南推荐函数名为words_with_underscores。

代码语言:javascript
复制
    tempdic={}

这是一个非常糟糕的变量名。它没有告诉我变量的用途。它告诉我变量是临时的(和所有变量一样),它是一个dict,这在{}中是显而易见的。

代码语言:javascript
复制
    returnstring=''

直到晚些时候才会出现..。为什么会在这里?

代码语言:javascript
复制
    for number in numberlist:
        if number-1 in tempdic.keys():

这和number - 1 in tempdic:一样

代码语言:javascript
复制
            tempdic[number-1]=number
        elif number-1 in tempdic.values():
            for key in tempdic.keys():
                if number-1==tempdic[key]: foundkey=key

如果你已经扫描了字典的键,这是一个你可能不应该使用字典的迹象。

代码语言:javascript
复制
            tempdic[foundkey]=number
        else:
            tempdic[number]=0

    keylist=list(tempdic.keys())
    keylist.sort()

这和keylist = sorted(tempdic)是一样的

代码语言:javascript
复制
    for key in keylist:
        if tempdic[key]>0:
            returnstring+=(str(key)+'-'+str(tempdic[key])+',')
        else: returnstring+=str(key)+','

我认为把这些放在一条线上会让人更难读懂。通常情况下,最好先建立一个列表,然后加入列表。

代码语言:javascript
复制
    return returnstring

这是另一种方法:我从@Jeff那里偷了一些部件,但我想尝试另一种方法。

代码语言:javascript
复制
import collections

pages = [1,2,5,6,7,9]
starts = collections.OrderedDict()
ends = collections.OrderedDict()
for idx, page in enumerate(pages):
    section = page - idx
    starts.setdefault(section, page)
    ends[section] = page
page_parts = []
for section, start in starts.items():
    end = ends[section]
    if start == end:
        page_parts.append("{0}".format(start))
    else:
        page_parts.append("{0}-{1}".format(start, end))
print(','.join(page_parts))
票数 1
EN

Code Review用户

发布于 2011-10-06 19:01:44

你对字典的使用似乎是一种让数字不符合顺序的方法。与其对它们进行排序,不如使用字典(以及相关的散列)来最大限度地提高效率。

这并不是很完美,因为您最终会对给定的值进行顺序搜索。

散列低:高范围(字典键:值对)以避免搜索没有多大帮助。只有他们的钥匙会被散列。在低端扩展范围的情况下,它确实有帮助。但是,为了在高端扩展范围,您必须使用字典值的搜索。

您真正创建的是“分区”的集合。每个分区都有一个低值和高值的边界。而不是字典,你也可以使用一个微不足道的元组(低,高)。对于大多数Pythonic来说,(低,高)对包括低,但不包括高。这是“半开区间”。

这里有一个使用简单的元组set的版本,它依赖于散列而不是二分法。

二进制搜索(使用bisect模块)也可能执行得很好。这将稍微简化相邻范围的合并,因为这两个范围实际上是相邻的。然而,这会导致重组顺序的成本。

从一组空的分区开始,第一个数字就会创建一个简单的分区。

下一个数字可以引出三件事中的一件。

  1. 它在低端或高端扩展现有分区。这个数字正好与一个范围相邻。
  2. 它创建了一个新的分区。该数字与任何范围不相邻。
  3. 它“桥接”两个相邻的分区,将它们合并成一个。这个数字与两个范围相邻。

就像这样。

代码语言:javascript
复制
def make_ranges(numberlist):
    ranges=set()
    for number in numberlist:
        print ranges, number
        adjacent_to = set( r for r in ranges if r[0]-1 == number or r[1] == number )
        if len(adjacent_to) == 0:
            # Add a new partition.
            r = (number,number+1)
            assert r[0] <= number < r[1] # Trivial, right?
            ranges.add( r )
        elif len(adjacent_to) == 1:
            # Extend this partition, build a new range.
            ranges.difference_update( adjacent_to )
            r= adjacent_to.pop()
            if r[0]-1 == number: # Extend low end
                r= (number,r[1])
            else:
                r= (r[0],number+1) # Extend high end
            assert r[0] <= number < r[1] 
            ranges.add( r )
        elif len(adjacent_to) == 2:
            # Merge two adjacent partitions.
            ranges.difference_update( adjacent_to )
            r0= adjacent_to.pop()
            r1= adjacent_to.pop()
            r = ( min(r0[0],r1[0]), max(r0[1],r1[1]) )
            assert r[0] <= number < r[1]
            ranges.add( r )
    return ranges

这是对您的算法进行的非常彻底的重新思考,所以这不是一个正确的代码评审。

在Python中,用分隔符组装字符串更简单。对于","-separated字符串的例子,请始终考虑这样做。

代码语言:javascript
复制
final = ",".join( details )

一旦你有了它,你只需要做一系列的细节。在这种情况下,每个细节都是"x“或”x“,作为范围可以采取的两种形式。

所以一定是这样的

代码语言:javascript
复制
details = [ format_a_range(r) for r in ranges ]

在本例中,我将格式显示为内联def。它也可以作为一个if-else表达式来完成。

根据您的范围,总体功能是这样的。

代码语言:javascript
复制
def formatpagelist (numberlist):
    ranges= make_ranges( numberlist )
    def format_a_range( low, high ):
        if low == high-1: return "{0}".format( low )
        return "{0}-{1}".format( low, high-1 )
    strings = [ format_a_range(r) for r in sorted(ranges) ]
    return ",".join( strings )
票数 0
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/5196

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档