Reversão multidimensional


23

Dada uma matriz ortogonal N-dimensional (não irregular) de números inteiros não negativos e uma indicação de quais dimensões reverter, retorne a matriz, mas invertida ao longo dessas dimensões. A indicação pode ser dada como uma lista booleana de comprimento N ou uma lista de um subconjunto das primeiras N dimensões indexadas de 0 ou 1.

Por favor, indique seus formatos de entrada. As explicações de código são muito apreciadas.

Exemplo percorrido

Recebemos o array 3D de 2 camadas e 3 linhas e 4 colunas

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

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

e um dos

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

Precisamos reverter a ordem da primeira e da última dimensão, que são as camadas e os elementos das linhas (as colunas), mas não as linhas de cada camada. Primeiro (a ordem real em que você faz isso não importa), invertemos a ordem das camadas:

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

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

e então invertemos a ordem dos elementos de cada linha:

[[[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 teste

[[[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]]]]


Eu sinto que a parte mais difícil da maioria das linguagens estaticamente tipificadas será jogar as assinaturas de tipo envolvidas.
Οurous

@ Οurous Como esses idiomas normalmente lidam com dados arbitrários de array?
Adám 03/09/19

1
existem três casos para o uso "normal", como eu vejo: apenas se preocupar com um nível da matriz (por exemplo: reversefunciona em matrizes arbitrárias, mas se preocupa apenas com o primeiro nível), genéricos ou classes recursivas (classes de tipo / objeto, dependendo da funcionalidade ou OOP, mas caso de uso semelhante). Os dois últimos são geralmente muito mais detalhados.
Οurous

Podemos armazenar a matriz como matrizes de ponteiros para ponteiros (em C ou ASM), em vez de matrizes multidimensionais apropriadas, onde tudo é contíguo na memória? Tenho certeza de que todas as linguagens normais de nível superior / de tipo dinâmico com aninhamento arbitrário de listas já estão tratando as coisas como listas de listas, não como matrizes, então vou assumir que está tudo bem.
5608 Peter Cordes

@ PeterCordes Claro, vá em frente.
Adám 5/09/18

Respostas:


8

APL (Dyalog) , 20 9 bytes

⊃{⌽[⍺]⍵}/

Experimente online!

Quão?

/ - reduzir - pegue o elemento mais à direita na entrada (a matriz) e aplique a função com o próximo elemento à esquerda como argumento à esquerda

{⌽[⍺]⍵}- reverter na dimensão left argument( )

- achatar a matriz fechada



8

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

Guardado 8 bytes graças a @Shaggy

Recebe entrada como (indications)(array), em que indicações é uma lista booleana.

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

Experimente online!

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

Apenas usando rno lugar de r||-1 parece funcionar .
Shaggy

Funcionaria f=([r,...b])=>a=>1/r?a.sort(_=>r).map(f(b)):a? No meu telefone, não é possível testar corretamente.
Shaggy

@Shaggy Nice! Eu gosto desse fluxo de processamento bastante incomum.
Arnauld


5

Geléia , 8 bytes

”€ẋ”ṚpFv

Leva uma lista de dimensões indexada em 0.

Experimente online!

Como 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.

1
Isso é horrível. Muito agradável!
Adám 03/09/18

5

R , 80 78 77 bytes

Crie a chamada para o extrator de R [criando uma lista de sequências revertidas onde indicado. Na verdade, eles contêm zeros, que são ignorados silenciosamente. A drop=Fé necessária para impedir a queda padrão do R de dimensões. Precisamos da revchamada para o indicador de reversão de dimensão, devido à maneira como R preenche matrizes.

-2 graças @Giuseppe

-1 usando atribuição em linha.

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

Experimente online!

Menção honrosa a @JayCe, que apresentou uma variação que obtém o mesmo resultado no mesmo comprimento:

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

Experimente online!


1
Resposta muito agradável de 78 bytes !
Giuseppe

1
Resposta muito profunda. Demorei um pouco para entender completamente. Eu tentei replicá-lo sem usar do.call- ele é mais comprido em 83 bytes, ainda o postando aqui como um comentário para referência: TIO
JayCe 4/18

Bem, na verdade, @JayCe, sua ótima resposta também pode ser alterada para 78 bytes!
precisa

5

Haskell, 120 119 bytes

a função f pega a lista N-dimensional e uma 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)

1
Você não precisa de parênteses F r.
Ørjan Johansen

1
Link do TIO com casos de teste e golfe de 1 byte do OJ.
Khuldraeseth na'Barya

4

05AB1E , 23 11 10 bytes

'€s×'R«J.V

Experimente online.

-12 bytes graças a @ Mr.Xcoder .

Insira como valores de verdade indexados a 0 (ou seja [0,2,3]), que é a primeira entrada.

Explicação:

'€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 exemplo: se a lista de entrada de índices for [0,2,3], ela criará a seguinte sequência:

R€€R€€€R

Qual irá:

    €€€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

Resposta original de 23 bytes:

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

Entrada como lista booleana (ie [1,0,1,1]), que é a primeira entrada.

Experimente online.

Explicação:

ć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 exemplo: Se a lista de entrada booleana for [1,0,1,1], ela criará a seguinte string:

εεεR}R} }R

Qual irá:

  ε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

1
Boa resposta, mas ... uhm ... esse 11 byter funcionaria?
Mr. Xcoder

