Powtórz ciąg do określonej długości

204

Jaki jest skuteczny sposób na powtórzenie łańcucha do określonej długości? Na przykład:repeat('abc', 7) -> 'abcabca'

Oto mój obecny kod:

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

Czy istnieje lepszy (bardziej pythoniczny) sposób na zrobienie tego? Może używasz rozumienia listy?

John Howard
źródło

Odpowiedzi:

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

Dla python3:

def repeat_to_length(string_to_expand, length):
    return (string_to_expand * (int(length/len(string_to_expand))+1))[:length]
Jason Scheirer
źródło
5
Wygląda na to, że korzysta z podziału na liczby całkowite. Czy to nie musi być //w Pythonie 3? Albo +1wystarczy upuszczenie i użycie jawnego wywołania funkcji pułapu. Również uwaga: wygenerowany ciąg ma w rzeczywistości dodatkowe powtórzenie, gdy dzieli się równomiernie; dodatkowe zostaje odcięte przez połączenie. To mnie na początku pomieszało.
jpmc26
int()robi to samo tutaj, ale tak, //może być mikroskopowo szybszy, ponieważ dzieli i piętro w jednym poleceniu zamiast dwóch.
Doyousketch2
667

Odpowiedź Jasona Scheirera jest prawidłowa, ale przydałaby się jeszcze bardziej ekspozycja.

Po pierwsze, aby powtórzyć ciąg liczbowy liczbę całkowitą, możesz użyć przeciążonego mnożenia:

>>> 'abc' * 7
'abcabcabcabcabcabcabc'

Tak więc, aby powtórzyć ciąg, aż będzie on co najmniej tak długi, jak chcesz, oblicz odpowiednią liczbę powtórzeń i umieść ją po prawej stronie tego operatora mnożenia:

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

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

Następnie możesz przyciąć go do odpowiedniej długości za pomocą wycinka tablicy:

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

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

Alternatywnie, jak zasugerowano w odpowiedzi pillmod, że prawdopodobnie nikt nie przewija się wystarczająco daleko, aby zauważyć, można użyć divmoddo obliczenia liczby pełnych wymaganych powtórzeń i liczby dodatkowych znaków naraz:

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

Który jest lepszy? Porównajmy to:

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

Tak więc wersja pillmod jest o około 40% wolniejsza, co jest zbyt złe, ponieważ osobiście uważam, że jest znacznie bardziej czytelna. Istnieje kilka możliwych przyczyn, zaczynając od kompilacji do około 40% więcej instrukcji kodu bajtowego.

Uwaga: w tych przykładach zastosowano //operator nowej isy do obcinania podziału na liczby całkowite. Jest to często nazywane funkcją Python 3, ale zgodnie z PEP 238 zostało wprowadzone od samego początku w Python 2.2. Wystarczy tylko mieć go używać w Pythonie 3 (lub w modułach, które mają from __future__ import division), ale można go używać niezależnie.

zwol
źródło
8
Nie, OP chce, aby wynik miał długość 7 (która nie jest wielokrotnością liczby 3).
IanS
1
Jestem trochę skonfliktowany, ponieważ to nie jest poprawna odpowiedź dla OP, ale jest to poprawna odpowiedź dla mnie i 489 innych osób ...
Matt Fletcher
2
@MattFletcher Po prostu popchnął mnie na linii od „I powinien przepisać to jako explainer na odpowiedź Zaakceptowano” „I będzie przepisać to ...” ;-)
Zwol
60

To jest dość pytoniczne:

newstring = 'abc'*5
print newstring[0:6]
Helen K.
źródło
14
0:7jeśli chcesz 7 znaków, takich jak OP.
Szalony fizyk
34
def rep(s, m):
    a, b = divmod(m, len(s))
    return s * a + s[:b]
pillmuncher
źródło
14
from itertools import cycle, islice
def srepeat(string, n):
   return ''.join(islice(cycle(string), n))
kennytm
źródło
To jest to, czego używam, gdy muszę tylko iterować po łańcuchu (wtedy nie jest wymagane łączenie). Niech biblioteki python wykonają zadanie.
wihlke
7

Być może nie jest to najbardziej wydajne rozwiązanie, ale z pewnością krótkie i proste:

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

repstr("foobar", 14)

Daje „foobarfoobarfo”. Jedną rzeczą w tej wersji jest to, że jeśli długość <len (ciąg), to łańcuch wyjściowy zostanie obcięty. Na przykład:

repstr("foobar", 3)

Daje „foo”.

Edycja: ku mojemu zaskoczeniu jest to szybsze niż obecnie akceptowane rozwiązanie (funkcja „repeat_to_length”), przynajmniej na krótkich ciągach:

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

Przypuszczalnie, jeśli łańcuch string * lengthbyłby długi lub długość była bardzo wysoka (to znaczy, gdyby marnotrawstwo części było wysokie), to działałoby to słabo. W rzeczywistości możemy zmodyfikować powyższe, aby to zweryfikować:

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
źródło
1
Możesz dodać przełącznik między dwiema wersjami w oparciu o długości wejściowe i wyjściowe w celu uzyskania maksymalnej optymalizacji.
Szalony fizyk
6

Co powiesz na string * (length / len(string)) + string[0:(length % len(string))]

murgatroid99
źródło
length / len(string)musi być owinięty w nawiasie, a brakuje Ci ostatniego ].
MikeWyatt,
1
Moim zdaniem najbardziej czytelny / intuicyjny. Myślę, że musisz użyć //do dzielenia liczb całkowitych w Pythonie 3. 0Łączenie jest opcjonalne. (Oczywiście wymagany jest dwukropek.)
jpmc26
6

używam tego:

def extend_string(s, l):
    return (s*l)[:l]
김민준
źródło
5

Nie chodzi o to, że nie ma wystarczających odpowiedzi na to pytanie, ale istnieje funkcja powtarzania; wystarczy zrobić listę, a następnie dołączyć wynik:

from itertools import repeat

def rep(s,n):
  ''.join(list(repeat(s,n))
Amy Platt
źródło
To nie odpowiada na pytanie. Ten powtórzy ciąg X razy, nie powtórzy go aż do długości X. Np. Można "abc", 4się spodziewać "abca". To by stworzyłoabcabcabcabc
Marcus Lind
3

Tak, rekurencja!

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

Nie będzie skalować na zawsze, ale jest w porządku dla mniejszych strun. I to jest ładne.

Przyznaję, że właśnie przeczytałem Little Schemer i lubię teraz rekursję.

Tryptyk
źródło
1

Jest to jeden ze sposobów, aby to zrobić przy użyciu listy, chociaż jest to coraz bardziej marnotrawne wraz ze wzrostem długości rptłańcucha.

def repeat(rpt, length):
    return ''.join([rpt for x in range(0, (len(rpt) % length))])[:length]
vezult
źródło
0

Kolejne podejście do FP:

def repeat_string(string_to_repeat, repetitions):
    return ''.join([ string_to_repeat for n in range(repetitions)])
Aleš Kotnik
źródło
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
źródło