두 문자열을 인터리브하는 가장 비단뱀적인 방법


115

두 개의 문자열을 함께 메시하는 가장 비단뱀적인 방법은 무엇입니까?

예를 들면 :

입력:

u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'

산출:

'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'

2
여기에 대한 답변은 두 개의 입력 문자열이 동일한 길이라고 가정했습니다. 이것이 안전한 가정입니까, 아니면 처리해야합니까?
SuperBiasedMan

@SuperBiasedMan 솔루션이있는 경우 모든 조건을 처리하는 방법을 확인하는 것이 도움이 될 수 있습니다. 질문과 관련이 있지만 구체적으로 내 경우는 아닙니다.
Brandon Deo

3
@drexx 어쨌든 최고의 답변자가 그것에 대한 해결책으로 댓글을 달았 기 때문에 나는 그것을 포괄적으로 편집했습니다.
SuperBiasedMan

답변:


127

나에게 가장 비단뱀적인 방법은 거의 같은 일을 하지만 +각 문자열의 개별 문자를 연결하기 위해 연산자를 사용하는 다음과 같습니다 .

res = "".join(i + j for i, j in zip(u, l))
print(res)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'

또한 두 번의 join()호출을 사용하는 것보다 빠릅니다 .

In [5]: l1 = 'A' * 1000000; l2 = 'a' * 1000000

In [6]: %timeit "".join("".join(item) for item in zip(l1, l2))
1 loops, best of 3: 442 ms per loop

In [7]: %timeit "".join(i + j for i, j in zip(l1, l2))
1 loops, best of 3: 360 ms per loop

더 빠른 접근 방식이 있지만 종종 코드를 난독 화합니다.

참고 : 두 입력 문자열의 길이 가 같지 않으면 더 zip짧은 문자열의 끝에서 반복을 중지 하므로 긴 문자열이 잘립니다 . 이 경우 대신 zip하나를 사용해야합니다 zip_longest( izip_longest으로부터 파이썬 2) itertools두 문자열이 완전히 소진되는 것을 보장하기 위해 모듈.


* Zen of Python 의 인용문 : 가독성이 중요합니다 .
Pythonic = 나를위한 가독성 ; i + j적어도 내 눈에는 시각적으로 더 쉽게 구문 분석됩니다.


1
하지만 n 개의 문자열에 대한 코딩 노력은 O (n)입니다. 그래도 n이 작은 한 좋습니다.
TigerhawkT3

생성기가 조인보다 더 많은 오버 헤드를 유발할 수 있습니다.
Padraic Cunningham

5
실행 "".join([i + j for i, j in zip(l1, l2)])그것은 확실히 빠른 것
Padraic 커닝햄

6
"".join(map("".join, zip(l1, l2)))반드시 더 비단뱀적인 것은 아니지만 더 빠릅니다.
Aleksi Torhamo

63

더 빠른 대안

또 다른 방법:

res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
print(''.join(res))

산출:

'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'

속도

더 빠른 것 같습니다.

%%timeit
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
''.join(res)

100000 loops, best of 3: 4.75 µs per loop

지금까지 가장 빠른 솔루션보다 :

%timeit "".join(list(chain.from_iterable(zip(u, l))))

100000 loops, best of 3: 6.52 µs per loop

더 큰 문자열의 경우 :

l1 = 'A' * 1000000; l2 = 'a' * 1000000

%timeit "".join(list(chain.from_iterable(zip(l1, l2))))
1 loops, best of 3: 151 ms per loop


%%timeit
res = [''] * len(l1) * 2
res[::2] = l1
res[1::2] = l2
''.join(res)

10 loops, best of 3: 92 ms per loop

파이썬 3.5.1.

길이가 다른 문자열의 변형

u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijkl'

짧은 것이 길이를 결정합니다 ( zip()동등)

min_len = min(len(u), len(l))
res = [''] * min_len * 2 
res[::2] = u[:min_len]
res[1::2] = l[:min_len]
print(''.join(res))