@ Mr.Xcoder Obrigado! Isso é realmente muito mais fácil. E foi capaz de jogar mais 1 byte acrescentando cada um, em vez de anexar e depois reverter.
Kevin Cruijssen

@ Mr.Xcoder Btw, por que faz 'x*o trabalho de repetir xn quantidade de vezes sem usar um swap, mas ele não funciona com '€*.. EDIT:? Só no legado embora ..
Kevin Cruijssen

A versão legada é bastante complicada, pode ser devido ao fato de ainda ser analisado como operador, mesmo que esteja em um literal de caractere? Não tenho certeza para ser honesto. Na nova versão, no *entanto , não se comporta da mesma maneira.
Mr. Xcoder

3

JavaScript (Node.js) , 60 bytes

Uma abordagem diferente (recursiva). não bate a resposta de Arnauld ... ainda ....

Toma a 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]))


3

Pitão , 15 bytes

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

Experimente aqui!

Irritantemente, manipular o caso da lista de dimensões vazias leva não menos de 2 bytes ... Prefiro usar ssno lugar de jk.nbut: | Supõe que a lista a ser transformada possa ser fornecida na sintaxe Pyth nativa, como uma sequência. Eu escrevi um conversor para a sintaxe Pyth para facilitar o teste. No infeliz caso em que o OP opte por não permitir isso, um operador de 17 bytes o "corrigirá":

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

3

Japonês , 15 14 bytes

Com alguma inspiração da solução de Arnauld .

Toma as indicações como a primeira entrada, como uma matriz booleana de 1s e 0s.

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

Tente


Explicação

                   :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

3

Limpo , 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)

Experimente online!

Uma versão da resposta de Damien Haskell usando o sistema de golfe do tipo Clean. Realmente mostra as extensas semelhanças entre os dois 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


1

(não testado, mas acho correto. A saída do compilador asm se parece com o que eu espero. Será atualizada se / quando encontrar tempo para escrever um equipamento de teste que crie e imprima essa estrutura de dados.)

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 = ponteiro e cai de uma função não nula 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 é uma estrutura do contador de profundidade, comprimento, matriz de {números inteiros ou ponteiros}. No nível inferior desta árvore não binária ( depth==0), a matriz de intptr_té uma matriz de números inteiros. Em níveis mais altos, é struct m*armazenado intptr_t. Traversal leva um elenco.

A R()função reversa é uma função de membro porque salva a declaração de um argumento e salva muita p->sintaxe para referenciar os membros da estrutura versus o thisponteiro implícito .

A única extensão GNU é o membro da matriz flexível C99 para criar uma estrutura de tamanho variável , suportada no C ++ como uma extensão GNU. Eu poderia ter usado um *amembro apontando para uma matriz alocada separadamente e ter isso como simples ISO C ++. (E isso realmente salvaria um byte sem exigir outras alterações). Eu escrevi isso como uma implementação de maquete / referência para uma versão asm.


A versão mais curta com apenas inttambém declara R()como retornando em intvez de void. Esses dois bits de hackery não têm relação; esta é apenas a versão "funciona em pelo menos uma implementação".

Ele deve funcionar bem em destinos de 32 bits (onde intpode conter um ponteiro), desde que você compile com o gcc7 ou mais antigo ou desative as otimizações. ( gcc8 -O3assume que a execução não pode chegar ao fundo de uma não- voidfunção porque isso seria UB.) x86 gcc -m32 -O3deve funcionar bem com o gcc7, como no Godbolt, onde eu incluí as duas versões (em diferentes espaços de nome) e uma versão sem função de membro .

Ungolfed

A função arg,, int r[]é uma matriz de 0 / números inteiros diferentes de zero que indica se uma determinada profundidade deve ser trocada, começando com o nível mais 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

Quando recursamos, passamos r+1, portanto, verificar a profundidade atual é sempre *r.

Uma versão anterior passou apenas rinalterada e verificada r[d]. Com um membro de matriz flexível, eu precisava armazenar algum tipo de indicador de último nível, porque a[]não é um ponteiro, é uma matriz verdadeira, sem indireção. Mas com um intptr_t *amembro, eu não poderia ter isso apenas nullptrno nível da folha, porque quero que sejam valores.

A reversão do nível atual antes ou depois da travessia da árvore não deve importar. Eu não tentei fazer isso durante .

Não tenho certeza de que std::reversevale a contagem de bytes versus um loop manual, especialmente se eu puder trabalhar chamando R()cada ponteiro exatamente uma vez em algum lugar dentro desse loop. Mas apenas sed!=0


Uau, impressionante.
Adám 5/09/18

@ Adám: obrigado, jogou surpreendentemente naturalmente e bem depois que eu escrevi. Estou curioso para ver o que posso fazer em código de máquina x86: P
Peter Cordes

1

Mathematica, 7 bytes

Reverse

Função. Dê a ele uma lista aninhada como o primeiro argumento e a lista baseada em 1 de níveis / dimensões para reverter como o segundo argumento. Experimente online!

Finalmente, outro desafio em que o Mathematica está embutido!


camadas para reverter ? Link TIO talvez?
Adám 5/09/18

@ Adám Ou seja, a lista de dimensões a reverter, geralmente referida como níveis no Mathematica.
LegionMammal978
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.