2010-11-02 5 views
5

J'ai écrit cette classe pour compresser et développer des listes de nombres en chaînes de séquence, y compris des valeurs de pas lorsque la valeur de l'étape est supérieure à 1. Le code est toujours trop lourd. Y a-t-il des bibliothèques qui peuvent faire quelque chose comme ça? Code éventuellement plus simple?Impression d'une chaîne numérique formatée à partir d'une liste de nombres en python

import re 

class Foo(object): 

    def __init__(self, num_list): 
     self.num_list = sorted(list(set([ int(n) for n in num_list ]))) 
    # end def __init__ 

    def gen_seq_data(self): 
     self.seq_data  = list() 
     index_offset  = None 
     backward_step_value = None 
     forward_step_value = None 
     sub_list   = list() 
     sub_list_step_value = None 
     for index, num in enumerate(self.num_list): 

      if index - 1 < 0: 
       backward_step_value = None 
      # end if 
      else: 
       backward_step_value = num - self.num_list[ index - 1 ] 
      # end else 

      try: 
       forward_step_value = self.num_list[ index + 1 ] - num 
      # end try 
      except IndexError: 
       forward_step_value = None 
      # end except 

      if backward_step_value is None: 
       sub_list.append(num) 
      # end if 
      elif backward_step_value == forward_step_value: 
       sub_list.append(num) 
       if forward_step_value is None: 
        self.seq_data.append((sub_list_step_value, sub_list)) 
       # end if 
      # end if 
      elif backward_step_value == sub_list_step_value: 
       sub_list.append(num) 
       if sub_list: 
        self.seq_data.append((sub_list_step_value, sub_list)) 
       # end if 
       sub_list = list() 
      # end elif 
      else: 
       if sub_list: 
        self.seq_data.append((sub_list_step_value, sub_list)) 
       # end if 
       sub_list = [ num ] 
       if forward_step_value is None: 
        self.seq_data.append((sub_list_step_value, sub_list)) 
       # end if 
      # end else 

      try: 
       sub_list_step_value = sub_list[ -1 ] - sub_list[ -2 ] 
      # end try 
      except IndexError: 
       sub_list_step_value = None 
      # end except 
     # end for 
    # end def gen_seq_object 

    def format_elements(self): 
     format_elements = list() 
     for step, num_list in self.seq_data: 
      if step is None: 
       format_elements.append('%s' % (num_list[ 0 ])) 
      # end if 
      elif step == 1: 
       format_elements.append('%s-%s' % (num_list[ 0 ], num_list[ -1 ])) 
      # end elif 
      else: 
       format_elements.append('%s-%sx%s' % (num_list[ 0 ], num_list[ -1 ], step)) 
      # end else 
     # end for 
     return format_elements 
    # end def format_range 

    def format_range(self): 
     return ','.join(self.format_elements()) 
    # end def format_range 

    def expand_range(self): 
     num_list = list() 
     for r_token in self.format_range().split(','): 
      if r_token.isdigit(): 
       num_list.append(int(r_token)) 
      # end if 
      elif '-' in r_token: 
       if 'x' in r_token: 
        start, end, step = re.split(r'[-|x]', r_token) 
        num_list.extend(range(int(start), int(end) + 1, int(step))) 
       # end if 
       else: 
        start, end = r_token.split('-') 
        num_list.extend(range(int(start), int(end) + 1)) 
       # end else 
      # end elif 
     # end for 
     return num_list 
    # end def expand_range 

# end class Foo 

Entrée/sortie:

data = [ 1, 4, 5, 6, 10, 15, 16, 17, 18, 20, 22, 24, 26, 27, 28, 30, 35, 40, 45, 50, 56, 63, 66, 69, 72 ] 

foo = Foo(data) 
foo.gen_seq_data() 

print data 

print foo.format_range() 
1,4-6,10,15-18,20-26x2,27,28,30-50x5,56,63-72x3 

print foo.expand_range() 
[1, 4, 5, 6, 10, 15, 16, 17, 18, 20, 22, 24, 26, 27, 28, 30, 35, 40, 45, 50, 56, 63, 66, 69, 72] 
+0

Veuillez corriger votre indentation. Votre code est mal formaté. S'il vous plaît (1) éditez la question, (2) lisez les instructions de mise en forme sur le côté droit de la page et (3) évitez de poster chaque morceau de code que vous possédez. Quel problème as-tu? Quel est le plus petit morceau de code qui montre ce ** problème **? –

