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.