Mehrdimensionale Umkehrung

23

Bei einem n-dimensionalen orthogonalen (nicht zackigen) Array nicht negativer Ganzzahlen und einer Angabe, welche Dimensionen umgekehrt werden sollen, wird das Array zurückgegeben, jedoch entlang dieser Dimensionen umgekehrt. Die Angabe kann als Boolesche Liste der Länge N oder als Liste einer Teilmenge der ersten N Dimensionen erfolgen, die von 0 oder 1 indexiert sind.

Bitte geben Sie Ihre Eingabeformate an. Code-Erklärungen werden sehr geschätzt.

Durchgelaufenes Beispiel

Wir erhalten das 2-lagige 3-zeilige 4-spaltige 3D-Array

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

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

und einer von

[true,false,true](Boolesche Liste)
[0,2](0-indizierte Liste)
[1,3](1-indizierte Liste)

Wir müssen die Reihenfolge der ersten und letzten Bemaßung umkehren, dh die Ebenen und Elemente der Zeilen (die Spalten), aber nicht die Zeilen jeder Ebene. Zuerst (die tatsächliche Reihenfolge, in der Sie dies tun, spielt keine Rolle) kehren wir die Reihenfolge der Ebenen um:

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

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

und dann kehren wir die Reihenfolge der Elemente jeder Zeile um:

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

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

Testfälle

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

Adam
quelle
Ich glaube, der schwierigste Teil in den meisten statisch getippten Sprachen wird darin bestehen, die Typunterschriften zu spielen.
Urous
@ Οurous Wie gehen diese Sprachen normalerweise mit beliebigen Array-Daten um?
Adám
1
Es gibt drei Fälle für den "normalen" Gebrauch, wie ich es sehe: sich nur um eine Ebene des Arrays zu kümmern (zB: reversefunktioniert bei beliebigen Arrays, kümmert sich aber nur um die erste Ebene), generische oder rekursive Klassen (Typ- / Objektklassen, abhängig von der Funktion) oder OOP, aber ähnlicher Anwendungsfall). Die beiden letzteren sind in der Regel weitaus ausführlicher.
Urous
Können wir die Matrix als Arrays von Zeigern auf Zeiger (in C oder asm) speichern, anstatt als richtige mehrdimensionale Arrays, in denen alles im Speicher zusammenhängend ist? Ich bin mir ziemlich sicher, dass alle normalen höherstufigen / dynamisch typisierten Sprachen mit willkürlicher Verschachtelung von Listen Dinge bereits als Listen von Listen und nicht als Matrizen behandeln, also gehe ich davon aus, dass es in Ordnung ist.
Peter Cordes
@ PeterCordes Sicher, mach weiter.
Adám

Antworten:

8

APL (Dyalog) , 20 9 Bytes

⊃{⌽[⍺]⍵}/

Probieren Sie es online!

Wie?

/ - reduct - nimm das am weitesten rechts stehende Element in der Eingabe (das Array) und wende die Funktion mit dem nächsten linken Element als linkes Argument an

{⌽[⍺]⍵}- In der Dimension left argument( ) umkehren

- Beiliegendes Array abflachen

Uriel
quelle
8

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

8 Bytes dank @Shaggy gespart

Übernimmt die Eingabe als (indications)(array), wobei die Angabe eine Boolesche Liste ist.

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

Probieren Sie es online!

Kommentiert

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
quelle
Nur die Verwendung von rinplace of r||-1 scheint zu funktionieren .
Shaggy
Würde f=([r,...b])=>a=>1/r?a.sort(_=>r).map(f(b)):afunktionieren Auf meinem Handy kann man das also nicht richtig testen.
Shaggy
@ Shaggy Schön! Mir gefällt dieser eher ungewöhnliche Ablauf.
Arnauld
5

Gelee , 8 Bytes

”€ẋ”ṚpFv

Nimmt eine mit 0 indizierte Liste von Dimensionen auf.

Probieren Sie es online!

Wie es funktioniert

”€ẋ”Ṛ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
quelle
1
Das ist schrecklich. Sehr schön!
Adám
5

R , 80 78 77 Bytes

