2015-06-03 16 views
5

Na przykład, mający łańcuch:Włącz każdą parę znaków w ciągu

abcdefghijklmnopqrstuvwxyz 

powinno spowodować coś takiego:

badcfehgjilknmporqtsvuxwzy 

Jak mogę nawet tego zabrać?

myślałem o czymś nie bardzo wydajny, takich jak:

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) 

Czy jest jakiś lepszy sposób? Jakiś sposób, który jest bardziej efektywny lub bardziej ogólny, więc mógłbym łatwiej przejść do tego z 3 liter?

+0

Czy długość nawet być zawsze? – thefourtheye

+0

@thefourtheye No **. Nawiasem mówiąc, sposób, w jaki pisałem, wciąż działa na strunie o nierównej długości. – Jack

+0

Czego oczekujesz wyniku, jeśli chcesz uzyskać 3 znaki? – thefourtheye

Odpowiedz

3

Jedno rozwiązanie bez użycia importu będzie przekonwertować ciąg do iteratora i podczas iteracji pobrać następny znak wywołując następny na iteracyjnej:

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

wczytywania:

>>> 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 

Ale połączenia z next spowalniają działanie, ponieważ do znalezienia next Python ma udać się do poleceń wbudowanych, począwszy od zakresu lokalnej, niech buforować go i spróbuj jeszcze raz:

>>> 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 

Ale najszybszym rozwiązaniem będzie użycie 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 

@ Kod Jøran jest również bardzo blisko do tego, gdy używana z listy zamiast wyrażenia generatora, ale tworzy dwa dodatkowe ciągi pamięć:

>>> %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 

Uwaga że zawsze powinniśmy karmić list do str.join jeśli prędkość jest problemem: https://stackoverflow.com/a/9061024/846892

+0

Najszybsze rozwiązanie. Dziękuję Ci :) – Jack

7

Możesz użyć funkcji zip(), która zwróci listę krotek jako [(b,a), (d,c), ...] i stosującą metodę .join() zarówno do elementów krotki, jak i listy.

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

EDIT: obsłużyć przypadek nieparzystej długości strun, jak sugeruje @Ashwini i @ TigerhawkT3, można zmienić kod jako:

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

Co ze strunami o nieparzystej długości? –

+0

Dodanie '+ a [-1], jeśli len (a)% 2 else '" 'zrobiłoby lewę. – TigerhawkT3

+0

Zaktualizowałem moją odpowiedź, Dzięki @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

nie jestem pewien, że sięganie po wyrażenia regularne jest zawsze najlepszą rzeczą do zrobienia, ale wydaje się, że pasuje tutaj. Znajdź 2 znaki, podkręć je w odwrotnej kolejności i kontynuuj, dopóki nie skończysz.

import re 

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

łatwo uogólnić na inne numery znaków:

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

lub

>>> 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

zgodzili się, że chociaż chunking i zip jest podejściem, po pierwsze, regex jest łatwiejszy do odczytania i bardziej rozszerzalny. –

+0

Tak, dlatego wziąłem to pod uwagę ... ładne i krótkie i bardzo łatwe do odczytania (może zrobić drugi argument opcjonalny (.?) Do obsługi nieparzystych ciągów znaków?) –

+0

... pozostawione jako ćwiczenie dla czytelnika ... :) – bgporter

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

Jest to bardzo podobne do innych odpowiedzi tylko niektóre, co bardziej wyraźne w wyjaśnianiu co robi czytnik kodu

0

Iterowanie parami znaków i łączenie ich z izip() jest dość proste, a obsługa nieparzystych długości łańcuchów może być potraktowana przez dodanie warunkowej konkatenacji do końca.

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 '')) 

To samo można zrobić trochę bardziej zwięźle za pomocą izip_longest() zamiast izip(), jak sugeruje @Ashwini w komentarzu.

from itertools import izip_longest 

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

To również zawiedzie w przypadku nieparzystych ciągów długości. 'izip_longest' z' fillvalue = '' 'będzie tutaj ogólnym rozwiązaniem. –

+0

@Ashwini: Dobra uwaga - naprawiona. – martineau