ทำซ้ำสตริงตามความยาวที่กำหนด


204

วิธีที่มีประสิทธิภาพในการทำซ้ำสตริงความยาวที่แน่นอนคืออะไร? เช่น:repeat('abc', 7) -> 'abcabca'

นี่คือรหัสปัจจุบันของฉัน:

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

มีวิธีที่ดีกว่า (pythonic) ในการทำเช่นนี้? อาจใช้ความเข้าใจในรายการ?

คำตอบ:


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

สำหรับ python3:

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

5
ดูเหมือนว่านี่เป็นการใช้ประโยชน์จากการหารจำนวนเต็ม นั่นไม่จำเป็นต้องอยู่//ใน Python 3 ใช่ไหม หรือวาง+1และใช้สายที่ชัดเจนไปยังฟังก์ชั่นเพดานจะพอเพียง นอกจากนี้หมายเหตุ: สตริงที่สร้างขึ้นจริง ๆ แล้วมีการทำซ้ำพิเศษเมื่อมันแบ่งเท่า ๆ กัน; ส่วนที่เหลือจะถูกตัดโดยรอยต่อ นั่นทำให้ฉันสับสนในตอนแรก
jpmc26

int()ทำสิ่งเดียวกันที่นี่ แต่ใช่แล้ว//อาจจะเร็วกว่าด้วยกล้องจุลทรรศน์เพราะมันจะแบ่ง & พื้นในหนึ่งคำสั่งแทนที่จะเป็นสอง
Doyousketch2

667

คำตอบของ Jason Scheirer นั้นถูกต้อง แต่สามารถใช้การอธิบายเพิ่มเติมได้บ้าง

ก่อนอื่นให้ทำซ้ำสตริงด้วยจำนวนเต็มคูณคุณสามารถใช้การคูณมากเกินไป:

>>> 'abc' * 7
'abcabcabcabcabcabcabc'

ดังนั้นในการทำซ้ำสตริงอย่างน้อยก็ตราบใดที่ความยาวคุณต้องการคุณจะคำนวณจำนวนการซ้ำที่เหมาะสมและวางไว้ทางด้านขวาของตัวดำเนินการคูณนั้น:

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

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

จากนั้นคุณสามารถตัดให้ได้ความยาวที่แน่นอนที่คุณต้องการด้วยชิ้นส่วนของอาร์เรย์:

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

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

อีกทางเลือกหนึ่งตามคำแนะนำของ pillmodที่อาจไม่มีใครเลื่อนลงมามากพอที่จะสังเกตเห็นอีกต่อไปคุณสามารถใช้divmodเพื่อคำนวณจำนวนการทำซ้ำเต็มจำนวนที่ต้องการและจำนวนอักขระพิเศษทั้งหมดในครั้งเดียว:

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

ไหนดีกว่ากัน ลองเปรียบเทียบกัน:

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

ดังนั้นเวอร์ชั่นของ Pillmod นั้นช้ากว่า 40% ซึ่งแย่มากเนื่องจากโดยส่วนตัวแล้วฉันคิดว่ามันอ่านง่ายกว่ามาก มีสาเหตุหลายประการที่เป็นไปได้นี้เริ่มต้นด้วยการรวบรวมเพื่อคำแนะนำ bytecode เพิ่มเติมประมาณ 40%

หมายเหตุ: ตัวอย่างเหล่านี้ใช้ตัวดำเนินการ new-ish //เพื่อตัดทอนการหารจำนวนเต็ม สิ่งนี้มักเรียกว่าฟีเจอร์ Python 3 แต่ตามPEP 238มันถูกนำกลับมาใช้ใหม่ใน Python 2.2 คุณจะต้องใช้มันใน Python 3 (หรือในโมดูลที่มีfrom __future__ import division) แต่คุณสามารถใช้ได้โดยไม่คำนึงถึง


8
ไม่ OP ต้องการผลลัพธ์ที่มีความยาว 7 (ซึ่งไม่ใช่ผลคูณของ 3)
IanS

