Inversión multidimensional

23

Dado un conjunto ortogonal N-dimensional (no desigual) de enteros no negativos, y una indicación de qué dimensiones invertir, devuelve el conjunto pero invertido a lo largo de esas dimensiones. La indicación puede darse como una lista booleana de longitud N o una lista de un subconjunto de las primeras N dimensiones indexadas desde 0 o 1.

Por favor, indique sus formatos de entrada. Las explicaciones del código son muy apreciadas.

Ejemplo de recorrido

Se nos proporciona la matriz 3D de 2 capas, 3 filas y 4 columnas.

[[[ 1, 2, 3, 4],
  [ 5, 6, 7, 8],
  [ 9,10,11,12]],

 [[13,14,15,16],
  [17,18,19,20],
  [21,22,23,24]]]

y uno de

[true,false,true](Lista booleana)
[0,2](lista indexada 0)
[1,3](lista indexada 1)

Necesitamos invertir el orden de las dimensiones primera y última, es decir, las capas y los elementos de las filas (las columnas), pero no las filas de cada capa. Primero (el orden real en el que haces esto no importa) invertimos el orden de las capas:

[[[13,14,15,16],
  [17,18,19,20],
  [21,22,23,24]],

 [[ 1, 2, 3, 4],
  [ 5, 6, 7, 8],
  [ 9,10,11,12]]]

y luego revertimos el orden de los elementos de cada fila:

[[[16,15,14,13],
  [20,19,18,17],
  [24,23,22,21]],

 [[ 4, 3, 2, 1],
  [ 8, 7, 6, 5],
  [12,11,10, 9]]]

Casos de prueba

[[[1,2,3,4],[5,6,7,8],[9,10,11,12]],[[13,14,15,16],[17,18,19,20],[21,22,23,24]]]
[true,false,true]/ [0,2]/ [1,3]
 ↓ 
[[[16,15,14,13],[20,19,18,17],[24,23,22,21]],[[4,3,2,1],[8,7,6,5],[12,11,10,9]]]


[[1,2,3],[4,5,6]]
[true,false]/ [0]/ [1]
 ↓
[[4,5,6],[1,2,3]]


[[1],[4]]
[true,false]/ [0]/ [1]
 ↓
[[4],[1]]


[[7]]
[true,true]/ [0,1]/ [1,2]
 ↓
[[7]]


[1,2,3,4,5,6,7]
[true]/ [0]/ [1]
 ↓
[7,6,5,4,3,2,1]


[]
[true]/ [0]/ [1]
 ↓
[]


[[],[]]
[false,false]/ []/ []
 ↓
[[],[]]


[[[[3,1,4,1],[5,9,2,6]],[[5,3,5,8],[9,7,9,3]]],[[[2,3,8,4],[6,2,6,4]],[[3,3,8,3],[2,7,9,5]]]]
[true,false,true,true]/ [0,2,3]/ [1,3,4]
 ↓
[[[[4,6,2,6],[4,8,3,2]],[[5,9,7,2],[3,8,3,3]]],[[[6,2,9,5],[1,4,1,3]],[[3,9,7,9],[8,5,3,5]]]]


[[[[3,1,4,1],[5,9,2,6]],[[5,3,5,8],[9,7,9,3]]],[[[2,3,8,4],[6,2,6,4]],[[3,3,8,3],[2,7,9,5]]]]
[false,true,false,false]/ [1]/ [2]
 ↓
[[[[5,3,5,8],[9,7,9,3]],[[3,1,4,1],[5,9,2,6]]],[[[3,3,8,3],[2,7,9,5]],[[2,3,8,4],[6,2,6,4]]]]


[[[[3,1,4,1],[5,9,2,6]],[[5,3,5,8],[9,7,9,3]]],[[[2,3,8,4],[6,2,6,4]],[[3,3,8,3],[2,7,9,5]]]]
[false,false,false,false]/ []/ []
 ↓
[[[[3,1,4,1],[5,9,2,6]],[[5,3,5,8],[9,7,9,3]]],[[[2,3,8,4],[6,2,6,4]],[[3,3,8,3],[2,7,9,5]]]]

