Lặp lại chuỗi đến độ dài nhất định


204

Một cách hiệu quả để lặp lại một chuỗi đến một độ dài nhất định là gì? Ví dụ:repeat('abc', 7) -> 'abcabca'

Đây là mã hiện tại của tôi:

def repeat(string, length):
    cur, old = 1, string
    while len(string) < length:
        string += old[cur-1]
        cur = (cur+1)%len(old)
    return string

Có cách nào tốt hơn (nhiều pythonic) để làm điều này không? Có thể sử dụng danh sách hiểu?

Câu trả lời:


73
def repeat_to_length(string_to_expand, length):
   return (string_to_expand * ((length/len(string_to_expand))+1))[:length]

Đối với python3:

def repeat_to_length(string_to_expand, length):
    return (string_to_expand * (int(length/len(string_to_expand))+1))[:length]

5
Có vẻ như điều này là lợi dụng sự phân chia số nguyên. Không cần phải có //trong Python 3 sao? Hoặc bỏ +1và sử dụng một cuộc gọi rõ ràng đến một chức năng trần sẽ đủ. Ngoài ra, một lưu ý: chuỗi được tạo thực sự có sự lặp lại thêm khi nó chia đều; phần phụ bị cắt bởi mối nối. Điều đó làm tôi bối rối lúc đầu.
jpmc26

int()thực hiện điều tương tự ở đây, nhưng phải, //có thể nhanh hơn về mặt kính hiển vi, bởi vì nó phân chia & sàn trong một lệnh thay vì hai.
Doyousketch2

667

Câu trả lời của Jason Scheirer là đúng nhưng có thể sử dụng một số giải trình khác.

Trước hết, để lặp lại một chuỗi số nguyên số lần, bạn có thể sử dụng phép nhân quá tải:

>>> 'abc' * 7
'abcabcabcabcabcabcabc'

Vì vậy, để lặp lại một chuỗi cho đến khi nó dài ít nhất bằng độ dài bạn muốn, bạn tính toán số lần lặp lại thích hợp và đặt nó ở phía bên phải của toán tử nhân:

