用破折号写出数字范围

时间:2015-04-02 17:16:40

标签: python

我不太清楚如何提出这个问题,甚至在Google上搜索答案,但我会在这里写出来。我有一个整数的排序列表,对应于文件中的行号。我想将它们转换为字符串,但对于顺序的数字,我希望字符串具有序列的第一个数字,短划线,然后是最后一个数字。这是一个例子:

line_nums = [ 1, 2, 3, 5, 7, 8, 9, 10 ]

我想将该列表转换为:

[ '1-3', '5', '7', '8-10' ]

我写了一些大部分都有效的代码。在某些序列中,它会将相同的数字放在字符串中两次。在最近执行此代码时,输​​入是:

[ 10007, 10008, 10009, 10010, 10011, 10013, 10015, 10016, 10017, 10018, 10019 ]

但我得到的是:

[ '10007-10011', '10013-10013', '10015-10019' ]

这是我的代码:

def get_line_numbers_concat(line_nums):
    seq = []
    final = []
    last = 0

    for index, val in enumerate(line_nums):

        if last + 1 == val or index == 0:
            seq.append(val)
            last = val
        else:
            final.append(str(seq[0]) + '-' + str(seq[len(seq)-1]))
            seq = []
            seq.append(val)
            last = val

        if index == len(line_nums) - 1:
            if len(seq) > 1:
                final.append(str(seq[0]) + '-' + str(seq[len(seq)-1]))
            else:
                final.append(str(seq[0]))

    final_str = ', '.join(map(str, final))
    return final_str

5 个答案:

答案 0 :(得分:5)

除了seq[0]实际上与seq[len(seq)-1]相同的元素,然后您将其简化为len(seq)==1的情况或如下所示{{1}然后你执行正常处理,否则只需添加第一个元素。

if len(seq) > 1

答案 1 :(得分:2)

您可能可能会重新安排代码,以便在最后一个案例中不必复制,但是可以使用其中的内容:

查看第一个if..else,

对于单值序列,

str(seq[len(seq)-1]))将等于str(seq[-1]),这与str(seq[0])相同。我认为这会给你"10013-10013"

尝试在{1}}之上添加一个if len(seq) > 1:,看看这在抑制它方面是否有用。你可能还需要一个类似的if / else来处理一个数字的情况。

答案 2 :(得分:2)

您可以使用OrderedDict使用新序列的开头作为键,并在最后一个等于当前+ 1时附加值,然后加入子列表的第一个和最后一个元素(如果多于一个)元素或者只是添加单个元素:

from collections import OrderedDict

od = OrderedDict()

# create iterator
it = iter(l)

# get first element to use as starting key
key = next(it)

od[key] = [key]

# keep track of previous element
prev = key

for i in it:
    # if last element + 1 is equal to the current
    # add it to the current sequence
    if prev + 1 == i:
        od[key].append(i)
    else:
        # else start a new sequence adding key
        key = i
        od[key] = [i]
    # update prev 
    prev = i

# if a sublist had len > 1 we have a sequence so join first and last
# elements using str.format or else we just extract a single element 
print(["{}-{}".format(sub[0], sub[-1]) if len(sub) > 1 else str(sub[0]) for sub in od.values()])
['10007-10011', 10013, '10015-10019']

您可以使用key = l[0]然后for i in l[1:]但切片会创建一个新列表,因此使用iter可以让我们使用next获取第一个元素,将指针移动到第二个元素元素,它允许我们提取第一个元素,只是迭代其余元素而不切片。

In [7]: l = [1,2,3,4]
In [8]: it = iter(l)    
In [9]: next(it) # first element
Out[9]: 1    
In [10]: next(it) # second element ...
Out[10]: 2     
In [11]: next(it)
Out[11]: 3
In [12]: next(it)
Out[12]: 4

当你遍历iter对象时,它与调用next相同,所以当我们用next删除第一个元素时,我们会迭代余数。

In [13]: l = [1,2,3,4]    
In [14]: it = iter(l)    
In [15]: key = next(it)   
In [16]: key
Out[16]: 1   
In [17]: for i in it:
   ....:     print(i)
   ....:     
2
3
4

您也可以在没有dict的情况下执行此操作,如果序列中至少有两个,则将标志设置为True:

key, out = next(it), []
prev, flag = key, False

for i in it:
    if prev + 1 == i:
        flag = True
    else:
        # if flag is set we have a sequence else just add the key
        out.append("{}-{}".format(key, prev) if flag else str(key))
        # reset flag
        flag = False
        key = i
    prev = i