Adán
fuente
Siento que la parte más difícil en la mayoría de los idiomas mecanografiados estáticamente es jugar golf con las firmas de tipo involucradas.
Agradable el
@ Οurous ¿Cómo manejan esos idiomas normalmente los datos de matriz arbitraria?
Adám
1
Hay tres casos para el uso "normal" tal como lo veo: solo preocuparse por un nivel de la matriz (por ejemplo: reversefunciona en matrices arbitrarias pero solo se preocupa por el primer nivel), genéricos o clases recursivas (clases de tipo / objeto dependiendo de la funcionalidad o POO, pero caso de uso similar). Los dos últimos suelen ser mucho más detallados.
Sepurous
¿Podemos almacenar la matriz como matrices de punteros a punteros (en C o asm), en lugar de matrices multidimensionales adecuadas donde todo es contiguo en la memoria? Estoy bastante seguro de que todos los lenguajes normales de nivel superior / dinámicamente tipados con anidamiento arbitrario de listas ya están tratando las cosas como listas de listas, no como matrices, por lo que voy a asumir que está bien.
Peter Cordes
@PeterCordes Claro, adelante.
Adám

Respuestas:

8

APL (Dyalog) , 20 9 bytes

⊃{⌽[⍺]⍵}/

Pruébalo en línea!

¿Cómo?

/ - reducir - tomar el elemento más a la derecha en la entrada (la matriz) y aplicar la función con el siguiente elemento izquierdo como argumento izquierdo

{⌽[⍺]⍵}- invertir en la dimensión left argument( )

- aplanar la matriz cerrada

Uriel
fuente
8

JavaScript (Node.js) , 58 55 53 45 bytes

Guardado 8 bytes gracias a @Shaggy

Toma la entrada como (indications)(array), donde las indicaciones son una lista booleana.

f=([r,...b])=>a=>1/r?a.sort(_=>r).map(f(b)):a

Pruébalo en línea!

Comentado

f = (                // f is a function taking:
  [r,                //   r = next 'reverse' Boolean flag
      ...b]          //   b = array of remaining flags
) =>                 // and returning an anonymous function taking:
  a =>               //   a = array (or sub-array) to process, or atomic element
    1 / r ?          // if r is defined:
      a.sort(_ => r) //   reverse a if r = 1; leave it unchanged otherwise
      .map(f(b))     //   for each element in the resulting array: do a recursive call,
                     //   using f to generate a new callback function for the next flag
    :                // else:
      a              //   a must be an atomic element and is simply left unchanged
Arnauld
fuente
Solo usar en rlugar de r||-1 parece funcionar .
Shaggy
Funcionaria f=([r,...b])=>a=>1/r?a.sort(_=>r).map(f(b)):a? En mi teléfono, así que no puedo probarlo correctamente.
Shaggy
@Shaggy Nice! Me gusta este flujo de procesamiento bastante inusual.
Arnauld
5

Jalea , 8 bytes

”€ẋ”ṚpFv

Toma una lista indexada de 0 de dimensiones.

Pruébalo en línea!

Cómo funciona

”€ẋ”ṚpFv  Main link. Left arg: D (dimensions, 0-based), Right arg: A (array)

”€ẋ       Repeat '€' d times, for each d in D.
   ”Ṛp    Perform Cartesian product of ['Ṛ'] and each string of '€'s, prepending a
          'Ṛ' to each string of '€'s.
      F   Flatten the result.
          If, e.g., D = [0,2,4], we build the string "ṚṚ€€Ṛ€€€€".
       v  Eval the resulting string, using A as left argument.
Dennis
fuente
1
Esto es horrible. ¡Muy agradable!
Adám
5

R , 80 78 77 bytes

