Багатовимірний розворот


23

Враховуючи N-мірний ортогональний (не рваний) масив невід'ємних цілих чисел та вказівку, які розміри потрібно обернути, повернути масив, але повернутий за цими розмірами. Вказівка ​​може бути подана як булевий список довжини N або список підмножини перших N розмірів, індексованих від 0 або 1.

Будь ласка, вкажіть свої вхідні формати. Пояснення коду високо оцінені.

Проведений приклад

Нам надається двошаровий 3-рядовий 4-колонковий 3D-масив

[[[ 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](список з індексом 0)
[1,3](1-індексований список)

Нам потрібно змінити порядок першого та останнього розмірів, тобто шарів та елементів рядків (стовпців), але не рядків кожного шару. По-перше (власне порядок, яким ви це робите, не має значення), ми змінюємо порядок шарів:

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

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

а потім повертаємо порядок елементів кожного ряду:

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


Я відчуваю, що найважчою частиною в більшості статично набраних мов буде проведення гольфу за типовими підписами.
Οurous

@ Οurous Як ці мови зазвичай поводяться з умовними даними масиву?
Адам

1
Є три випадки "нормального" використання, як я це бачу: турбується лише про один рівень масиву (наприклад: reverseпрацює на довільних масивах, але лише піклується про перший рівень), генеричні або рекурсивні класи (тип / об'єктні класи залежно від функціоналу або OOP, але подібний варіант використання). Останні два зазвичай набагато більш багатослівні.
Οurous

Чи можемо ми зберігати матрицю у вигляді масивів покажчиків до покажчиків (у С чи АСМ) замість правильних багатовимірних масивів, де все є суміжним у пам'яті? Я впевнений, що всі звичайні мови вищого рівня / динамічно набрані типи з довільним вкладом списків вже трактують речі як списки списків, а не матриць, тому я вважаю, що це добре.
Пітер Кордес

@PeterCordes Звичайно, йти вперед.
Адам

Відповіді:


8

APL (Dyalog) , 20 9 байт

⊃{⌽[⍺]⍵}/

Спробуйте в Інтернеті!

Як?

/ - зменшити - візьміть крайній правий елемент у вході (масив) і застосуйте функцію з наступним лівим елементом як лівий аргумент

{⌽[⍺]⍵}- реверс у left argument( ) вимірі

- вирівняти вкладений масив



8

JavaScript (Node.js) , 58 55 53 45 байт

Збережено 8 байт завдяки @Shaggy

Вводиться як (indications)(array), де вказівки є булевим списком.

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

Спробуйте в Інтернеті!

Прокоментував

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

Просто використання rinplace, r||-1 здається, працює .
Shaggy

Було б f=([r,...b])=>a=>1/r?a.sort(_=>r).map(f(b)):aпрацювати? На моєму телефоні не можна перевірити належним чином.
Кудлатий

@Shaggy Nice! Мені подобається цей досить незвичний потік обробки.
Арнольд


5

Желе , 8 байт

”€ẋ”ṚpFv

Візьме 0-індексований список параметрів.

Спробуйте в Інтернеті!

Як це працює

”€ẋ”Ṛ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
Це жахливо. Дуже хороша!
Адам

5

R , 80 78 77 байт

Створіть виклик до витяжки R, [створивши список послідовностей, обернених там, де зазначено. Вони насправді містять нулі, які мовчки ігноруються. drop=FНеобхідно для запобігання капання по замовчуванням R в розмірностей. Нам потрібен revдзвінок на показник зворотного розміру через те, як R заповнює масиви.

-2 спасибі @Giuseppe

-1, використовуючи поточне призначення.

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

Спробуйте в Інтернеті!

Почесна згадка @JayCe, який придумав варіацію, яка отримує той же результат однакової довжини:

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

Спробуйте в Інтернеті!


1
78 байт дуже приємна відповідь!
Джузеппе

1
Дуже глибока відповідь. Мені знадобилося певний час, щоб зрозуміти це повністю. Я спробував тиражувати його без використання do.call- це довше на 83 байти, все ще публікую це тут як коментар для довідки: TIO
JayCe

Ну насправді, @JayCe, вашою чудовою відповіддю може бути також гольф на 78 байт!
J.Doe

5

Haskell, 120 119 байт

функція f приймає вхідний N-мірний список і список bool

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
Круглі дужки навколо не потрібні F r.
Ørjan Johansen

1
Посилання TIO з тестовими кейсами та 1-байтним гольфом OJ.
Khuldraeseth na'Barya

4

05AB1E , 23 11 10 байт

'€s×'R«J.V

Спробуйте в Інтернеті.

-12 байт завдяки @ Mr.Xcoder .

Введіть як 0-індексовані триєдні значення (тобто [0,2,3]), що є першим входом.

Пояснення:

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

Наприклад: якщо вхідний список індексів [0,2,3], він створить наступний рядок:

R€€R€€€R

Що:

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

Оригінальна відповідь на 23 байти:

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

Введіть як булевий список (тобто [1,0,1,1]), який є першим входом.

Спробуйте в Інтернеті.

Пояснення:

ć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

Наприклад: Якщо булівський список вхідних даних [1,0,1,1], він створить наступну рядок:

εεεR}R} }R

Що:

  ε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
Гарна відповідь, але ... е-е ... чи працював би цей 11 байт ?
Містер Xcoder

@ Mr.Xcoder Дякую! Це дійсно набагато простіше. І змогли переграти ще 1 байт, додавши кожен, а не попередньо, а потім перевернувши назад.
Кевін Крейссен

@ Mr.Xcoder Btw, чому 'x*працює, щоб повторити xn кількість разів, не використовуючи swap, але це не працює '€*? .. EDIT: Тільки в спадщині, хоча ..
Кевін Круїйссен

Застаріла версія є досить баггічною, це може бути пов’язано з тим, що вона все ще розбирається як оператор, навіть незважаючи на те, що вона перебуває в значній літералі? Не впевнений, чесно. У новій версії, тим *не менше , веде себе не так.
Містер Xcoder

3

JavaScript (Node.js) , 60 байт

Інший (рекурсивний) підхід. не перемагає відповідь Арнальда ... поки що ....

Приймає вхід як 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

Pyth , 15 байт

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

Спробуйте тут!

Прикро, обробка випадку з порожнім списком розмірів займає не менше 2 байт ... Я вважаю за краще використовувати ssзамість jk.nbut: | Передбачається, що список, який потрібно трансформувати, може бути наданий у рідному синтаксисі Pyth, як рядок. Я написав перетворювач в синтаксис Pyth, щоб полегшити тестування. У тому нещасному випадку, що ОП вирішить не допустити цього, 17-байтовий "виправить" це:

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

3

Japt , 15 14 байт

З деяким натхненням від рішення Арнальда .

Приймає ознаки як перший вхід, як булева масив 1s та 0s.

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

Спробуй це


Пояснення

                   :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

Чистота , 122 112 байт

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

Спробуйте в Інтернеті!

Версія відповіді Даміена Хаскелла з використанням системи гольфістів типу Clean. Дійсно показує велику схожість між двома мовами.

Пояснили:

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

(неперевірено, але я вважаю правильним. Вихід ASM компілятора виглядає так, як я очікую. Оновиться, якщо / коли знайду час написати тестовий ремінь, який створює та друкує цю структуру даних.)

GNU C ++ (портативний) 148 байт

#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 = покажчик і випадає з недійсної функції UB) 120 байт

#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));}};

Це структура лічильника глибини, довжини, масиву {цілих чисел або покажчиків}. У нижньому рівні цього небінарного дерева ( depth==0) масив intptr_t- це масив цілих чисел. На вищих рівнях це struct m*зберігається в intptr_t. Траверсал приймає акторський склад.

R()Зворотна функція є членом , тому що економить оголошуючи ARG, і економить багато p->синтаксису для посилання на елементи цієї структури по порівнянні з неявним thisпокажчиком.

Єдине розширення GNU - це гнучкий масив C99 для створення структури змінного розміру , яка підтримується в C ++ як розширення GNU. Я міг би використати *aчлен, який вказує на окремо виділений масив, і це буде просто ISO C ++. (І це насправді збереже байт, не вимагаючи інших змін). Я написав це як макет / довідкова реалізація для версії ASM.


Коротша версія з просто intтакож оголошує R()повернення intзамість void. Ці два біти хакера не пов'язані між собою; це лише "працює принаймні на одну реалізацію" версії.

Він повинен добре працювати на 32-бітних цілях (де intможна утримувати вказівник), якщо ви компілюєте з gcc7 або старшими або відключаєте оптимізацію. ( gcc8 -O3Передбачається , що виконання не може досягти дна в не- voidфункції , так як це було б UB.) X86 gcc -m32 -O3має працювати нормально з gcc7, як на Godbolt , де я включив обидві версії (в різних просторах імен), яка не є членом-функції версії .

Безумовно

Функція arg, int r[]- це масив 0 / ненульових цілих чисел, які вказують, чи слід міняти задану глибину, починаючи з самого зовнішнього рівня.

#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

Коли ми повторюємось, ми проходимо r+1, тому перевірка поточної глибини - це завжди *r.

Більш рання версія просто пройшла rбез змін та перевірена r[d]. З гнучким членом масиву мені потрібно було зберігати якийсь індикатор останнього рівня, оскільки a[]це не вказівник, це справжній масив без опосередкованості. Але з intptr_t *aчленом я не міг би просто бути таким nullptrдля рівня листя, тому що я хочу, щоб це були цінності.

Зміна поточного рівня до або після обходу дерева не має значення. Я не намагався це робити під час .

Я не впевнений, що std::reverseварто рахувати байт проти ручного циклу, особливо якщо я можу працювати R()над вказівником кожного разу точно десь усередині цього циклу. Але тільки якщоd!=0


Ого, вражаюче.
Adám

@ Adám: спасибі, після того, як я це написав, це було надзвичайно природно і добре. Мені цікаво побачити, що я можу зробити в машинному коді x86: P
Пітер Кордес

1

Математика, 7 байт

Reverse

Функція. Надайте йому вкладений список як перший аргумент, а на основі 1-го списку рівнів / розмірів потрібно повернути як другий аргумент. Спробуйте в Інтернеті!

Нарешті, ще одне завдання, коли Mathematica має вбудований!


шари для реверсування ? TIO посилання, можливо?
Adám

@ Adám Тобто, перелік розмірів, які потрібно повернути, як правило, називають рівнями в Mathematica.
LegionMammal978
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.