# catch last element
out.append("{}-{}".format(key, prev) if flag else str(key))

答案 3 :(得分:0)

我想提供一种替代解决方案,对我来说,看起来更简单 并且更容易使用。

这是因为它看起来完全像一个可以很容易解决的问题 左侧折叠,这正是reduce在python中的内容 (http://en.wikipedia.org/wiki/Fold_%28higher-order_function%29

  

reduce(function,iterable [,initializer])

     

将两个参数的函数累加到可迭代的项目中,从左到右,以便将迭代减少到单个值。例如,reduce(lambda x,y:x + y,[1,2,3,4,5])计算(((((1 + 2)+3)+4)+5)。左参数x是累加值,右参数y是迭代的更新值。如果存在可选的初始值设定项,则它将放置在计算中的iterable项之前,并在iterable为空时用作默认值。如果未给出初始化程序且iterable只包含一个项目,则返回第一个项目。大致相当于:

简单地说,我会处理iterable,这将是line_nums 使用提供的function一次一个值,由{I}决定 如果该值是已创建序列的一部分。这样我就会结束 用表示连续数字序列的列表列表。然后我 将它们转换为范围(xx-yy)或仅转换为单个值(xx)字符串。

所以我的解决方案看起来像这样:

def make_sequences(sequences, val):
    if sequences != [] and sequences[-1][-1] == val - 1:
        return sequences[:-1] + [sequences[-1] + [val]]
    return sequences + [[val]]

def sequence_to_string(s):
    return '%s-%s' % (s[0], s[-1]) if len(s) > 1 else str(s[0])

def get_line_numbers_concat(line_nums):
    return ', '.join(
        sequence_to_string(seq)
        '%s-%s' % (seq[0], seq[-1])
        for seq in reduce(make_sequences, line_nums, [])
    )

sequence_to_string(..)get_line_numbers_concat(..)函数是 非常简单,所以我只是解释一下里面发生了什么 make_sequences(..)

def make_sequences(sequences, val):

在第一次通话时,他sequences将为[](因为这已传递给reduceget_line_numbers_concat(..)),在后续的电话中,这就是 结果列表将构建 - 结果 make_sequences(..)sequences传递给make_sequences(..)后续调用 line_nums。为了说清楚,这是如何使用它来调用它 原make_sequences([], 10007) ==> [[10007]] make_sequences([[10007]], 10008) ==> [[10007, 10008]] ... make_sequences([[10007, 10008, 10009, 10010, 10011]], 10013) ==> [[10007, 10008, 10009, 10010, 1011], [10013]] ...

val

然后我们只需决定sequences是否属于最后一个序列 if sequences != [] and sequences[-1][-1] == val - 1: # (1)

sequences

这可以确保sequences[-1][-1]不为空(否则我们会得到 索引错误),然后我们检查最后一个序列中的最后一个数字 序列(即val - 1等于val因此 return sequences[:-1] + [sequences[-1] + [val]] 应附加到最后一个序列。

这是在这里完成的:

sequences[:-1]

我们获取除最后一个序列(val)之外的所有序列并追加 给他们一个新的序列,这是将(1)追加到最后的结果 序列

但是如果条件seqences == []不成立 - 这意味着要么没有 先前的序列(val)或最后一个序列的最后一个数字 并不比val少一个。在那种情况下,我们添加一个新序列 只有一个值 return sequences + [[val]]

{{1}}

答案 4 :(得分:0)

我尽量避免:

  • 开头或结尾的特殊处理
  • 设置和检查标记
  • 重复代码(甚至是单行)

这是我的解决方案:

{

#include<stdio.h>
#include<stdint.h>
#include<sys/ipc.h>
#include<sys/shm.h>


int main(int argc, char *argv[])
{

        int ret = 0;
        int shmid = 0;
        int key = 0xbade;
        struct shmid_ds shmi = {0};

        shmid = shmget(key, 0x200000, IPC_CREAT|0666);
        if (shmid < 0)
                perror("shmget");

        ret = shmctl(shmid, IPC_STAT, (struct shmid_ds *)&shmi);
        if (ret < 0)
                perror("shmctl");

        shmi.shm_perm.uid = 0;
        shmi.shm_perm.cuid = 0;
        shmi.shm_perm.cgid = 0;
        shmi.shm_perm.gid = 0;

        ret = shmctl(shmid, IPC_SET, (struct shmid_ds *)&shmi);
        if (ret < 0)
                perror("shmctl");

        return 0;
}
}