Cree la llamada al extractor de R [creando una lista de secuencias invertidas donde se indica. En realidad contienen ceros, que se ignoran en silencio. Se drop=Fnecesita para evitar la caída predeterminada de dimensiones de R. Necesitamos la revllamada al indicador de inversión de dimensión, debido a la forma en que R llena las matrices.

-2 gracias @Giuseppe

-1 utilizando la asignación en línea.

function(x,a,d=dim(x))do.call("[",c(list(x),Map(seq,r<-d*rev(a),d-r),drop=F))

Pruébalo en línea!

Mención de honor a @JayCe, quien presentó una variación que obtiene el mismo resultado en la misma duración:

function(x,a,d=dim(x))array(x[t(t(expand.grid(Map(seq,r<-d*rev(a),d-r))))],d)

Pruébalo en línea!

J.Doe
fuente
1
78 bytes muy buena respuesta!
Giuseppe
1
Respuesta muy profunda. Me tomó un tiempo entenderlo completamente. Traté de replicarlo sin usarlo do.call; es más largo en 83 bytes, todavía publico esto aquí como comentario para referencia: TIO
JayCe
Bueno, en realidad, @ JayCe, ¡tu gran respuesta también se puede jugar a 78 bytes!
J.Doe
5

Haskell, 120 119 bytes

la función f toma la lista N-dimensional y una lista de bool como entrada

class F r where f::[Bool]->r->r
instance F Int where f=seq
instance F r=>F[r]where f(a:y)=last(id:[reverse|a]).map(f y)
Damien
fuente
1
No necesitas paréntesis F r.
Ørjan Johansen
1
Enlace TIO con casos de prueba y golf de 1 byte de OJ.
Khuldraeseth na'Barya
4

05AB1E , 23 11 10 bytes

'€s×'R«J.V

Pruébalo en línea.

-12 bytes gracias a @ Mr.Xcoder .

Ingrese como valores de verdad indexados en 0 (es decir [0,2,3]), que es la primera entrada.

Explicación:

'€s×           # Repeat "€" the indices amount of times
               #  i.e. [0,2,3] → ["","€€","€€€"]
    'R«        # Append each with "R"
               #  i.e. ["","€€","€€€"] → ["R","€€R","€€€R"]
        J      # Join them all together
               #  i.e. ["R","€€R","€€€R"] → R€€R€€€R
         .V    # Execute string as 05AB1E code

Por ejemplo: si la lista de entrada de índices es [0,2,3], creará la siguiente cadena:

R€€R€€€R

Que lo hará:

    €€€R    # Reverse the items in the most inner (4th level) lists
 €€R        # Reverse the most inner (3rd level) lists themselves
            # Do nothing with the inner (2nd level) lists 
R           # Reverse the entire outer (1st level) list

Respuesta original de 23 bytes:

ćURvy„ RèJ…εÿ}}„ RXèJ.V

Ingrese como boolean-list (ie [1,0,1,1]), que es la primera entrada.

Pruébalo en línea.

Explicación:

ćU                 # Pop and save the first boolean in variable `X`
  R                # Reverse the remaining boolean-list
   v    }          # Loop `y` over each of them:
     Rè           #  Take the string "R ", and index the current boolean (0 or 1) in it
    J              #  Join it together with the string of the previous iteration
    …εÿ}           #  Surround it with "ε" and "}"
          RXè     # Index variable `X` also in "R "
              J    # Join it together with the rest
.V                 # Execute string as 05AB1E code

Por ejemplo: si la lista de entrada booleana es [1,0,1,1], creará la siguiente cadena:

εεεR}R} }R

Que lo hará:

  εR}         # Reverse the items in the most inner (4th level) lists
 ε   R}       # Reverse the most inner (3rd level) lists themselves
ε       }     # Do nothing with the inner (2nd level) lists 
         R    # Reverse the entire outer (1st level) list
Kevin Cruijssen
fuente
1
Buena respuesta, pero ... uhm ... ¿funcionaría este 11 byter ?
Sr. Xcoder
@ Mr.Xcoder Gracias! Eso es mucho más fácil. Y he podido jugar 1 byte más agregando cada uno, en lugar de pretender y luego revertir.
Kevin Cruijssen
@ Mr.Xcoder Btw, ¿por qué 'x*funciona repetir xn cantidad de veces sin usar un swap, pero no funciona '€*? ... EDITAR: solo en el legado ...
Kevin Cruijssen
La versión heredada es bastante defectuosa, ¿podría deberse al hecho de que todavía se analiza como un operador a pesar de que está en un carácter literal? No estoy seguro de ser honesto. En la nueva versión, *no se comporta de la misma manera.
Sr. Xcoder
3