1
ฉันบิตขัดแย้งเพราะนี้ไม่ได้เป็นคำตอบที่ถูกต้องสำหรับ OP แต่เป็นคำตอบที่ถูกต้องสำหรับผมและ 489 คนอื่น ๆ ...
แมตต์เฟลตเชอร์

2
@MattFletcher คุณเพิ่งผลักฉันข้ามเส้นจาก "ฉันควรจะเขียนใหม่นี้เป็นผู้อธิบายของคำตอบที่ยอมรับ" เป็น "ฉันจะเขียนใหม่นี้ ... " ;-)
zwol



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

นี่คือสิ่งที่ฉันใช้เมื่อฉันต้องการวนซ้ำสตริงเท่านั้น (ไม่จำเป็นต้องเข้าร่วม) ให้ห้องสมุดหลามทำงาน
wihlke

7

อาจไม่ใช่วิธีที่มีประสิทธิภาพที่สุด แต่สั้นและเรียบง่าย:

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

repstr("foobar", 14)

ให้ "foobarfoobarfo" สิ่งหนึ่งที่เกี่ยวกับรุ่นนี้คือถ้า length <len (string) สตริงผลลัพธ์จะถูกตัดทอน ตัวอย่างเช่น:

repstr("foobar", 3)

ให้ "foo"

แก้ไข: อันที่จริงแล้วฉันประหลาดใจนี่เร็วกว่าโซลูชันที่ยอมรับในปัจจุบัน (ฟังก์ชั่น 'repeat_to_length') อย่างน้อยในสตริงสั้น ๆ :

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

สันนิษฐานว่าถ้าสายยาวหรือยาวมาก (นั่นคือถ้าความสิ้นเปลืองของstring * lengthส่วนสูง) จากนั้นก็จะทำงานได้ไม่ดี และในความเป็นจริงเราสามารถแก้ไขด้านบนเพื่อตรวจสอบสิ่งนี้:

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
คุณสามารถเพิ่มสวิตช์ระหว่างสองเวอร์ชันโดยยึดตามความยาวอินพุตและเอาต์พุตเพื่อเพิ่มประสิทธิภาพสูงสุด
นักฟิสิกส์บ้า

6

เกี่ยวกับ string * (length / len(string)) + string[0:(length % len(string))]


length / len(string)]ความต้องการที่จะเป็นเสื้อคลุมในวงเล็บและคุณกำลังขาดหายไปสุดท้าย
MikeWyatt

1
ในความคิดของฉันอ่าน / ใช้งานง่ายที่สุด ฉันคิดว่าคุณต้องใช้//สำหรับการหารจำนวนเต็มใน Python 3 0ในการต่อเป็นทางเลือก (จำเป็นต้องใช้โคลอนแน่นอน)
jpmc26


5

ไม่ใช่ว่ายังไม่ได้รับคำตอบสำหรับคำถามนี้เพียงพอ แต่มีฟังก์ชั่นซ้ำ; เพียงแค่ต้องทำรายการแล้วเข้าร่วมผลลัพธ์:

from itertools import repeat

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

นี่ไม่ได้ตอบคำถาม อันนี้ซ้ำสตริง X ครั้งมันไม่ซ้ำจนกว่าความยาว X เช่นจะคาดหวัง"abc", 4 "abca"สิ่งนี้จะสร้างabcabcabcabc
Marcus Lind

3

การเรียกซ้ำย่ะ!

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

จะไม่ขยายขนาดตลอดไป แต่ใช้ได้กับสตริงที่เล็กกว่า และมันก็สวย

ฉันยอมรับว่าฉันอ่าน Little Schemer และฉันชอบการสอบถามซ้ำตอนนี้


1

นี่เป็นวิธีหนึ่งในการทำโดยใช้ความเข้าใจของรายการแม้ว่าจะสิ้นเปลืองมากขึ้นเมื่อความยาวของrptสตริงเพิ่มขึ้น

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


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))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.