Ulangi string hingga panjang tertentu

204

Apa cara yang efisien untuk mengulang string ke panjang tertentu? Misalnya:repeat('abc', 7) -> 'abcabca'

Ini kode saya saat ini:

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

Apakah ada cara yang lebih baik (lebih pythonic) untuk melakukan ini? Mungkin menggunakan pemahaman daftar?

John Howard
sumber

Jawaban:

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

Untuk python3:

def repeat_to_length(string_to_expand, length):
    return (string_to_expand * (int(length/len(string_to_expand))+1))[:length]
Jason Scheirer
sumber
5
Sepertinya ini mengambil keuntungan dari pembagian integer. Bukankah itu harus //dalam Python 3? Atau menjatuhkan +1dan menggunakan panggilan eksplisit ke fungsi langit-langit sudah cukup. Juga, catatan: string yang dihasilkan sebenarnya memiliki pengulangan ekstra ketika membaginya secara merata; ekstra akan terputus oleh sambungan. Itu membingungkan saya pada awalnya.
jpmc26
int()melakukan hal yang sama di sini, tapi ya, //mungkin secara mikroskopis lebih cepat, karena ia membagi & lantai dalam satu perintah, bukan dua.
Doyousketch2
667

Jawaban Jason Scheirer benar tetapi bisa menggunakan penjelasan lebih lanjut.

Pertama, untuk mengulangi string bilangan bulat beberapa kali, Anda dapat menggunakan perkalian yang kelebihan beban:

>>> 'abc' * 7
'abcabcabcabcabcabcabc'

Jadi, ulangi string hingga setidaknya sepanjang yang Anda inginkan, Anda menghitung jumlah pengulangan yang tepat dan meletakkannya di sisi kanan operator perkalian:

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

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

Kemudian, Anda bisa memotongnya ke panjang persis yang Anda inginkan dengan irisan array:

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

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

Atau, seperti yang disarankan dalam jawaban pillmod ini yang mungkin gulungan tidak ada turun cukup jauh dengan pemberitahuan lagi, Anda dapat menggunakan divmoduntuk menghitung jumlah pengulangan penuh dibutuhkan, dan jumlah karakter tambahan, sekaligus:

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

Mana yang lebih baik? Mari kita membandingkannya:

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

Jadi, versi pillmod kira-kira 40% lebih lambat, yang terlalu buruk, karena secara pribadi saya pikir itu jauh lebih mudah dibaca. Ada beberapa kemungkinan alasan untuk ini, mulai dengan kompilasi hingga sekitar 40% lebih banyak instruksi bytecode.

Catatan: contoh-contoh ini menggunakan //operator new-ish untuk memotong divisi integer. Ini sering disebut fitur Python 3, tetapi menurut PEP 238 , itu diperkenalkan sepanjang jalan kembali di Python 2.2. Anda hanya perlu menggunakannya di Python 3 (atau modul yang ada from __future__ import division) tetapi Anda dapat menggunakannya.

zwol
sumber
8
Tidak, OP ingin hasil dengan panjang 7 (yang bukan kelipatan 3).
IanS
1
Saya agak berkonflik karena ini bukan jawaban yang benar untuk OP tetapi adalah jawaban yang benar untuk saya dan 489 orang lain ...
Matt Fletcher
2
@MattFletcher Anda baru saja mendorong saya melewati garis dari "Saya harus menulis ulang ini sebagai penjelasan dari jawaban yang diterima" menjadi "Saya akan menulis ulang ini ..." ;-)
zwol
60

Ini sangat pythonic:

newstring = 'abc'*5
print newstring[0:6]
Helen K
sumber
14
0:7jika Anda ingin 7 karakter seperti OP.
Fisikawan Gila
34
def rep(s, m):
    a, b = divmod(m, len(s))
    return s * a + s[:b]
pembuat pil
sumber
14
from itertools import cycle, islice
def srepeat(string, n):
   return ''.join(islice(cycle(string), n))
kennytm
sumber
Ini adalah apa yang saya gunakan ketika saya hanya perlu mengulangi string (tidak perlu bergabung saat itu). Biarkan pustaka python melakukan pekerjaan.
wihlke
7

Mungkin bukan solusi yang paling efisien, tetapi tentu saja singkat & sederhana:

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

repstr("foobar", 14)

Memberi "foobarfoobarfo". Satu hal tentang versi ini adalah jika panjang <len (string) maka string output akan terpotong. Sebagai contoh:

repstr("foobar", 3)

Memberi "foo".

Sunting: sebenarnya mengejutkan saya, ini lebih cepat daripada solusi yang saat ini diterima (fungsi 'repeat_to_length'), setidaknya pada string pendek:

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

Agaknya jika tali itu panjang, atau panjangnya sangat tinggi (yaitu, jika pemborosan string * lengthbagian itu tinggi) maka itu akan berkinerja buruk. Dan sebenarnya kita dapat memodifikasi hal di atas untuk memverifikasi ini:

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
Adam Parkin
sumber
1
Anda dapat menambahkan sakelar antara dua versi berdasarkan panjang input dan output untuk optimalisasi maksimum.
Fisikawan Gila
6

Bagaimana tentang string * (length / len(string)) + string[0:(length % len(string))]

murgatroid99
sumber
length / len(string)harus menjadi pembungkus dalam tanda kurung, dan Anda melewatkan yang terakhir ].
MikeWyatt
1
Yang paling mudah dibaca / intuitif sejauh ini, menurut saya. Saya pikir Anda perlu menggunakan //untuk pembagian integer di Python 3. The 0dalam sambatan adalah opsional. (Usus besar diperlukan, tentu saja.)
jpmc26
6

saya menggunakan ini:

def extend_string(s, l):
    return (s*l)[:l]
김민준
sumber
5

Bukan berarti belum ada jawaban yang cukup untuk pertanyaan ini, tetapi ada fungsi berulang; hanya perlu membuat daftar dan kemudian bergabung dengan output:

from itertools import repeat

def rep(s,n):
  ''.join(list(repeat(s,n))
Amy Platt
sumber
Ini tidak menjawab pertanyaan. Yang ini mengulangi string X kali, itu tidak mengulanginya sampai panjang X. Misalnya "abc", 4harapkan "abca". Ini akan membuatabcabcabcabc
Marcus Lind
3

Rekursi Yay!

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

Tidak akan skala selamanya, tapi itu bagus untuk string yang lebih kecil. Dan itu cantik.

Saya akui saya baru saja membaca Little Schemer dan saya suka rekursi sekarang.

Triptych
sumber
1

Ini adalah salah satu cara untuk melakukannya menggunakan pemahaman daftar, meskipun semakin boros karena panjang rptstring meningkat.

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

Pendekatan FP lain:

def repeat_string(string_to_repeat, repetitions):
    return ''.join([ string_to_repeat for n in range(repetitions)])
Aleš Kotnik
sumber
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))
Aditya Verma
sumber