2013-10-07 16 views
6

Questo può essere un problema banale, ma voglio saperne di più su altri modi più intelligenti ed efficaci per risolverlo.Modo efficace di conteggio Vero e Falso

Ho un elenco di articoli e ogni articolo ha una proprietà a il cui valore è binario.

  • Se ogni elemento della lista ha a == 0, poi ho impostare una variabile separata b = 0.
  • Se ogni elemento nell'elenco ha a == 1, ho impostato b = 1.
  • Se è presente una combinazione di a == 0 e a == 1 nell'elenco, quindi ho impostato b = 2.

posso usare un set per tenere traccia dei tipi di a valore, in modo tale che se ci sono due elementi nel set dopo scorrendo l'elenco, allora posso impostare b = 2, mentre se v'è un solo elemento nel set ho appena recuperato l'oggetto (o 0 o 1) e lo uso per impostare b.

Un modo migliore?

+3

"Better" è soggettivo e anche qui dipende un po 'da quale dei 3 casi si prevede di colpire il più delle volte e può anche dipendere dal lunghezza della lista, ecc. – mgilson

+0

@ mgilson, buoni punti. Non so quale caso (s) verrà più spesso di altri e la proprietà 'a' è impostata in base all'input degli utenti. So che la lunghezza della lista sarà breve, cioè <= 1000 articoli. – ChrisG

risposta

18

Suggerisco di utilizzare any e all. Direi che il vantaggio di questo è la leggibilità piuttosto che l'intelligenza o l'efficienza. Per esempio:

>>> vals0 = [0, 0, 0, 0, 0] 
>>> vals1 = [1, 1, 1, 1, 1] 
>>> vals2 = [0, 1, 0, 1, 0] 
>>> def category(vals): 
...  if all(vals): 
...   return 1 
...  elif any(vals): 
...   return 2 
...  else: 
...   return 0 
... 
>>> category(vals0) 
0 
>>> category(vals1) 
1 
>>> category(vals2) 
2 

puo essere accorciato un po 'se ti piace:

>>> def category(vals): 
...  return 1 if all(vals) else 2 if any(vals) else 0 
... 

Questo funziona con tutto ciò che può essere interpretato da __nonzero__ (o __bool__ in Python 3) come avere un vero o falso valore.

+0

+1 Esattamente la risposta che avrei postato un paio di minuti prima di te se il mio ISP non avesse deciso di morire. :-) (eccetto il one-liner, che è il codice golf). –

+0

nah, non è codice golf, puoi vedere quel nome di parametro orribilmente lungo? –

2

potresti anche utilizzare i set.

s = set([i.a for i in your_list]) 
if len(s) == 1: 
    b = s.pop() 
else: 
    b = 2 
+1

Un oggetto set non supporta l'indicizzazione. Usa 'next (iter (s))'. Inoltre, perché il 'i.a'? – Noctua

+0

i.a era semplicemente un pull i valori fuori dalla lista di oggetti come indicato nella domanda: "Ho una lista di articoli e ogni oggetto ha una proprietà il cui valore è binario." – bcollins

+0

Sì, grazie per il commento Noctua. Gli insiemi NON supportano l'indicizzazione. Stavo anche osservando un comportamento strano con il condizionale in-linea e rimosso. Penso che mi piaccia il any/all answer the best – bcollins

26

un solo passaggio attraverso la lista, e non strutture dati aggiuntivi costruiti:

def zot(bs): 
    n, s = len(bs), sum(bs) 
    return 1 if n == s else 2 if s else 0 
+0

nice. cosa significa zot? zero-uno-due? – bcollins

+1

Questo attira l'attenzione sul caso d'angolo 'bs == []'. È interessante che le nostre soluzioni lo gestiscano allo stesso modo ('zot' e' category' restituiscono entrambi '1'), ma non sono sicuro che sia la risposta corretta. 'all ([])' restituisce 'True', mentre' any ([]) 'restituisce' False', quindi se avessi organizzato la mia istruzione if in modo diverso, 'category ([])' avrebbe restituito '0'. – senderle