산출:

AaBbCcDdEeFfGgHhIiJjKkLl

긴 것이 길이를 결정합니다 ( itertools.zip_longest(fillvalue='')동등 함).

min_len = min(len(u), len(l))
res = [''] * min_len * 2 
res[::2] = u[:min_len]
res[1::2] = l[:min_len]
res += u[min_len:] + l[min_len:]
print(''.join(res))

산출:

AaBbCcDdEeFfGgHhIiJjKkLlMNOPQRSTUVWXYZ

49

join()zip().

>>> ''.join(''.join(item) for item in zip(u,l))
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'

17
또는''.join(itertools.chain.from_iterable(zip(u, l)))
Blender

1
하나가 다른 것보다 zip짧으면 목록이 잘 리며, 더 짧은 목록이 완전히 반복되면 중지됩니다.
SuperBiasedMan

5
@SuperBiasedMan-네. itertools.zip_longest문제가되는 경우 사용할 수 있습니다.
TigerhawkT3

18

Python 2에서 작업을 수행 하는 가장 빠른 방법은 작은 문자열의 경우 목록 분할 속도의 ~ 3 배, 긴 문자열의 경우 ~ 30x입니다.

res = bytearray(len(u) * 2)
res[::2] = u
res[1::2] = l
str(res)

그러나 이것은 Python 3에서 작동하지 않습니다. 다음과 같은 것을 구현할 수 있습니다.

res = bytearray(len(u) * 2)
res[::2] = u.encode("ascii")
res[1::2] = l.encode("ascii")
res.decode("ascii")

그러나 그때까지 당신은 이미 작은 문자열에 대한 목록 슬라이싱에 대한 이득을 잃어 버렸고 (긴 문자열의 경우 여전히 20 배의 속도) 이것은 비 ASCII 문자에서도 작동하지 않습니다.

FWIW, 당신이 경우에 하는 대규모 문자열에이 일을 모든 사이클을 필요로 하고 어떤 이유로 파이썬 문자열을 사용해야합니다 ... 여기 그 방법은 다음과 같습니다

res = bytearray(len(u) * 4 * 2)

u_utf32 = u.encode("utf_32_be")
res[0::8] = u_utf32[0::4]
res[1::8] = u_utf32[1::4]
res[2::8] = u_utf32[2::4]
res[3::8] = u_utf32[3::4]

l_utf32 = l.encode("utf_32_be")
res[4::8] = l_utf32[0::4]
res[5::8] = l_utf32[1::4]
res[6::8] = l_utf32[2::4]
res[7::8] = l_utf32[3::4]

res.decode("utf_32_be")

작은 유형의 일반적인 경우를 특수 케이스로 지정하는 것도 도움이됩니다. FWIW, 이것은 긴 문자열의 경우 목록 분할 속도의 3 배에 불과 하고 작은 문자열 의 경우 4 ~ 5 배 더 느립니다 .

어느 쪽이든 join솔루션을 선호 하지만 타이밍이 다른 곳에서 언급되었으므로 참여하는 것이 좋을 것이라고 생각했습니다.


16

가장 빠른 방법을 원한다면 itertoolsoperator.add다음 과 결합 할 수 있습니다 .

In [36]: from operator import add

In [37]: from itertools import  starmap, izip

In [38]: timeit "".join([i + j for i, j in uzip(l1, l2)])
1 loops, best of 3: 142 ms per loop

In [39]: timeit "".join(starmap(add, izip(l1,l2)))
1 loops, best of 3: 117 ms per loop

In [40]: timeit "".join(["".join(item) for item in zip(l1, l2)])
1 loops, best of 3: 196 ms per loop

In [41]:  "".join(starmap(add, izip(l1,l2))) ==  "".join([i + j   for i, j in izip(l1, l2)]) ==  "".join(["".join(item) for item in izip(l1, l2)])
Out[42]: True

