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?

John Howard
fuente

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]
Jason Scheirer
fuente
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.

zwol
fuente
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]
Helen K
fuente
14
0:7si quieres 7 caracteres como OP.
Físico loco
34
def rep(s, m):
    a, b = divmod(m, len(s))
    return s * a + s[:b]
Pillmuncher
fuente
14
from itertools import cycle, islice
def srepeat(string, n):
   return ''.join(islice(cycle(string), n))
kennytm
fuente
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
Adam Parkin
fuente
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))]

murgatroid99
fuente
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
6

yo uso esto:

def extend_string(s, l):
    return (s*l)[:l]
김민준
fuente
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))
Amy Platt
fuente
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.

Tríptico
fuente
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]
vezult
fuente
0

Otro enfoque de FP:

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