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:
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
ycons
; - una list es una colección secuencial inmutable con
cons
(orest
) que agrega (o elimina) losfirst
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.