p
является (полиморфной) функцией типа, принимающей перестановку в качестве списка Int
s, и вложенный список, представляющий многомерный массив Int
s.
Вызовите как p [2,1] [[10,20,30],[40,50,60]]
, однако, если заданный по умолчанию тип не удался, вам, возможно, придется добавить аннотацию типа вроде :: [[Int]]
(вложенную соответствующим образом), дающую тип результата.
import Data.List
class P a where p::[Int]->[a]->[a]
instance P Int where p _=id
instance P a=>P[a]where p(x:r)m|n<-p r<$>m,y:z<-sort r=last$n:[p(x:z)<$>transpose n|x>y]
Попробуйте онлайн!
Задачи игры в гольф с вложенными массивами произвольной глубины немного неудобны в Haskell, потому что статическая типизация имеет тенденцию мешать. Хотя списки на Haskell (с точно таким же синтаксисом, что и в описании задачи) могут быть вложены просто отлично, списки с различной глубиной вложенности относятся к несовместимым типам. Кроме того, стандартные функции синтаксического анализа Haskell требуют знания типа значения, которое вы пытаетесь проанализировать.
В результате кажется неизбежным, что программа должна включать в себя объявления, относящиеся к типу, которые являются относительно многословными. Для части игры в гольф я остановился на определении типа P
, такого, который p
может быть полиморфным по типу массива.
Между тем, тестовое устройство TIO показывает способ обойти проблему разбора.
Как это устроено
Подводя итог сути этого алгоритма: он выполняет пузырьковую сортировку в списке перестановок, транспонируя соседние измерения, когда соответствующие индексы перестановки меняются местами.
Как указано в class P a
объявлении, в любом случае p
принимает два аргумента: перестановку (всегда типа [Int]
) и массив.
- Перестановка может быть дана в форме в описании вызова, хотя способ работы алгоритма, выбор индексов является произвольным, за исключением их относительного порядка. (Так что и 0- и 1-основанная работа.)
- База
instance P Int
обрабатывает массивы измерения 1, которые p
просто возвращаются без изменений, поскольку одно измерение может быть отображено только на себя.
- Другой
instance P a => P [a]
определяется рекурсивно, вызывая p
с подмассивами измерения n , чтобы определить его для массивов измерений n + 1 .
p(x:r)m
первый вызов p r
рекурсивно для каждого элемента m
, давая массив результатов, n
в котором все измерения, кроме первого, были правильно переставлены относительно друг друга.
- Оставшаяся перестановка, которая должна быть выполнена,
n
определяется как x:y:z = x:sort r
.
- Если
x<y
тогда первое измерение n
уже правильно размещено и n
просто возвращается.
- Если
x>y
, то первое и второе измерение n
нужно поменять местами, что и делается с помощью transpose
функции. Наконец p(x:z)
, применяется рекурсивно к каждому элементу результата, гарантируя, что исходное первое измерение перемещается в правильную позицию.
exec
(сохраняя два байта) , так как это утверждение в Python 2.