Erstellen Sie den Aufruf für den Extraktor [von R, indem Sie eine Liste von Sequenzen erstellen, die an den angegebenen Stellen umgekehrt sind. Sie enthalten tatsächlich Nullen, die unbemerkt ignoriert werden. Dies drop=Fwird benötigt, um das standardmäßige Löschen von Dimensionen durch R zu verhindern. Wir benötigen den revAufruf des Dimensionsumkehrindikators, da R Arrays füllt.

-2 danke @ Giuseppe

-1 mit Inline-Zuweisung.

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

Probieren Sie es online!

Lobende Erwähnung an @JayCe, der sich eine Variante ausgedacht hat, die das gleiche Ergebnis in der gleichen Länge erzielt:

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

Probieren Sie es online!

J.Doe
quelle
1
78 Bytes sehr schöne Antwort!
Giuseppe
1
Sehr tiefe Antwort. Ich brauchte eine Weile, um es vollständig zu verstehen. Ich habe versucht, es zu replizieren, ohne es zu verwenden do.call- es ist länger bei 83 Bytes und
postet
Tatsächlich, @JayCe, kann Ihre großartige Antwort auch auf 78 Bytes übertragen werden!
J.Doe
5

Haskell, 120 119 Bytes

Die Funktion f nimmt die N-dimensionale Liste und eine Liste von Bool als Eingabe

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
quelle
1
Sie brauchen keine runden Klammern F r.
Ørjan Johansen
1
TIO-Verknüpfung mit Testfällen und OJs 1-Byte-Golf.
Khuldraeseth na'Barya
4

05AB1E , 23 11 10 Bytes

'€s×'R«J.V

Probieren Sie es online aus.

-12 Bytes dank @ Mr.Xcoder .

Eingabe als 0-indizierte Wahrheitswerte (dh [0,2,3]), die die erste Eingabe ist.

Erläuterung:

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

Beispiel: Wenn die Eingabe-Liste der Indizes lautet [0,2,3], wird die folgende Zeichenfolge erstellt:

R€€R€€€R

Welches wird:

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

Ursprüngliche 23-Byte-Antwort:

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

Eingabe als Boolesche Liste (dh [1,0,1,1]), die die erste Eingabe ist.

Probieren Sie es online aus.

Erläuterung:

ć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

Beispiel: Wenn die boolesche Eingabeliste "" lautet [1,0,1,1], wird die folgende Zeichenfolge erstellt:

εεεR}R} }R

Welches wird:

  ε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
quelle
1
Nette Antwort, aber ... ähm ... würde dieser 11 Byte funktionieren?
Mr. Xcoder
@ Mr.Xcoder Danke! Das ist in der Tat viel einfacher. Und in der Lage gewesen, 1 weiteres Byte zu spielen, indem man jedes anhängt, anstatt es voranzustellen und dann umzukehren.
Kevin Cruijssen
@ Mr.Xcoder Übrigens, warum funktioniert 'x*es xn Mal ohne WAP zu wiederholen s, aber es funktioniert nicht mit '€*? .. EDIT: Nur im Erbe aber ..
Kevin Cruijssen
Die Vorgängerversion ist ziemlich fehlerhaft, könnte es an der Tatsache liegen, dass sie immer noch als Operator analysiert wird, obwohl es sich um ein Zeichenliteral handelt? Ich bin mir nicht sicher, ob ich ehrlich bin. Verhält sich in der neuen Version *trotzdem nicht so.
Mr. Xcoder
3

JavaScript (Node.js) , 60 Byte

Ein anderer (rekursiver) Ansatz. schlägt Arnauld's Antwort noch nicht ...

Übernimmt die Eingabe als 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
quelle
3

Pyth , 15 Bytes

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

Probieren Sie es hier aus!

Es ist ärgerlich, dass die Bearbeitung des leeren Dimensionslisten-Falls nicht weniger als 2 Byte dauert ... Ich würde lieber ssanstelle von jk.nbut: | verwenden Angenommen, die zu transformierende Liste kann in nativer Pyth-Syntax als Zeichenfolge angegeben werden. Ich habe einen Konverter in Pyth-Syntax geschrieben , um das Testen zu vereinfachen. In dem unglücklichen Fall, dass das OP dies nicht zulässt, wird es von einem 17-Byte-System "behoben":

.v+jk.n*\_*L\ME\E
Mr. Xcoder
quelle
3

Japt , 15 bis 14 Bytes

Mit etwas Inspiration von Arnauld's Lösung .

Nimmt die Angaben als erste Eingabe als boolesches Array von 1s und 0s.

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

Versuch es


Erläuterung

                   :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
Zottelig
quelle
3

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

Probieren Sie es online!

Eine Version von Damiens Haskell-Antwort unter Verwendung des Golfspielersystems von Clean. Zeigt wirklich die umfangreichen Ähnlichkeiten zwischen den beiden Sprachen.

Erklärt:

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
quelle
1

