Cách khó nhất để nối hai dây lại với nhau là gì?
Ví dụ:
Đầu vào:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Đầu ra:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Cách khó nhất để nối hai dây lại với nhau là gì?
Ví dụ:
Đầu vào:
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
Đầu ra:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Câu trả lời:
Đối với tôi, cách khó hiểu nhất * là cách sau đây thực hiện khá nhiều điều tương tự nhưng sử dụng +
toán tử để nối các ký tự riêng lẻ trong mỗi chuỗi:
res = "".join(i + j for i, j in zip(u, l))
print(res)
# 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Nó cũng nhanh hơn so với sử dụng hai join()
cuộc gọi:
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
Các phương pháp tiếp cận nhanh hơn tồn tại, nhưng chúng thường làm xáo trộn mã.
Lưu ý: Nếu hai chuỗi đầu vào không cùng độ dài thì chuỗi dài hơn sẽ bị cắt ngắn khi zip
ngừng lặp lại ở cuối chuỗi ngắn hơn. Trong trường hợp này, thay vì zip
một, nên sử dụng zip_longest
( izip_longest
trong Python 2) từ itertools
mô-đun để đảm bảo rằng cả hai chuỗi đều được sử dụng hết.
* Để lấy một trích dẫn từ Zen của Python : Tính dễ đọc .
Pythonic = khả năng đọc đối với tôi; i + j
được phân tích cú pháp trực quan dễ dàng hơn, ít nhất là đối với mắt tôi.
"".join([i + j for i, j in zip(l1, l2)])
và nó chắc chắn sẽ là nhanh nhất
"".join(map("".join, zip(l1, l2)))
thậm chí còn nhanh hơn, mặc dù không nhất thiết phải nhiều pythonic hơn.
Cách khác:
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
print(''.join(res))
Đầu ra:
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
Có vẻ như nó nhanh hơn:
%%timeit
res = [''] * len(u) * 2
res[::2] = u
res[1::2] = l
''.join(res)
100000 loops, best of 3: 4.75 µs per loop
hơn giải pháp nhanh nhất cho đến nay:
%timeit "".join(list(chain.from_iterable(zip(u, l))))
100000 loops, best of 3: 6.52 µs per loop
Cũng cho các chuỗi lớn hơn:
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
Python 3.5.1.
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijkl'
zip()
tương đương)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))
Đầu ra:
AaBbCcDdEeFfGgHhIiJjKkLl
itertools.zip_longest(fillvalue='')
tương đương)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))
Đầu ra:
AaBbCcDdEeFfGgHhIiJjKkLlMNOPQRSTUVWXYZ
Với join()
và zip()
.
>>> ''.join(''.join(item) for item in zip(u,l))
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
''.join(itertools.chain.from_iterable(zip(u, l)))
zip
danh sách ngắn hơn danh sách kia, vì nó sẽ dừng khi danh sách ngắn hơn đã được lặp lại hoàn toàn.
itertools.zip_longest
có thể được sử dụng nếu nó trở thành một vấn đề.
Trên Python 2, cho đến nay cách nhanh hơn để thực hiện mọi việc, với tốc độ gấp ~ 3 lần tốc độ cắt danh sách đối với chuỗi nhỏ và ~ 30x đối với chuỗi dài, là
res = bytearray(len(u) * 2)
res[::2] = u
res[1::2] = l
str(res)
Tuy nhiên, điều này sẽ không hoạt động trên Python 3. Bạn có thể thực hiện một cái gì đó như
res = bytearray(len(u) * 2)
res[::2] = u.encode("ascii")
res[1::2] = l.encode("ascii")
res.decode("ascii")
nhưng đến lúc đó bạn đã mất lợi ích khi cắt danh sách đối với các chuỗi nhỏ (tốc độ vẫn gấp 20 lần đối với các chuỗi dài) và điều này thậm chí không hoạt động đối với các ký tự không phải ASCII.
FWIW, nếu bạn đang làm điều này trên các chuỗi lớn và cần mọi chu kỳ và vì lý do nào đó phải sử dụng chuỗi Python ... đây là cách thực hiện:
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")
Vỏ đặc biệt trong trường hợp phổ biến của các loại nhỏ hơn cũng sẽ hữu ích. FWIW, tốc độ này chỉ gấp 3 lần tốc độ cắt danh sách đối với các chuỗi dài và chậm hơn từ 4 đến 5 đối với các chuỗi nhỏ.
Dù bằng cách nào, tôi thích các join
giải pháp hơn, nhưng vì thời gian đã được đề cập ở những nơi khác, tôi nghĩ tôi cũng có thể tham gia.
Nếu bạn muốn một cách nhanh nhất, bạn có thể kết hợp itertools với operator.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
Nhưng kết hợp lại izip
và chain.from_iterable
nhanh hơn
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
Cũng có một sự khác biệt đáng kể giữa
chain(*
và chain.from_iterable(...
.
In [5]: timeit "".join(chain(*izip(l1, l2)))
1 loops, best of 3: 212 ms per loop
Không có thứ gì gọi là trình tạo với phép nối, việc truyền một cái sẽ luôn chậm hơn vì python trước tiên sẽ xây dựng danh sách bằng cách sử dụng nội dung vì nó thực hiện hai lần chuyển dữ liệu, một để tìm ra kích thước cần thiết và một để thực sự làm nối mà sẽ không thể sử dụng trình tạo:
/* 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.
*/
Ngoài ra, nếu bạn có các chuỗi độ dài khác nhau và bạn không muốn mất dữ liệu, bạn có thể sử dụng 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'
Đối với python 3, nó được gọi là zip_longest
Nhưng đối với python2, gợi ý của veedrac cho đến nay là nhanh nhất:
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
list
?? là không cần thiết
"".join(list(...))
cho tôi 6,715280318699769 và timeit sự "".join(starmap(...))
cho tôi 6,46332361384313
"".join(list(starmap(add, izip(l1,l2))))
chậm hơn "".join(starmap(add, izip(l1,l2)))
. Tôi chạy thử nghiệm trong máy của mình bằng python 2.7.11 và python 3.5.1 ngay cả trong bảng điều khiển ảo của www.python.org với python 3.4.3 và tất cả đều nói như nhau và tôi chạy nó một vài lần và luôn giống nhau
Bạn cũng có thể làm điều này bằng cách sử dụng map
và operator.add
:
from operator import add
u = 'AAAAA'
l = 'aaaaa'
s = "".join(map(add, u, l))
Đầu ra :
'AaAaAaAaAa'
Bản đồ làm gì là nó lấy mọi phần tử từ có thể lặp đầu tiên u
và các phần tử đầu tiên từ có thể lặp thứ hai l
và áp dụng hàm được cung cấp làm đối số đầu tiên add
. Sau đó tham gia chỉ cần tham gia cùng họ.
Rất nhiều đề xuất này giả sử các chuỗi có độ dài bằng nhau. Có thể điều đó bao gồm tất cả các trường hợp sử dụng hợp lý, nhưng ít nhất đối với tôi có vẻ như bạn cũng có thể muốn chứa các chuỗi có độ dài khác nhau. Hay tôi là người duy nhất nghĩ rằng lưới sẽ hoạt động giống như thế này:
u = "foobar"
l = "baz"
mesh(u,l) = "fboaozbar"
Một cách để làm điều này sẽ như sau:
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:]])
Tôi thích sử dụng hai for
s, tên biến có thể đưa ra gợi ý / nhắc nhở về những gì đang diễn ra:
"".join(char for pair in zip(u,l) for char in pair)
Cảm thấy hơi khó hiểu khi không xem xét câu trả lời trong danh sách kép ở đây, để xử lý chuỗi n với nỗ lực O (1):
"".join(c for cs in itertools.zip_longest(*all_strings) for c in cs)
đâu all_strings
là danh sách các chuỗi bạn muốn xen kẽ. Trong trường hợp của bạn all_strings = [u, l]
,. Một ví dụ sử dụng đầy đủ sẽ trông như thế này:
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'
Như nhiều câu trả lời, nhanh nhất? Có lẽ không, nhưng đơn giản và linh hoạt. Ngoài ra, không có quá nhiều phức tạp được thêm vào, điều này nhanh hơn một chút so với câu trả lời được chấp nhận (nói chung, việc thêm chuỗi hơi chậm trong python):
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
Có thể nhanh hơn và ngắn hơn so với giải pháp hàng đầu hiện tại:
from itertools import chain
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
res = "".join(chain(*zip(u, l)))
Chiến lược khôn ngoan về tốc độ là làm càng nhiều càng tốt ở cấp độ C. Cùng một bản sửa lỗi zip_longest () cho các chuỗi không đồng đều và nó sẽ ra khỏi cùng một mô-đun như chain (), vì vậy không thể cho tôi quá nhiều điểm ở đó!
Các giải pháp khác mà tôi đã đưa ra trong quá trình thực hiện:
res = "".join(u[x] + l[x] for x in range(len(u)))
res = "".join(k + l[i] for i, k in enumerate(u))
Bạn có thể sử dụng 1iteration_utilities.roundrobin
u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l = 'abcdefghijklmnopqrstuvwxyz'
from iteration_utilities import roundrobin
''.join(roundrobin(u, l))
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
hoặc ManyIterables
lớp từ cùng một gói:
from iteration_utilities import ManyIterables
ManyIterables(u, l).roundrobin().as_string()
# returns 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
1 Đây là từ một thư viện của bên thứ ba tôi đã viết: iteration_utilities
.