Repita la cuerda a cierta longitud


205

¿Cuál es una manera eficiente de repetir una cadena a una determinada longitud? P.ej:repeat('abc', 7) -> 'abcabca'

Aquí está mi código actual:

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

¿Hay una manera mejor (más pitónica) de hacer esto? Tal vez utilizando la comprensión de la lista?

Respuestas:


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

Para python3:

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

55
Parece que esto se está aprovechando de la división de enteros. ¿No necesita eso estar //en Python 3? O dejar caer +1y usar una llamada explícita a una función de techo sería suficiente. Además, una nota: la cadena generada en realidad tiene una repetición adicional cuando se divide de manera uniforme; el empalme corta el extra. Eso me confundió al principio.
jpmc26

int()hace lo mismo aquí, pero sí, //puede ser microscópicamente más rápido, porque hace la división y el piso en un comando en lugar de dos.
Doyousketch2

667

La respuesta de Jason Scheirer es correcta, pero podría usar un poco más de exposición.

En primer lugar, para repetir una cadena un número entero de veces, puede usar la multiplicación sobrecargada:

>>> 'abc' * 7
'abcabcabcabcabcabcabc'

Entonces, para repetir una cadena hasta que sea al menos tan larga como la longitud que desea, calcule el número apropiado de repeticiones y colóquelo en el lado derecho de ese operador de multiplicación:

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

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

Luego, puede recortarlo a la longitud exacta que desee con un segmento de matriz:

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

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

Alternativamente, como se sugiere en la respuesta de pillmod de que probablemente nadie se desplaza lo suficiente como para notarlo, puede usar divmodpara calcular la cantidad de repeticiones completas necesarias y la cantidad de caracteres adicionales, todo a la vez:

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

¿Cual es mejor? Vamos a compararlo:

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

Entonces, la versión de pillmod es un 40% más lenta, lo cual es una lástima, ya que personalmente creo que es mucho más legible. Hay varias razones posibles para esto, comenzando con la compilación de aproximadamente un 40% más de instrucciones de código de bytes.

Nota: estos ejemplos usan el //operador new-ish para truncar la división de enteros. Esto a menudo se llama una característica de Python 3, pero de acuerdo con PEP 238 , se introdujo en Python 2.2. Solo tiene que usarlo en Python 3 (o en módulos que lo tengan from __future__ import division), pero puede usarlo independientemente.


8
No, OP quiere que el resultado sea de longitud 7 (que no es múltiplo de 3).
IanS

1
Estoy un poco en conflicto porque esta no es la respuesta correcta para OP pero es la respuesta correcta para mí y para otras 489 personas ...
Matt Fletcher

2
@MattFletcher Sólo me ha empujado sobre la línea de "yo debería volver a escribir esto como un explicador de la respuesta aceptada" a "Me voy a volver a escribir esto ..." ;-)
Zwol

60

Esto es bastante pitónico:

newstring = 'abc'*5
print newstring[0:6]

14
0:7si quieres 7 caracteres como OP.
Físico loco


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

Esto es lo que uso cuando solo necesito iterar sobre la cadena (no se necesita unir entonces). Deje que las bibliotecas de Python hagan el trabajo.
wihlke

7

Quizás no sea la solución más eficiente, pero ciertamente corta y simple:

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

repstr("foobar", 14)

Da "foobarfoobarfo". Una cosa sobre esta versión es que si length <len (string) entonces la cadena de salida se truncará. Por ejemplo:

repstr("foobar", 3)

Da "foo".

Editar: para mi sorpresa, esto es más rápido que la solución actualmente aceptada (la función 'repeat_to_length'), al menos en cadenas cortas:

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

Presumiblemente, si la cuerda era larga, o la longitud era muy alta (es decir, si el desperdicio de la string * lengthpieza era alto), entonces funcionaría mal. Y, de hecho, podemos modificar lo anterior para verificar esto:

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
Puede agregar un interruptor entre las dos versiones en función de las longitudes de entrada y salida para una optimización máxima.
Mad Physicist

6

Qué tal si string * (length / len(string)) + string[0:(length % len(string))]


length / len(string)debe estar entre paréntesis, y te estás perdiendo el último ].
MikeWyatt

1
El más legible / intuitivo hasta ahora, en mi opinión. Creo que debe usar //para la división de enteros en Python 3. El 0empalme es opcional. (Se requieren dos puntos, por supuesto).
jpmc26


5

No es que no haya habido suficientes respuestas a esta pregunta, pero hay una función de repetición; solo necesito hacer una lista de y luego unirme a la salida:

from itertools import repeat

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

Esto no responde la pregunta. Éste repite la cadena X veces, no la repite hasta la longitud X. Por ejemplo "abc", 4, esperaría "abca". Esto crearíaabcabcabcabc
Marcus Lind el

3

Yay recursión!

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

No se escalará para siempre, pero está bien para cadenas más pequeñas. Y es bonito

Admito que acabo de leer Little Schemer y me gusta la recursión en este momento.


1

Esta es una forma de hacerlo utilizando una lista de comprensión, aunque es cada vez más derrochador a medida que aumenta la longitud de la rptcadena.

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

0

Otro enfoque de FP:

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))
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.