+0

Penso che l'affermazione del problema sia contraddittoria: per un input vuoto, è banalmente vero che tutti sono 0, e che tutti sono 1. Quindi restituiamo la somma delle due risposte corrette ;-) –

1

È possibile definire due booleano vars hasZero e hasOne e metterli a True se il valore corrispondente è stato raggiunto, mentre l'iterazione la lista . Quindi b = 2 se hasZero and hasOne, b = 1 se solo hasOne e b = 0 se solo hasZero.

Un altro modo: è possibile sommare tutti i valori a lungo l'elenco. Se sumA == len(list) quindi b = 1, se sumA == 0 quindi b = 0 e se 0 < sumA < len(list) quindi b = 2.

15

Qualcuno ha menzionato codice di golf, quindi non può resistere una variazione sul @ senderle di:

[0,2,1][all(vals) + any(vals)] 

Breve spiegazione: Questo utilizza i valori booleani come i loro equivalenti interi per indicizzare un elenco di risposte desiderate. Se all è vero, allora anche any deve essere vero, quindi la loro somma è 2.any da solo dà 1 e nessuna corrispondenza dà 0. Questi indici restituiscono i valori corrispondenti dall'elenco.

Se i requisiti originali potrebbero essere modificati per utilizzare 1 per any e 2 per all sarebbe ancora più semplice per restituire solo il numero intero di any + all

+0

+1, vero modo pythonic :) – kasitan

1

soluzione corto circuito. Probabilmente il modo più efficiente per farlo in Python.

MODIFICA: Incluso any e all come da suggerimento nei commenti.

EDIT2: Ora è un one-liner.

b = 1 if all(A) else 2 if any(A) else 0 
+1

Le funzioni 'any()' e 'all()' integrate sono probabilmente più veloci di qualsiasi esplicito loop-through-the-items. – martineau

+0

@martineau Il ciclo esplicito per il ciclo è implementato in C, proprio come 'any' e' all'. Non vedo perché l'iterazione sarebbe più lenta dei tuoi metodi. – Shashank

+0

Il ciclo non è implementato in C allo stesso modo. – martineau

0

Questo è simile al suggerimento di senderle, ma scritto per accedere agli oggetti a proprietà.

from random import randint 

class Item(object): 
    def __init__(self, a): 
     self.a = a 

all_zeros = [Item(0) for _ in xrange(10)] 
all_ones = [Item(1) for _ in xrange(10)] 
mixture = [Item(randint(0, 1)) for _ in xrange(10)] 

def check(items): 
    if all(item.a for item in items): 
     return 1 
    if any(item.a for item in items): 
     return 2 
    else: 
     return 0 

print 'check(all_zeros):', check(all_zeros) 
print 'check(all_ones):', check(all_ones) 
print 'check(mixture):', check(mixture) 
3

Utilizzo di un dizionario:

zonk_values = {frozenset([0]): 0, frozenset([1]): 1, frozenset([0, 1]): 2} 
def zonk(a): 
    return zonk_values[frozenset(a)] 

Anche questo ha bisogno di un solo passaggio attraverso la lista.

+0

È vero, è one-pass, ma visita ogni oggetto nella lista e crea un 'frozenset' mentre lo fa. – martineau

0

È possibile utilizzare l'elenco iter di attori ed attrici:

>>> L = [0, 0, 0, 0, 0] 
>>> L1 = [1, 1, 1, 1, 1] 
>>> L2 = [0, 1, 0, 1, 0] 
>>> def fn(i): 
...  i = iter(i) 
...  if all(i): return 1 
...  return 2 if any(i) else 0 
... 
>>> fn(L) 
0 
>>> fn(L1) 
1 
>>> fn(L2) 
2 
2
def zot(bs): 
    return len(set(bs)) if sum(bs) else 0