그러나 결합 izip하고 chain.from_iterable다시 더 빠릅니다

In [2]: from itertools import  chain, izip

In [3]: timeit "".join(chain.from_iterable(izip(l1, l2)))
10 loops, best of 3: 98.7 ms per loop

chain(*와 사이에도 상당한 차이가 있습니다 chain.from_iterable(....

In [5]: timeit "".join(chain(*izip(l1, l2)))
1 loops, best of 3: 212 ms per loop

조인이있는 생성기와 같은 것은 없습니다. 하나를 전달하는 것은 데이터에 대해 두 번의 전달을 수행하기 때문에 먼저 콘텐츠를 사용하여 목록을 작성하기 때문에 하나를 전달하는 것이 항상 느려질 것입니다. 하나는 필요한 크기를 파악하고 하나는 실제로 수행하기 때문입니다. 생성기로는 불가능한 조인 :

join.h :

 /* Here is the general case.  Do a pre-pass to figure out the total
  * amount of space we'll need (sz), and see whether all arguments are
  * bytes-like.
   */

또한 길이가 다른 문자열이 있고 데이터를 잃지 않으려면 izip_longest 사용할 수 있습니다 .

In [22]: from itertools import izip_longest    
In [23]: a,b = "hlo","elworld"

In [24]:  "".join(chain.from_iterable(izip_longest(a, b,fillvalue="")))
Out[24]: 'helloworld'

파이썬 3의 경우 zip_longest

그러나 python2의 경우 veedrac의 제안이 훨씬 빠릅니다.

In [18]: %%timeit
res = bytearray(len(u) * 2)
res[::2] = u
res[1::2] = l
str(res)
   ....: 
100 loops, best of 3: 2.68 ms per loop

2
list? 필요로하지 않는됩니다
카퍼필드

1
내 테스트에 따르면 중개자 목록을 만드는 데 시간을 낭비하고 반복자를 사용하는 목적을 무너 뜨립니다. Timeit는 "".join(list(...))나에게 6.715280318699769을주고 timeit은 "".join(starmap(...))나에게 6.46332361384313 제공
카퍼필드

1
그렇다면 기계에 의존하는 것은 무엇입니까 ?? 테스트를 어디에서 실행하든 동일한 결과 "".join(list(starmap(add, izip(l1,l2))))"".join(starmap(add, izip(l1,l2))). 나는 내 컴퓨터에서 파이썬 2.7.11과 파이썬 3.5.1에서 테스트를 실행한다. 심지어 www.python.org 의 가상 콘솔에서도 파이썬 3.4.3을 사용하고 모두 똑같이 말하고 몇 번 실행하고 항상 같은
카퍼필드

내가 읽고 내가 보는 것은 그것이 당신이 그것을 전달하는 것에 관계없이 버퍼 변수에 항상 내부적으로 목록을 작성한다는 것입니다. 그래서 더 많은 이유는 목록을 제공하지 않습니다
Copperfield

@Copperfield, 목록 호출에 대해 이야기하거나 목록을 전달합니까?
Padraic Cunningham

12

mapoperator.add다음을 사용하여이 작업을 수행 할 수도 있습니다 .

from operator import add

u = 'AAAAA'
l = 'aaaaa'

s = "".join(map(add, u, l))

출력 :

'AaAaAaAaAa'

어떤 맵 않는 것은 처음부터 반복 가능한 모든 요소 걸립니다 u번째 반복 가능한로부터 첫번째 요소를 l첫 번째 인수로서 기능하고 적용 add. 그런 다음 조인은 그들을 조인합니다.


9

Jim의 대답은 훌륭하지만 몇 가지 수입품에 신경 쓰지 않는다면 제가 가장 좋아하는 옵션이 있습니다.

from functools import reduce
from operator import add

reduce(add, map(add, u, l))

7
그는 대부분의 Haskellic이 아니라 대부분의 Pythonic이라고 말했습니다.)
Curt

7

이러한 제안의 대부분은 문자열 길이가 같다고 가정합니다. 아마도 모든 합리적인 사용 사례를 다룰 수 있지만 적어도 나에게는 길이가 다른 문자열도 수용하고 싶을 것 같습니다. 아니면 메시가 다음과 같이 작동해야한다고 생각하는 유일한 사람입니까?

u = "foobar"
l = "baz"
mesh(u,l) = "fboaozbar"

이를 수행하는 한 가지 방법은 다음과 같습니다.

def mesh(a,b):
    minlen = min(len(a),len(b))
    return "".join(["".join(x+y for x,y in zip(a,b)),a[minlen:],b[minlen:]])

5

나는 두 개의를 사용하는 것을 좋아하는데 for, 변수 이름은 무슨 일이 일어나고 있는지에 대한 힌트 / 알림을 줄 수 있습니다.

"".join(char for pair in zip(u,l) for char in pair)

4

다른 기본적인 접근 방식을 추가하기 만하면됩니다.

st = ""
for char in u:
    st = "{0}{1}{2}".format( st, char, l[ u.index( char ) ] )

4

O (1) 노력으로 n 문자열을 처리하기 위해 여기에서 이중 목록 이해 답변을 고려하지 않는 것이 비단 식적이라고 느낍니다.

"".join(c for cs in itertools.zip_longest(*all_strings) for c in cs)

all_strings인터리브하려는 문자열 목록은 어디에 있습니까 ? 귀하의 경우 all_strings = [u, l]. 전체 사용 예는 다음과 같습니다.

import itertools
a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
b = 'abcdefghijklmnopqrstuvwxyz'
all_strings = [a,b]
interleaved = "".join(c for cs in itertools.zip_longest(*all_strings) for c in cs)
print(interleaved)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'

많은 답변처럼 빠른가요? 아마 아니지만 간단하고 유연합니다. 또한 복잡성을 너무 많이 추가하지 않으면 허용되는 답변보다 약간 빠릅니다 (일반적으로 문자열 추가는 파이썬에서 약간 느립니다).

In [7]: l1 = 'A' * 1000000; l2 = 'a' * 1000000;

In [8]: %timeit "".join(a + b for i, j in zip(l1, l2))
1 loops, best of 3: 227 ms per loop

In [9]: %timeit "".join(c for cs in zip(*(l1, l2)) for c in cs)
1 loops, best of 3: 198 ms per loop

하지만 여전히 가장 빠른 답변만큼 빠르지는 않습니다. 동일한 데이터와 컴퓨터에서
50.3ms를 얻었습니다

3

현재 선도적 인 솔루션보다 잠재적으로 더 빠르고 짧습니다.

from itertools import chain

u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'

res = "".join(chain(*zip(u, l)))

속도 측면에서 전략은 가능한 한 C 수준에서 많은 일을하는 것입니다. 고르지 않은 문자열에 대해 동일한 zip_longest () 수정 사항이 있으며 chain ()과 동일한 모듈에서 나올 것이므로 거기에 너무 많은 포인트를 줄 수 없습니다!

그 과정에서 내가 생각 해낸 다른 솔루션 :

res = "".join(u[x] + l[x] for x in range(len(u)))

res = "".join(k + l[i] for i, k in enumerate(u))

3

1을 사용할 수 있습니다.iteration_utilities.roundrobin

u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'

from iteration_utilities import roundrobin
''.join(roundrobin(u, l))
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'

또는 ManyIterables동일한 패키지 의 클래스 :

from iteration_utilities import ManyIterables
ManyIterables(u, l).roundrobin().as_string()
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'

1 이것은 내가 작성한 타사 라이브러리에서 가져온 것입니다 iteration_utilities..


2

읽기 쉽고 쉬운 방법을 얻으려면 zip ()을 사용합니다.

result = ''
for cha, chb in zip(u, l):
    result += '%s%s' % (cha, chb)

print result
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.