def repeat_to_at_least_length(s, wanted):
    return s * (wanted//len(s) + 1)

>>> repeat_to_at_least_length('abc', 7)
'abcabcabc'

Sau đó, bạn có thể cắt nó theo độ dài chính xác mà bạn muốn với một lát mảng:

def repeat_to_length(s, wanted):
    return (s * (wanted//len(s) + 1))[:wanted]

>>> repeat_to_length('abc', 7)
'abcabca'

Ngoài ra, như được đề xuất trong câu trả lời của Pillmod mà có lẽ không ai cuộn xuống đủ xa để chú ý nữa, bạn có thể sử dụng divmodđể tính số lần lặp lại đầy đủ cần thiết và số lượng ký tự phụ, cùng một lúc:

def pillmod_repeat_to_length(s, wanted):
    a, b = divmod(wanted, len(s))
    return s * a + s[:b]

Cái nào tốt hơn? Hãy để điểm chuẩn cho nó:

>>> import timeit
>>> timeit.repeat('scheirer_repeat_to_length("abcdefg", 129)', globals=globals())
[0.3964178159367293, 0.32557755894958973, 0.32851039397064596]
>>> timeit.repeat('pillmod_repeat_to_length("abcdefg", 129)', globals=globals())
[0.5276265419088304, 0.46511475392617285, 0.46291469305288047]

Vì vậy, phiên bản của Pillmod chậm hơn 40%, điều này quá tệ, vì cá nhân tôi nghĩ nó dễ đọc hơn nhiều. Có một số lý do có thể cho việc này, bắt đầu với việc biên dịch các hướng dẫn mã byte thêm khoảng 40%.

Lưu ý: những ví dụ này sử dụng //toán tử new-ish để cắt bớt số nguyên. Đây thường được gọi là tính năng Python 3, nhưng theo PEP 238 , nó đã được giới thiệu hoàn toàn trong Python 2.2. Bạn chỉ phải sử dụng nó trong Python 3 (hoặc trong các mô-đun có from __future__ import division) nhưng bạn có thể sử dụng nó bất kể.


8
Không, OP muốn kết quả có độ dài 7 (không phải là bội số của 3).
IanS

1
Tôi hơi mâu thuẫn vì đây không phải là câu trả lời đúng cho OP nhưng là câu trả lời đúng cho tôi và 489 người khác ...
Matt Fletcher

2
@MattFletcher Bạn vừa đẩy tôi qua dòng từ "Tôi phải viết lại câu này như một người giải thích câu trả lời được chấp nhận" thành "Tôi sẽ viết lại điều này ..." ;-)
zwol 16/03/18



14
from itertools import cycle, islice
def srepeat(string, n):
   return ''.join(islice(cycle(string), n))

Đây là những gì tôi sử dụng khi tôi chỉ cần lặp qua chuỗi (sau đó không cần tham gia). Hãy để các thư viện python làm công việc.
wihlke

7

Có lẽ không phải là giải pháp hiệu quả nhất, nhưng chắc chắn ngắn & đơn giản:

def repstr(string, length):
    return (string * length)[0:length]

repstr("foobar", 14)

Cung cấp "foobarfoobarfo". Một điều về phiên bản này là nếu độ dài <len (chuỗi) thì chuỗi đầu ra sẽ bị cắt ngắn. Ví dụ:

repstr("foobar", 3)

Cho "foo".

Chỉnh sửa: thực sự làm tôi ngạc nhiên, điều này nhanh hơn giải pháp hiện được chấp nhận (chức năng 'repeat_to_length'), ít nhất là trên các chuỗi ngắn:

from timeit import Timer
t1 = Timer("repstr('foofoo', 30)", 'from __main__ import repstr')
t2 = Timer("repeat_to_length('foofoo', 30)", 'from __main__ import repeat_to_length')
t1.timeit()  # gives ~0.35 secs
t2.timeit()  # gives ~0.43 secs

Có lẽ nếu chuỗi dài hoặc độ dài rất cao (nghĩa là nếu độ lãng phí của string * lengthphần cao) thì nó sẽ hoạt động kém. Và trên thực tế, chúng ta có thể sửa đổi những điều trên để xác minh điều này:

from timeit import Timer
t1 = Timer("repstr('foofoo' * 10, 3000)", 'from __main__ import repstr')
t2 = Timer("repeat_to_length('foofoo' * 10, 3000)", 'from __main__ import repeat_to_length')
t1.timeit()  # gives ~18.85 secs
t2.timeit()  # gives ~1.13 secs

1
Bạn có thể thêm một chuyển đổi giữa hai phiên bản dựa trên độ dài đầu vào và đầu ra để tối ưu hóa tối đa.
Nhà vật lý điên

6

Làm thế nào về string * (length / len(string)) + string[0:(length % len(string))]


length / len(string)cần phải được bao bọc trong ngoặc đơn và bạn đang thiếu cái cuối cùng ].
MikeWyatt

1
Theo tôi, dễ đọc nhất / trực quan nhất. Tôi nghĩ bạn cần sử dụng //để phân chia số nguyên trong Python 3. Trong 0mối nối là tùy chọn. (Tất nhiên là bắt buộc phải có dấu hai chấm.)
jpmc26

6

tôi sử dụng cái này

def extend_string(s, l):
    return (s*l)[:l]

5

Không phải là không có đủ câu trả lời cho câu hỏi này, nhưng có một chức năng lặp lại; chỉ cần lập một danh sách và sau đó tham gia đầu ra:

from itertools import repeat

def rep(s,n):
  ''.join(list(repeat(s,n))

Điều này không trả lời câu hỏi. Cái này lặp lại chuỗi X lần, nó không lặp lại cho đến khi X dài. Eg "abc", 4sẽ mong đợi "abca". Điều này sẽ tạo raabcabcabcabc
Marcus Lind

3

Yay đệ quy!

def trunc(s,l):
    if l > 0:
        return s[:l] + trunc(s, l - len(s))
    return ''

Sẽ không mở rộng mãi mãi, nhưng nó tốt cho các chuỗi nhỏ hơn. Và nó thật đẹp.

Tôi thừa nhận tôi vừa đọc Little Schemer và tôi thích đệ quy ngay bây giờ.


1

Đây là một cách để làm điều đó bằng cách hiểu danh sách, mặc dù nó ngày càng lãng phí khi độ dài của rptchuỗi tăng lên.

def repeat(rpt, length):
    return ''.join([rpt for x in range(0, (len(rpt) % length))])[:length]

0

Một aproach FP khác:

def repeat_string(string_to_repeat, repetitions):
    return ''.join([ string_to_repeat for n in range(repetitions)])

0
def extended_string (word, length) :

    extra_long_word = word * (length//len(word) + 1)
    required_string = extra_long_word[:length]
    return required_string

print(extended_string("abc", 7))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.