Dada una lista a, puedo cortarla haciendo:

a[start:end:step]

Sin embargo, esto es solo un corte lineal. Por ejemplo, me gustaría seleccionar los índices que son potencias de 2. Lamentablemente, esto no funciona:

a[slice(2**x for x in range(len(a))]

¿Hay alguna manera de evitar bucles cuando se requiere un corte no lineal?

EDITAR : Principalmente necesito esto para modificar la lista. P.ej.

a[*non-linear-slicing*] = [*list-with-new-values*]
1
Pigna 30 oct. 2017 a las 13:19

5 respuestas

La mejor respuesta

EDITAR : principalmente necesito esto para modificar la lista. P.ej.

a[*non-linear-slicing*] = [*list-with-new-values*]

Excelente. Entonces podemos usar esa lista de nuevos valores para impulsar esto. Manifestación:

>>> a = range(17)
>>> newvals = ['foo', 'bar', 'baz', 'qux']

>>> for i, val in enumerate(newvals):
        a[2**i] = val

>>> a
[0, 'foo', 'bar', 3, 'baz', 5, 6, 7, 'qux', 9, 10, 11, 12, 13, 14, 15, 16]

O si ya tiene los índices en una lista, use zip en lugar de enumerate:

>>> a = range(17)
>>> indexes = [1, 2, 4, 8]
>>> newvals = ['foo', 'bar', 'baz', 'qux']
>>> for i, val in zip(indexes, newvals):
        a[i] = val

>>> a
[0, 'foo', 'bar', 3, 'baz', 5, 6, 7, 'qux', 9, 10, 11, 12, 13, 14, 15, 16]

Y dado que acaba de mencionar que viene de R, tal vez quiera usar NumPy, que admite esa indexación:

>>> import numpy as np
>>> a = np.arange(13)
>>> a[[1, 2, 4, 8]] = [111, 222, 444, 888]
>>> a
array([  0, 111, 222,   3, 444,   5,   6,   7, 888,   9,  10,  11,  12])
1
Stefan Pochmann 30 oct. 2017 a las 15:34

Podrías usar la comprensión de la lista:

is_power_of_two = lambda num: num != 0 and ((num & (num - 1)) == 0)
print [item for i, item in enumerate(a) if is_power_of_two(i)]
2
Neo 30 oct. 2017 a las 10:27

¿Es esto lo que estas tratando de hacer?

a = [0,1,2,3,4,5,6,7,8,9]
a[1],a[2],a[4],a[8] = [11,12,14,18]
# a = [0,11,12,3,14,5,6,7,18,9]

No sé cómo construir a[1],a[2],a[4],a[8] de manera genérica, ya que las comprensiones o el generador no funcionarán, ya que no admiten la operación de asignación.

1
Adirio 30 oct. 2017 a las 11:11

No estoy segura de si esto es lo que quieres, pero puedes probar esto:

    # Example list
l = [1,2,3,4,5,6,7,8]

result = list(map(lambda y: l[y], list(filter(lambda x: (x>0 and (x & (x-1) == 0)),list(range(0,len(l)))))))
print(result)

El resultado es:

[2, 3, 5]

Pero si fuera usted, no lo usaría porque es apenas legible.

1
Vasilis G. 30 oct. 2017 a las 13:46

¿Por qué iterar sobre toda la lista, cuando solo necesita los elementos 2 ** i th? Lo que mejora la complejidad dramáticamente.

a = range(100)

def two_power_slice(lst):
    result = []
    for i in xrange(len(lst)):
        j = 2 ** i
        if j > len(lst) - 1:
            break
        result.append(lst[j])      
    return result

print two_power_slice(a)

Creo que también puedes usar el logaritmo para evitar usar if por completo.

Editar: Versión mejorada, errores corregidos.

def two_power_slice(lst):
    if not lst: return lst
    result = []
    for i in xrange(int(math.ceil(math.log(len(lst), 2)))):
        result.append(lst[2 ** i])      
    return result
1
Rockybilly 30 oct. 2017 a las 18:10