2015-06-03 9 views
5

Ad esempio, avere la stringa:Interruttore ogni coppia di caratteri di una stringa

abcdefghijklmnopqrstuvwxyz 

dovrebbe portare a qualcosa di simile:

badcfehgjilknmporqtsvuxwzy 

Come posso anche andare a questo proposito?

ho pensato a qualcosa di non molto efficiente, come ad esempio:

s = str(range(ord('a'), ord('z') + 1)) 
new_s = '' 
for i in xrange(len(s)): 
    if i != 0 and i % 2 == 0: 
     new_s += '_' + s[i] 
    else: 
     new_s += s[i] 
# Now it should result in a string such as 'ab_cd_ef_...wx_yz' 
l = new_s.split('_') 
for i in xrange(len(l)): 
    l[i] = l[i][::-1] 
result = str(l) 

Esiste un modo migliore? Qualche modo è più efficiente o più generale, quindi potrei anche parlarne più facilmente con 3 lettere?

+0

Intende la lunghezza di essere ancora sempre? – thefourtheye

+0

@thefourtheye No **. A proposito, il modo in cui ho postato funziona ancora per una stringa con una lunghezza irregolare. – Jack

+0

Cosa ti aspetti dal risultato se vuoi ottenere 3 personaggi? – thefourtheye

risposta

3

Una soluzione senza l'uso di importazioni sarà quello di convertire la stringa a un iteratore e durante l'iterazione prendere il carattere successivo chiamando successiva sul iteratore:

>>> s = "abcdefghijklmnopqrstuvwxyz" 
>>> it = iter(s) 
>>> ''.join(next(it, '') + c for c in it) 
'badcfehgjilknmporqtsvuxwzy' 

Timings:

>>> s = "abcdefghijklmnopqrstuvwxyz" * 10**5 
>>> def func_next_no_cache(s): 
    it = iter(s) 
    return ''.join([next(it, '') + c for c in it]) 
... 
>>> %timeit func_next_no_cache(s) 
1 loops, best of 3: 291 ms per loop 

Ma le chiamate a next in realtà lo stanno rallentando perché per trovare next Python deve andare ai comandi incorporati a partire dal ambito locale, cerchiamo di memorizzare nella cache e riprovare:

>>> def func_next_cache(s, next=next): 
    it = iter(s) 
    return ''.join([next(it, '') + c for c in it]) 
... 
>>> %timeit func_next_cache(s) 
1 loops, best of 3: 241 ms per loop 

Ma la soluzione più veloce sarà quello di utilizzare itertools.izip_longest:

>>> from itertools import izip_longest 
>>> def func_izip_l(s): 
    it = iter(s) 
    return "".join([b+a for a, b in izip_longest(it, it, fillvalue='')]) 
... 
>>> %timeit func_izip_l(s) 

1 loops, best of 3: 209 ms per loop 

@ codice di Joran è anche molto vicino a questo quando viene utilizzato con una lista, invece di generatore di espressione, ma crea due stringhe supplementari in memoria:

>>> %timeit "".join([b+a for a, b in izip_longest(s[::2], s[1::2], fillvalue="")]) 
1 loops, best of 3: 212 ms per loop 

Nota che dovremmo sempre nutrire una list-str.join se la velocità è una preoccupazione: https://stackoverflow.com/a/9061024/846892

+0

La soluzione più veloce. Grazie :) – Jack

7

È possibile utilizzare la funzione zip() che restituisce un elenco di tuple come [(b,a), (d,c), ...] e il metodo di applicazione .join() per entrambi gli elementi della tupla e dell'elenco.

a = "abcdefghijklmnopqrstuvwxyz" 
# a[::2] = "acegikmoqsuwy" 
# a[1::2] = "bdfhjlnprtvx" 
print "".join("".join(i) for i in zip(a[1::2], a[::2])) 
>>> badcfehgjilknmporqtsvuxwzy 

EDIT: per gestire il caso di stringhe di lunghezza dispari, come suggerito da @Ashwini e @ TigerhawkT3, è possibile modificare il codice come:

print "".join("".join(i) for i in zip(a2, a1)) + a[-1] if len(a)%2 else '' 
+2

E le stringhe di lunghezza dispari? –

+0

Aggiungendo '+ a [-1] se len (a)% 2 altro ''' farebbe il trucco. – TigerhawkT3

+0

Aggiornato la mia risposta, grazie a @AshwiniChaudhary. – ZdaR

0
from itertools import zip, chain 

c1 = [c for i, c in enumerate(s) if i % 2 == 0] 
c2 = [c for i, c in enumerate(s) if i % 2 == 1] 
''.join(chain.from_iterable(zip(c2,c1))) 
2

io non sono certo che raggiungere prima le espressioni regolari è sempre la cosa migliore da fare, ma sembra che si adatti a questo. Trova 2 caratteri, sottoponili in ordine inverso e continua fino a quando non sei fuori stringa.

import re 

>>> s = "abcdefghijklmnopqrstuvwxyz" 
>>> re.sub(r'(.)(.)', "\g<2>\g<1>", s) 
'badcfehgjilknmporqtsvuxwzy' 

facilmente generalizzata ad altri numeri di caratteri:

>>> def swap3(txt): 
... return re.sub(r'(.)(.)(.)', '\g<3>\g<2>\g<1>', txt) 
... 
>>> swap3(s) 
'cbafedihglkjonmrqputsxwvyz' 

o

>>> def parameterizedSwap(txt, numChars): 
... pat = r"(.)" * numChars 
... replace = "".join(["\g<{0}>".format(numChars-i) for i in range(numChars)]) 
... return re.sub(pat, replace, txt) 
... 
>>> parameterizedSwap(s, 5) 
'edcbajihgfonmlktsrqpyxwvuz' 
+1

concorda sul fatto che, anche se il chunking e il zip sono l'approccio che prima preferisco, regex è più facile da leggere e più estendibile. –

+0

sì, ecco perché ho upvoted questo ... bello e breve e molto facile da leggere (forse rendere il secondo argomento opzionale (.?) Per gestire stringhe di lunghezza dispari?) –

+0

... lasciato come esercizio per il lettore ... :) – bgporter

1
from itertools import izip_longest as myzip 
"".join(b+a for a,b in myzip(a[::2],a[1::2],fillvalue="")) 

questo è molto simile alle altre risposte solo alcune quello più esplicito nello spiegare ciò che sta facendo al lettore del codice

0

L'iterazione di più coppie di caratteri e la loro unione con izip() è abbastanza semplice e la gestione di lunghezze di stringa dispari può essere gestita aggiungendo una concatenazione condizionale alla fine.

from itertools import izip 

s = "abcdefghijklmnopqrstuvwxyz" 
print ("".join(((pair[1]+pair[0]) for pair in izip(*[iter(s)]*2))) + 
      (s[-1] if len(s) % 2 else '')) 

La stessa cosa si può fare un po 'più succintamente utilizzando izip_longest() invece di izip(), come suggerisce @Ashwini nel commento.

from itertools import izip_longest 

s = "abcdefghijklmnopqrstuvwxyz" 
print "".join(((pair[1]+pair[0]) for pair in 
        izip_longest(*[iter(s)]*2, fillvalue=''))) 
+0

Ciò fallirà anche per stringhe di lunghezza dispari. 'izip_longest' con' fillvalue = '' 'sarà una soluzione generale qui. –

+0

@Ashwini: buon punto - risolto. – martineau

Problemi correlati