Версия PascalABC.NET 3.2

Конечно, по строкам. В Паскале многомерные массивы построчно хранятся, точно так же, как в Фортране исторически многомерные массивы хранятся по столбцам. У Вас же не возникает вопроса, по строкам или по столбцам работает ReadMatrInteger? Если понадобится по столбцам - вот тогда используется a.ElementsByCol, В то же время, a.ElementsByRow можно оставить в качестве синтаксического сахара. И a.ElementsWithIndexes остается (кстати, Вы не же задаете вопрос, по строкам это делается или по колонкам). Не могу согласится с “мухами и котлетами”: если уж это Reshape(), то изменение формы массива должно быть именно универсальным. И если графически элементы двумерного массива выглядят, как прямоугольник, то этот прямоугольник можно произвольно деформировать при условии, что он остается прямоугольником, в котором количество образующих его элементов не увеличивается. В том числе, этот прямоугольник можно вытягивать до горизонтальной линии (строки) или вертикальной (колонки). Но и в NumPy, и в Fortran можно с помощью Reshape менять также и ранг (количество измерений) массива - это тоже входит в понятие изменения формы. В конце-концов, в обоих этих языках Reshape универсальный метод (функция), который работает с массивами произвольной размерности и нет проблемы из a[6,8] получить b[2,6,4], с[48] или d[12,4]. Почему в Паскале это должно быть кастрировано?

Тут я Вас понимаю. Но поймите и Вы: я вовсе не предлагаю создавать двумерный массив из куска одномерного: если это понадобится, всегда можно воспользоваться соответствующим срезом при условии, что Reshape умеет создавать матрицу из всех элементов одномерного массива или последовательности. Но срезов для двумерных массивов у нас нет и не знаю, будут ли.

Пусть дан массив a[n,n+1], (к примеру, это расширенная матрица для системы n линейных уравнений и требуется взять n первых столбцов, чтобы получить и затем обратить матрицу системы). Сейчас это делается примерно так:

var tmp:=a.ElementsByCol.ToArray[:n*n];
  var b:=new integer[n,n];
  for var i:=0 to n-1 do
    for var j:=0 to n-1 do
      b[j,i]:=tmp[i*n+j];

А чем плохо было бы это сделать вот так: var b:=a.Transpose.Reshape(n,n).Transpose; ? Я скажу чем: странным двукратным транспонированием. А появляется оно потому, что не случайно и NumPy, и Fortran умеют делать Reshape и построчно, и поколонно. Да-да, как раз третьим опциональным параметром, меняющим порядок рассмотрения измерений массива по умолчанию. Который, насколько я понимаю, Вы назвали “не надо тупить в конец”. И да, нужно еще научить Transpose быть расширением, возвращающим матрицу.

Хотя, у нас есть функция Length(массив, номер измерения), как раз для n-мерного массива возвращает количество элементов по нужному измерению, а для одномерных по этот второй параметр может опускаться. И что мешает также опционально написать var b:=a.Reshape(n,n,1) и разом решить проблему?

Понадобилось упорядочить матрицу по определенному номеру колонки. Попытался сделать в функциональном стиле. Сравнительно долго ломал голову, но нормального решения не нашел. Нашел половинчатое, но какое-то оно некрасивое.

// PascalABC.NET 3.2, сборка 1417 от 28.03.2017
begin
  var a:=MatrRandom(4,3,10,50); a.Println;
  // для примера сортировка по 2-й колонке (индекс колонки 1)
  var v:=a.Col(1).Select((v,i)->(v,i)).OrderBy(vi->vi[0]).ToArray;
  var b:=new integer[a.RowCount,a.ColCount];
  for var i:=0 to a.RowCount-1 do b.SetRow(i,a.Row(v[i][1]));
  a:=Copy(b);
  Writeln; a.Println;
end.

Для сравнения приведу традиционное решение с пузырьковой сортировкой, как наиболее просто реализуемым видом обменной сортировки. Обменной - чтобы задействовать процедуру SwapRows.

