clojure sequence slice

Corte de secuencia idiomática en Clojure



partition clojure (4)

Cortar una secuencia es un "olor a código", una secuencia en general está diseñada para el acceso secuencial de los elementos.

Si va a hacer un montón de corte / concatenación, hay estructuras de datos mucho mejores disponibles, en particular, la implementación del vector RRB-Tree:

Esto soporta operaciones subvec y catvec muy eficientes.

En Python, hay una manera conveniente de tomar partes de una lista llamada "rebanar":

a = [1,2,3,4,5,6,7,8,9,10] # ≡ a = range(1,10) a[:3] # get first 3 elements a[3:] # get all elements except the first 3 a[:-3] # get all elements except the last 3 a[-3:] # get last 3 elements a[3:7] # get 4 elements starting from 3rd (≡ from 3rd to 7th exclusive) a[3:-3] # get all elements except the first 3 and the last 3

Jugando con clojure.repl/doc en Clojure, encontré equivalentes para todos ellos, pero no estoy seguro de que sean idiomáticos.

(def a (take 10 (iterate inc 1))) (take 3 a) (drop 3 a) (take (- (count a) 3) a) (drop (- (count a) 3) a) (drop 3 (take 7 a)) (drop 3 (take (- (count a) 3) a))

Mi pregunta es: ¿cómo cortar secuencias en Clojure? En otras palabras, ¿cuál es la forma correcta de devolver diferentes partes de una secuencia?


Hay una función subvec . Desafortunadamente, solo funciona con vectores, por lo que tendrías que transformar tu secuencia:

subvec


La noción de Python de una secuencia es muy diferente de la de Clojure.

En Python,

  • una sequence es un conjunto ordenado finito indexado por números no negativos ; y
  • una lista es una secuencia mutable a la que puede agregar o quitar segmentos.

En Clojure,

  • una sequence es una interfaz que admite first , rest y cons ;
  • una list es una colección secuencial inmutable con cons (o rest ) que agrega (o elimina) los first elementos (devolviendo las listas así modificadas, de todos modos).

La cosa más cercana en Clojure a una lista de Python es un vector . Como sugiere Adam Sznajder , puede subvec usando subvec , aunque no puede agregar o eliminar segmentos como puede hacerlo en Python.

subvec es una operación rápida de tiempo constante, mientras que drop permite pagar por el número de elementos omitidos ( take hace pagar por los elementos que atraviesa, pero estos son los que le interesan).

Tus ejemplos se convierten en ...

(def a (vec (range 1 (inc 10)))) (subvec a 0 3) ; [1 2 3] (subvec a 3) ; [4 5 6 7 8 9 10] (subvec a 0 (- (count a) 3)) ; [1 2 3 4 5 6 7] (subvec a (- (count a) 3)) ; [8 9 10] (subvec a 3 (+ 3 4)) ; [4 5 6 7] (subvec a 3 (- (count a) 3)) ; [4 5 6 7]


Puede simplificar todos los que usan count usando take-last o drop-last lugar:

(def a (take 10 (iterate inc 1))) (take 3 a) ; get first 3 elements (drop 3 a) ; get all elements except the first 3 (drop-last 3 a) ; get all elements except the last 3 (take-last 3 a) ; get last 3 elements (drop 3 (take 7 a)) ; get 4 elements starting from 3 (drop 3 (drop-last 3 a)) ; get all elements except the first and the last 3

Y como se sugiere en los comentarios a continuación, puede usar la macro ->> para "unir" varias operaciones juntas. Por ejemplo, las dos últimas líneas también podrían escribirse así:

(->> a (take 7) (drop 3)) ; get 4 elements starting from 3 (->> a (drop-last 3) (drop 3)) ; get all elements except the first and the last 3

Creo que los dos métodos son muy legibles si solo está aplicando dos operaciones a una lista, pero cuando tiene una cadena larga como take , map , filter , drop , first , usar la macro ->> puede hacer el código mucho más fácil. Leer y probablemente aún más fácil de escribir.