+3

@ user494203: Jetez les lignes "# end if" etc. ils sont monstrueusement inutiles, surtout ceux qui se produisent juste avant elif et else. Si vous faites cela, les gens peuvent être plus enclins à lire votre code et à suggérer d'autres désincrustations. –

Répondre

2

One. Supprimez tous les commentaires #END. Ils sont monstrueusement inutiles. Votre indentation parle d'elle-même. Utilise le.

Deux. Ne fais pas de ça une classe. Ce n'est pas un objet distinct avec des responsabilités distinctes. C'est juste un algorithme. Composé de fonctions. Au mieux, c'est une classe avec toutes les méthodes statiques.

Trois. Ne jamais le faire

for index, num in enumerate(self.num_list): 
     if index - 1 < 0: 
      backward_step_value = None 
     # end if 
     else: 
      backward_step_value = num - self.num_list[ index - 1 ] 
     # end else 

Si le premier élément est spécial, traitez-le séparément.

backward_step_value = self.num_list[0] 
for num in self.num_list[1:]: 

Vous avez rarement besoin de l'index pour quelque chose comme ça. En effet, la seule raison d'avoir l'index semble être de traiter le premier élément spécialement.

Enfin, il s'agit d'une "réduction". Utiliser une fonction de générateur

def reduce_list(some_list): 
    v= min(some_list) 
    low, high = v, v 
    for v in sorted(some_list)[1:]: 
     if v == high+1: 
      high= high+1 
     else: 
      yield low, high 
    yield low, high 

Ceci pourrait donner votre liste de plages contiguës. Vous pouvez ensuite les formater.

format_elements(reduce_list(some_list)) 
+0

Cette solution n'atteint pas la partie la plus délicate de la solution OP, à savoir identifier non seulement des plages continues, mais aussi des plages avec un pas fixe de> 1. – jchl

+0

@jchl: Correct. Le but n'était pas de réparer leur code. Le but était de fournir un moyen de réviser le code pour qu'il soit moins «maladroit». Inclure l'étape en tant que variable au lieu de «1» ne devrait pas être trop difficile à faire en tant qu'extension. –

1

Les poignées de solution suivante plages non contiguës, et préserve également le comportement d'ignorer les gammes de longueur 2.

def reduce_list(seq): 
    l = sorted(set(seq)) 
    low = high = l[0] 
    step = None 
    for v in l[1:]: 
     if step is None or v - high == step: 
      # Extend the current range. 
      step = v - high 
      high = v 
     elif high - low == step: 
      # The current range only has two values. Yield the 
      # first value, and start a new range comprising the 
      # second value and the current value. 
      yield low, low, None 
      step = v - high 
      low = high 
      high = v 
     else: 
      # Yield the current range, and start a new one. 
      yield low, high, step 
      low = high = v 
      step = None 
    if high - low == step: 
     # The final range has only two values. Yield them 
     # individually. 
     yield low, low, None 
     step = None 
     low = high 
    yield low, high, step 

def format_element(low, high, step): 
    if step is None: 
     assert low == high 
     return "%s" % (low,) 
    elif step == 1: 
     return "%s-%s" % (low, high) 
    else: 
     return "%s-%sx%s" % (low, high, step) 

def format_list(seq): 
    return ','.join(format_element(*e) for e in seq) 

Voici un code de test:

def test(*args): 
    print args, "==", format_list(reduce_list(args)) 

test(1) 
test(1, 2) 
test(1, 2, 3) 
test(0, 10) 
test(0, 10, 20) 
test(0, 10, 11, 12, 14, 16) 
test(0, 2, 4, 8, 16, 32, 64) 
test(0, 1, 3, 4, 6, 7, 9, 10) 
test(0, 1, 3, 6, 10, 15, 21, 28) 

qui sort:

(1,) == 1 
(1, 2) == 1,2 
(1, 2, 3) == 1-3 
(0, 10) == 0,10 
(0, 10, 20) == 0-20x10 
(0, 10, 11, 12, 14, 16) == 0,10-12,14,16 
(0, 2, 4, 8, 16, 32, 64) == 0-4x2,8,16,32,64 
(0, 1, 3, 4, 6, 7, 9, 10) == 0,1,3,4,6,7,9,10 
(0, 1, 3, 6, 10, 15, 21, 28) == 0,1,3,6,10,15,21,28 
+0

Merci pour votre aide. Ce sont de bonnes solutions. – Bip

Questions connexes