// PascalABC.NET 3.2, сборка 1417 от 28.03.2017
begin
  var a:=MatrRandom(4,3,10,50); a.Println;
  // для примера сортировка по 2-й колонке (индекс колонки 1)
  for var i:=0 to a.RowCount-2 do
    for var k:=0 to a.RowCount-2-i do
      if a[k,1]>a[k+1,1] then a.SwapRows(k,k+1);
  Writeln; a.Println
end.

Может быть, у кого-то возникли “мысли по поводу”?

Ну, матрица - это не последовательность, поэтому функциональных методов мало. Отсортировать-то несложно:

a.Rows.OrderBy(x->x.ToArray[1])

Сложно потом всё в матрицу собрать

В этом и проблема вся! Особенно, если посмотреть, какого типа получится результат. Потому я все еще надеюсь, что средства для “сборки” матрицы, для её реконфигурации рано или поздно появятся. О “встроенной” сортировке речи нет, это частная задача, её делать незачем.

А приведите код, который бы решал эту проблему

Эту - это какую? Сортировку? Если да, то пока что я не пойму, что происходит: не удается из Вашего кода [quote=“Admin, post:166, topic:1177, full:true”] a.Rows.OrderBy(x->x.ToArray[1]) [/quote]

получить обычный одномерный массив. Ни через Select, ни через SelectMany. Не пойму в чем дело, но SelectMany, который обычно понижал уровень массива на ранг, не срабатывает. Т.е. все время остается что-то вроде array of sequence T или, в лучшем случае array of sequence integer. Если получить просто array of integer, то собрать из него двумерный массив не проблема. Может, что-то c SelectMany не так?

А если Вы возвращаетесь к теме Reshape, я ведь уже предлагал вариант реализации. Но могу попробовать и еще предложить в случае, если сформулировать требования, которым должен удовлетворять код.

Нет, я не возвращаюсь никуда - я хочу чтобы Вы для этого кода сформулировали, как Вы бы хотели решать задачу

Помогите мне избавится от этого сумасшествия!!! Я НЕ МОГУ сделать из результата исполнения этого кода обычный array of integer! Никак не получается. Только .Print позволяет избавиться от этого дурацкого array of sequence ! Либо дыра в SelectMany, либо в моей голове.

А пока я решаю эту задачу так:

begin
  var a:=MatrRandom(4,3,10,50); a.Println;
  var n:=2; // для примера: сортировка по n-й колонке (индекс n-1)
  var v:=a.Col(n-1).Select((x,i)->(x,i)).OrderBy(xi->xi[0]).ToArray;
  var b:=Copy(a);
  for var i:=0 to a.RowCount-1 do
    a.SetRow(i,b.Row(v[i][1]));
  Writeln; a.Println
end.

Решение банальное: Строится вектор ключей сортировки, сортируется, исходный массив переписывается в рабочий и затем строки из рабочего массива в соответствии с ключами переписываются в исходный. Если речь о процедуре, можно переписывать в соответствии с вектором исходный массив в выходной.

Где-то вот так примерно:

function MatrSortByCol<T>(Self:array [,] of T; n:integer):
    array [,] of T; extensionmethod;
begin
  Result:=new T[Self.RowCount,Self.ColCount];
  var v:=Self.Col(n).Select((x,i)->(x,i)).OrderBy(xi->xi[0]).ToArray;
  for var i:=0 to Self.RowCount-1 do
    Result.SetRow(i,Self.Row(v[i][1]))
end;

begin
  var a:=MatrRandom(4,3,10,50); a.Println; Writeln;
  // для примера: сортировка по 2-й колонке (индекс 1)
  a.MatrSortByCol(1).Println
end.

P.S. Ваши видеолекции по повышению квалификации определенно оказались полезны))

Я думаю, Вас интересует вот это:

var q := a.Rows.OrderBy(x->x.ToArray[1]).Select(x->x.ToArray).ToArray;

.Select(x->x.ToArray).ToArray; позволяет последовательность последовательностей превратить в массив массивов

А то, что я хотел, я написал. Могу теперь еще подумать, как получить отсортированную матрицу из этого q. Но что-то мне шепчет, что решение будет хуже того, что я выше предложил под условным именем MatrSortByCol