JavaScript (Node.js) , 60 bytes

Un enfoque diferente (recursivo). no supera la respuesta de Arnauld ... todavía ...

Toma la entrada como array, boolean list

f=(a,r)=>r>[]?(r[0]?a.reverse():a).map(c=>f(c,r.slice(1))):a

f=(a,r)=>r>[]?(r[0]?a.reverse():a).map(c=>f(c,r.slice(1))):a

console.log(f([[[[3,1,4,1],[5,9,2,6]],[[5,3,5,8],[9,7,9,3]]],[[[2,3,8,4],[6,2,6,4]],[[3,3,8,3],[2,7,9,5]]]],[true,false,true,true]))

Luis felipe De jesus Munoz
fuente
3

Pyth , 15 bytes

.v+jk.n*\_*L\ME

Pruébalo aquí!

Molesto, manejar el caso de la lista de dimensiones vacía toma no menos de 2 bytes ... Prefiero usar ssen lugar de jk.npero: | Asume que la lista a transformar se puede dar en sintaxis nativa de Pyth, como una cadena. He escrito un convertidor a la sintaxis de Pyth para facilitar las pruebas. En el desafortunado caso de que el OP opta por no permitir esto, un 17-byter lo "arreglará":

.v+jk.n*\_*L\ME\E
Sr. Xcoder
fuente
3

Japt , 15 14 bytes

Con un poco de inspiración de la solución de Arnauld .

Toma las indicaciones como la primera entrada, como una matriz booleana de 1sy 0s.

Ê?Vn@ÎãßUÅX:V

Intentalo


Explicación

                   :Implicit input of boolean array U=indications and multi-dimensional integer array V
Ê                  :Get the length of U
 ?                 :If truthy (i.e., >0)
  Vn               :  Sort V
    @ÎÃ            :   Function that gets the first element of U; 0 will leave the array untouched, 1 will reverse it.
       £           :  Map each X
        ß          :  Run the programme again with the following inputs
         UÅ        :   U with the first element removed
           X       :   X will serve as the new value of V
            :      :Else
             V     :  Just return V
Lanudo
fuente
3

Limpio , 122 112 bytes

import StdEnv
class$r::[Bool]->r->r
instance$r where$_=id
instance$[r]| $r where$[a:y]=if(a)reverse id o map($y)

Pruébalo en línea!

Una versión de la respuesta de Damien Haskell usando el sistema de tipo golfista de Clean. Realmente muestra las amplias similitudes entre los dos idiomas.

Explicado:

import StdEnv                 // import basic stuff
class $ r :: [Bool] -> r -> r // $ takes a boolean list and returns a function on r to r
instance $ r                  // instance on all types, taken when no more specific instances exist
    where $ _ = id            // return the identity function for all arguments
instance $ [r] | $ r          // instance for lists of a type which has an instance itself
    where $ [a: y]
        = if(a) reverse id    // reverse if the head of the argument is true
            o map ($ y)       // composed with the map function acting on $ applied to the tail
Οurous
fuente
1

(no probado pero creo que es correcto. La salida del compilador asm se parece a lo que esperaba. Se actualizará si encuentro tiempo para escribir un arnés de prueba que cree e imprima esta estructura de datos).

GNU C ++ (portátil) 148 bytes

#include<algorithm>
#include<cstdint>
struct m{intptr_t d,l,a[];void R(int*r){if(*r)std::reverse(a,a+l);for(int i=0;d&&i<l;((m*)a[i++])->R(r+1));}};

GNU C ++ (int = puntero y se cae de una función no vacía UB) 120 bytes