(ungetestet, aber ich denke korrekt. Die Compiler-ASM-Ausgabe sieht so aus, wie ich es erwartet habe. Wird aktualisiert, wenn ich Zeit finde, ein Test-Harness zu schreiben, das diese Datenstruktur erstellt und druckt.)

GNU C ++ (portabel) 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 = Zeiger und fällt von einer nicht leeren Funktion UB ab) 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));}};

Dies ist eine Struktur aus Tiefenzähler, Länge, Array von {Ganzzahlen oder Zeigern}. In der untersten Ebene dieses nicht-binären Baums ( depth==0) ist das Array von intptr_tein Array von Ganzzahlen. In höheren Ebenen ist es in struct m*gespeichert intptr_t. Traversal nimmt eine Besetzung.

Die R()umgekehrte Funktion ist eine Elementfunktion, da dadurch die Deklaration eines Arguments und eine Menge p->Syntax für die Referenzierung der Strukturelemente gegenüber dem impliziten thisZeiger eingespart werden.

Die einzige GNU-Erweiterung ist das flexible C99-Array-Mitglied , das eine Struktur mit variabler Größe erstellt , die in C ++ als GNU-Erweiterung unterstützt wird. Ich hätte ein *aMitglied verwenden können, das auf ein separat zugewiesenes Array zeigt, und dies wäre einfach ISO C ++. (Und das würde tatsächlich ein Byte speichern, ohne dass weitere Änderungen erforderlich wären). Ich habe dies als Modell- / Referenzimplementierung für eine asm-Version geschrieben.


Die kürzere Version mit intdeklariert eben auch R()als zurückkehrend intstatt void. Diese beiden Teile der Hackerei haben nichts miteinander zu tun. Dies ist nur die Version "arbeitet an mindestens einer Implementierung".

Es sollte auf 32-Bit-Zielen (wo intein Zeiger enthalten sein kann) einwandfrei funktionieren, solange Sie mit gcc7 oder älter kompilieren oder Optimierungen deaktivieren. ( Nimmt an, gcc8 -O3dass die Ausführung nicht das Ende einer Nicht- voidFunktion erreichen kann, da dies UB wäre.) x86 gcc -m32 -O3sollte mit gcc7 einwandfrei funktionieren, wie auf Godbolt, wo ich beide Versionen (in verschiedenen Namespaces) und eine Nicht-Member-Funktionsversion aufgenommen habe .

Ungolfed

Die Funktion arg,, int r[]ist ein Array von Ganzzahlen 0 / ungleich Null, die angeben, ob eine bestimmte Tiefe ausgetauscht werden soll, beginnend mit der äußersten Ebene.

#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

Wenn wir wiederkehren, gehen wir vorbei r+1, sodass immer die aktuelle Tiefe überprüft wird *r.

Eine frühere Version wurde nur runverändert übergeben und überprüft r[d]. Bei einem flexiblen Arraymitglied musste ich eine Art Anzeige der letzten Ebene speichern, da a[]es sich nicht um einen Zeiger handelt, sondern um ein echtes Array ohne Indirektion. Aber mit einem intptr_t *aMitglied konnte ich das nicht nur nullptrfür die Blattebene haben, weil ich möchte, dass es Werte sind.

Das Umkehren des aktuellen Niveaus vor oder nach dem Durchlaufen des Baums sollte keine Rolle spielen. Ich habe nicht versucht, es während zu tun .

Ich bin mir nicht sicher , ob diesstd::reverse die Anzahl der Bytes im Vergleich zu einer manuellen Schleife wert ist, insbesondere wenn ich R()jeden Zeiger genau einmal irgendwo innerhalb dieser Schleife aufrufen kann . Aber nur wennd!=0

Peter Cordes
quelle
Whoa, beeindruckend.
Adám
@ Adám: danke, es hat sich überraschend natürlich und gut abgespielt, nachdem ich es geschrieben habe. Ich bin gespannt, was ich mit x86-Maschinencode machen kann: P
Peter Cordes
1

Mathematica, 7 Bytes

Reverse

Funktion. Geben Sie als erstes Argument eine verschachtelte Liste und als zweites Argument die auf 1 basierende Liste der Ebenen / Dimensionen an, die umgekehrt werden sollen. Probieren Sie es online!

Endlich eine weitere Herausforderung, bei der Mathematica eine eingebaute hat!

LegionMammal978
quelle
schichten umzukehren ? TIO Link vielleicht?
Adám
@ Adám Das heißt, die Liste der umzukehrenden Dimensionen , die in Mathematica allgemein als Ebenen bezeichnet werden .
LegionMammal978