Вот такое решение я предлагаю:

begin
  var a := MatrRandom(4,3,10,50); 
  a.Println; Writeln;
  var b := a.Rows.Select(x->x.ToArray).ToArray;
  Sort(b,(x,y)->x[0]-y[0]);
  a.Fill((i,j)->b[i,j]);
  a.Println;
end.

Мне пришлось добавить Fill в стандартную библиотеку. И для одномерных массивов тоже. Я думаю, что уместно сделать преобразование двумерного массива в массив массивов и наоборот (в этом случае проверять размерности и при несовпадении - исключение). Что-нибудь вроде

a.RowsArray

И тогда Ваше преобразование в одномерный массив станет не нужно

Так вроде же Fill для одномерных массивов и так есть?. А вот для двумерных, возможно, Вы куда-то и добавили Fill, но в доступной на данный момент для скачивания сборке 1421 от 22.04.2017 строка

a.Fill((i,j)->b[i,j]);

вызывает ошибку компиляции " Неизвестное имя ‘Fill’ "

Да, выложил с Fill. Поторопился…

1 лайк

Спасибо, работает. Но, быть может, в данном случае было бы более последовательным писать так:

begin
  var a := MatrRandom(4,3,10,50); 
  a.Println; Writeln;
  var b := a.Rows.Select(x->x.ToArray).ToArray;
  Sort(b,(x,y)->x[0]-y[0]);
  a.Fill((i,j)->b[i][j]);
  a.Println;
end.

Все же у нас получается у b тип System.Int32[][], поэтому и b[i][j]

Из любопытства сравнил производительность.

begin
  Milliseconds;
  var a:=MatrRandom(100,100,-1000,1000);
  for var i:=1 to 10000 do begin
    var b := a.Rows.Select(x->x.ToArray).ToArray;
    Sort(b,(x,y)->x[1]-y[1]);
    a.Fill((i,j)->b[i,j]);
    end;
  var t:=MillisecondsDelta;
  Writeln(t);
end.

Время выполнения 6859

function MatrSortByCol<T>(Self:array [,] of T; n:integer):
    array [,] of T; extensionmethod;
begin
  Result:=new T[Self.RowCount,Self.ColCount];
  var v:=Self.Col(n).Select((x,i)->(x,i)).OrderBy(xi->xi[0]).ToArray;
  for var i:=0 to Self.RowCount-1 do
    Result.SetRow(i,Self.Row(v[i][1]))
end;

begin
  Milliseconds;
  var a:=MatrRandom(100,100,-1000,1000);
  for var i:=1 to 10000 do
    a.MatrSortByCol(1);
  var t:=MillisecondsDelta;
  Writeln(t);
end.

Время выполнения 2125

Понятно, что это всего лишь 0.21с против 0.69с на матрице 100х100, но все же тройная разница…

Ну тогда так:

begin
  Milliseconds;
  var a:=MatrRandom(100,100,-1000,1000);
  for var i:=1 to 10000 do 
  begin
    var b := ArrGen(a.RowCount,k->a.Row(k));
    Sort(b,(x,y)->x[1]-y[1]);
    a.Fill((i,j)->b[i][j]);
  end;
  var t:=MillisecondsDelta;
  Writeln(t);
end.
2 лайка

А вот это “правильное решение” - 2766, т.е. 0.28c, вполне сопоставимое с 0.21с, но зато имеющее более короткую запись. Плюс, чисто в функциональном стиле.

3 сообщения перенесены в новую тему: Что-то не работает, потом напишу

Цикл loop появился :slight_smile:

Спасибо, будем иметь в виду. Не знаю, из какого языка он пришел в таком синтаксисе, но жаль, что не из Бейсика))) Там, конечно, вещь…

DO [WHILE булево] [UNTIL булево]
   тело цикла
LOOP [WHILE булево] [UNTIL булево]

Можно сконструировать, что угодно.

Хотя, если подумать, do while и repeat until и так есть. Пожалуй, хотелось бы только “эстетический вариант” бесконечного цикла

loop
   тело цикла