#include<algorithm>
struct m{int d,l,a[],R(int*r){if(*r)std::reverse(a,a+l);for(int i=0;d&&i<l;((m*)a[i++])->R(r+1));}};

Esta es una estructura de contador de profundidad, longitud, matriz de {enteros o punteros}. En el nivel inferior de este árbol no binario ( depth==0), la matriz de intptr_tes una matriz de enteros. En niveles superiores, se struct m*almacena enintptr_t . El recorrido toma un yeso.

La R()función inversa es una función miembro porque ahorra declarar un argumento y una gran p->sintaxis para hacer referencia a los miembros de la estructura frente al thispuntero implícito .

La única extensión de GNU es el miembro de matriz flexible C99 para hacer una estructura de tamaño variable , que es compatible con C ++ como una extensión de GNU. Podría haber usado un *amiembro que apunta a una matriz asignada por separado y que esto sea ISO C ++ simple. (Y eso realmente ahorraría un byte sin requerir ningún otro cambio). Escribí esto como una implementación de maqueta / referencia para una versión asm.


La versión más corta con solo inttambién declara R()que regresa en intlugar de void. Estos dos bits de piratería informática no están relacionados; esta es solo la versión "funciona en al menos una implementación".

Debería funcionar bien en destinos de 32 bits (donde intpuede contener un puntero), siempre que compile con gcc7 o anterior, o desactive las optimizaciones. ( gcc8 -O3asume que la ejecución no puede llegar al final de una no voidfunción porque sería UB). x86 gcc -m32 -O3debería funcionar bien con gcc7, como en Godbolt, donde incluí ambas versiones (en diferentes espacios de nombres) y una versión sin función miembro .

Sin golf

La función arg, int r[]es una matriz de enteros 0 / distintos de cero que indican si se debe intercambiar una profundidad dada, comenzando con el nivel más externo.

#include<algorithm>  // for std::reverse
#include<cstdint>    // for intptr_t.  GNU C defines __intptr_t, so we could use that...

struct m{
    __intptr_t d,l,a[];    // depth = 0 means values, >0 means pointers.
    // l = length
    //__intptr_t a[];  // flexible array member: array contiguous with the struct

    void R(int r[]) {
        if(*r)
            std::reverse(a, a+l);   // *r && std::reverse() doesn't work because it returns void.

        if(d) // recurse if this isn't the bottom depth
            for(int i=0 ; i<l ; i++)  // tree traversal
                ((m*)a[i])->R(r+1);   // with the rest of the depth list
    }

}; // struct m

Cuando repetimos, pasamos r+1, por lo que siempre es necesario verificar la profundidad actual *r.

Una versión anterior simplemente pasó rsin cambios y se verificó r[d]. Con un miembro de matriz flexible, necesitaba almacenar algún tipo de indicador de último nivel porque a[]no es un puntero, es una matriz verdadera sin direccionamiento indirecto. Pero con un intptr_t *amiembro, no podría simplemente ser eso nullptrpara el nivel de la hoja, porque quiero que sean valores.

Invertir el nivel actual antes o después del recorrido del árbol no debería importar. No intenté hacerlo durante .

No estoy seguro de questd::reverse valga la pena contar los bytes frente a un bucle manual, especialmente si puedo trabajar invocando R()cada puntero exactamente una vez dentro de ese bucle. Pero solo sid!=0

Peter Cordes
fuente
Whoa, impresionante.
Adám
@ Adám: gracias, jugó golf sorprendentemente naturalmente y mucho después de que lo escribí. Tengo curiosidad por ver qué puedo hacer en el código de máquina x86: P
Peter Cordes
1

Mathematica, 7 bytes

Reverse

Función. Déle una lista anidada como primer argumento, y la lista basada en 1 de niveles / dimensiones para invertir como segundo argumento.Pruébalo en línea!

Finalmente, ¡otro desafío donde Mathematica tiene un proyecto incorporado!

LegionMammal978
fuente
capas para revertir ? Enlace TIO tal vez?
Adám
@ Adám Es decir, la lista de dimensiones a revertir, generalmente conocidas como niveles en Mathematica.
LegionMammal978