Многомерное обращение

23

Учитывая N-мерный ортогональный (не рваный) массив неотрицательных целых чисел и указание, какие измерения нужно повернуть, вернуть массив, но обратный по этим измерениям. Указание может быть дано как логический список длины N или список подмножества первых N измерений, проиндексированных от 0 или 1.

Пожалуйста, укажите ваши форматы ввода. Пояснения к коду высоко ценятся.

Проходной пример

Нам дан 2-х слойный 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работает с произвольными массивами, но заботится только о первом уровне), универсальные или рекурсивные классы (классы типа / объекта в зависимости от функционала) или ООП, но аналогичный вариант использования). Последние два обычно намного более многословны.
1839 года
Можем ли мы хранить матрицу в виде массивов указателей на указатели (в C или asm) вместо надлежащих многомерных массивов, где все непрерывно в памяти? Я почти уверен, что все обычные языки высокого уровня / динамически типизированные с произвольным вложением списков уже воспринимают вещи как списки, а не как матрицы, так что я собираюсь предположить, что все в порядке.
Питер Кордес
@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
Arnauld
источник
Кажется, просто работает rвместо . r||-1
Лохматый
Будет f=([r,...b])=>a=>1/r?a.sort(_=>r).map(f(b)):aработать? На моем телефоне так не могу проверить правильно.
Лохматый
@ Шэгги Хорошо! Мне нравится этот довольно необычный процесс обработки.
Арно
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)

Попробуйте онлайн!

j.doe
источник
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)
Damien
источник
1
Вам не нужны круглые скобки F r.
Орджан Йохансен
1
Связь TIO с контрольными случаями и однобайтовым гольфом OJ.
Хулдраесет на'Барья
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 Кстати, почему 'x*работает, чтобы повторить xколичество раз без использования sWAP, но это не работает '€*? .. РЕДАКТИРОВАТЬ: Хотя только в наследство ..
Кевин Круйссен
Устаревшая версия довольно глючная, это может быть связано с тем, что она все еще анализируется как оператор, даже если она находится в символьном литерале? Не уверен, чтобы быть честным. В новой версии, тем *не менее , не ведет себя так же.
г-н 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.n: | Предполагается, что преобразуемый список может быть задан в собственном синтаксисе Pyth как строка. Я написал конвертер в синтаксис Pyth, чтобы упростить тестирование. В неудачном случае, когда OP решает не допустить этого, 17-байтовый «исправит» это:

.v+jk.n*\_*L\ME\E
Мистер Xcoder
источник
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)

Попробуйте онлайн!

Версия ответа Haskell Дэмиена, использующая систему чистоты для игры в гольф. Действительно показывает обширные сходства между двумя языками.

Разъяснение:

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
источник
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()reverse является функцией-членом, потому что она сохраняет объявление 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, где я включил обе версии (в разных пространствах имен) и версию, не являющуюся членом-функцией ,

Ungolfed

Функция 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: спасибо, это удивительно естественно и хорошо после того, как я написал это. Мне любопытно посмотреть, что я могу сделать в машинном коде x86: P
Питер Кордес
1

Mathematica, 7 байтов

Reverse

Функция. В качестве первого аргумента укажите вложенный список, а в качестве второго аргумента - список уровней / измерений на основе 1. Попробуйте онлайн!

Наконец, еще одна проблема, где Mathematica имеет встроенную функцию!

LegionMammal978
источник
слои поменять ? Ссылка TIO может быть?
Адам
@ Adám То есть список измерений, которые нужно изменить, обычно называемые уровнями в Mathematica.
LegionMammal978