Версия PascalABC.NET 3.2

Сделали ElementsByRow ElementsByCol ElementsWithIndexes SetRow SetCol Пример по ConvertAll:

begin
  var a := Arr(2,3,4);
  var b := a.ConvertAll(x->x+0.1);
  b.Println
end.
1 лайк

Спасибо большое, все попробовал, работает. ElementsByRow и ElementsByCol - насколько я понял, можно воспринимать теперь, как синтаксический сахар:

Тем не менее, удобно и понятно, особенно для новичков на первых порах. По-моему, “mast have”

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

Теперь несколько не по теме этого сообщения. Понемногу делаю библиотеку для работы с матрицами. Именно матрицами в математическом смысле. попробовал перегрузить операции - получилось для сложения, вычитания и умножения. Произведений у матриц, как известно два: векторное и скалярное (я рассматриваю векторы, как матрицы, т.е. векторы-строки и векторы-столбцы). Векторное используется все же чаще и для него я сохранил традиционную операцию * А для скалярного начал думать, какой символ взять. Выяснил, что символы можно брать только от тех операций, которые уже определены в Паскале. В справке прочитал:

Перегружать можно все операции за исключением @ (взятие адреса), as, is, new. Кроме того, можно перегружать специальные бинарные операции +=, -=, *=, /=, не возвращающие значений. Перегружать можно только еще не перегруженные операции.

Не знаю, перегружена ли операция ^ , но этим символом воспользоваться не удалось. И еще, в справке увидел операцию => - это что?

Таблица приоритетов операций @, not, ^, +, - (унарные), new 1 (наивысший) *, /, div, mod, and, shl, shr, as, is 2 +, - (бинарные), or, xor 3 =, <>, <, >, <=, >=, in, => 4 ?: 5 (низший)

Вот это совсем не хочется делать в стандартной библиотеке. Эта операция - очень специфическая и черевата ошибками в случае несовпадения длин. Так что давайте оставим это пользователям

Это небезопасные операции. Как и превращение последовательности в матрицу.[quote=“RAlex, post:145, topic:1177”] И еще, в справке увидел операцию => - это что? [/quote]

Её уже нет. Уберем в следующей версии справки

Это будет означать лишь одно - невозможность решить большую часть задач без применения циклов. И тогда смысл всех этих манипуляций с матрицами в функциональном стиле становится не особо понятным: надо изучать приличный объем новаций, а все равно мы возвращается к циклу. Небезопасные операции… а давайте вспомним те же реляционные СУБД и взглянем на матрицу, как на таблицу. Сначала в SQL начали думать о выборках и появился “безопасный” SELECT. А потом пришли к тому. что нужны и INSERT, и DELETE, и UPDATE. И в работе с СУБД от SQL-выборки в “безопасные” курсоры read-only пришли к обновляемым курсорам. Ибо без этого слишком ущербно и однобоко получается.

Вот простая и достаточно характерная задача. В двумерном массиве, заполненном случайными целыми числами из интервала [10;50], заменить все элементы, расположенные между минимальным и максимальным значениями, на ноль. Матрицу рассматривать в порядке по строкам. Я не вижу другого разумного варианта, кроме циклов.

// PascalABC.NET 3.2, сборка 1415 от 25.03.2017

begin
  var m,n:integer;
  Read(m,n);
  var a:=MatrRandom(m,n,10,50);
  a.Println(3); Writeln;
  var s:=a.ElementsByRow.ToArray;
  var i1:=s.IndexMax;
  var i2:=s.IndexMin;
  if i1>i2 then Swap(i1,i2);
  if i2-i1>1 then begin
    var q:=(s[:i1+1]+ArrFill(i2-i1-1,0)+s[i2:]).ToArray;
    for var i:=0 to m-1 do
      for var j:=0 to n-1 do
        a[i,j]:=q[n*i+j];
    a.Println(3)
    end
end.

Почему-то тут ностальгически вспомнился Fortran-IV, который позволял обращаться к n-мерному массиву, как к одномерному, используя так называемый “обобщенный индекс”.

В современных языках это не делается.

Стандартные библиотеки содержат только безопасные операции. Всё, что Вы перечисляете, относится к тому, старому миру, когда безопасности не придавали должного внимания.

Сейчас подобные методы реализует пользователь - никакая стандартная библиотека ответственность на себя не возьмёт. Или использует циклы.

Можно реализовать некоторые методы, которые преобразуют двумерный массив в такой же двумерный массив или выполняют операцию foreach.

Да хотя бы сделайте ConvertAll. Foreach - это ВЫБОРКА, а проблема в массовой модификации данных. Вот, пожалуйста, еще задача. Простейшая. А как без цикла?

Pascal. Двумерный массив. Получить новую матрицу путем деления всех элементов данной матрицы на ее наименьший по модулю элемент

Ненормально писать коды, подобные нижеследующему

  for var i:=0 to a.RowCount-1 do
    a.SetRow(i,(a.Row(i).Select(x->2*x)).ToArray);

Уж лучше тогда работать с матрицами в обычном “старом стиле”, забыв про функциональное программирование, В моей голове вполне укладывается концепция безопасности операций. Но выборка при этом может быть совершенно произвольной! Почему же не сделать возможности генерации некоего двумерного массива на базе последовательности? В чем тут опасность? Если длина последовательности k и мы желаем создать двумерный массив, содержащий n столбцов, неужели так невозможно найти число строк m = k div n и разрешить формирование, если k mod n = 0 ? Вовсе же нет необходимости вписываться в какой-то существующий массив или обеспечивать некий заранее заданный размер - все динамически вычисляется - и размер, и возможность создания.

Это всё чудесно делает NumPy в Python. Эти операции относятся к тому миру, в котором всё надо делать быстро. Если ваш “современный мир” не в состоянии обеспечить и эффективность, и безопасность, то всё пропало, мир пришёл не туда.

1 лайк

Посмотрю

А как бы Вы хотели?

Например, как-то вот так:

// PascalABC.NET 3.2, сборка 1415 от 25.03.2017

function MatrUnpack<T>(s:sequence of T;cols:integer):array[,] of T;
begin
  var r:=new T[s.Count div cols,cols];
  var n:=r.Length;
  var k:=0;
  foreach var elem in s do begin
    r[k div cols, k mod cols]:=elem;
    k+=1;
    if k=n then break
    end;
  Result:=r
end;

function MatrUnpack<T>(Self:sequence of T;cols:integer):array[,] of T;
  extensionmethod;
begin
  var r:=new T[Self.Count div cols,cols];
  var n:=r.Length;
  var k:=0;
  foreach var elem in Self do begin
    r[k div cols, k mod cols]:=elem;
    k+=1;
    if k=n then break
    end;
  Result:=r
end;

begin
  var a:=MatrRandom(4,5,10,50);
  a.Println(3); Writeln;
  var b:=a.ElementsByRow.Select(x->x.IsOdd?x+1:x-1).MatrUnpack(5);
  b.Println(3)
end.

 17 22 40 31 12
 28 41 13 43 19
 32 20 13 45 46
 30 28 27 35 50

 18 21 39 32 11
 27 42 14 44 20
 31 19 14 46 45
 29 27 28 36 49

P.S. В качестве первого обзора NumPy

Сделал:

begin
  var a := MatrRandom(2,3);
  a.Println;
  a.Transform(x->x+1);
  a.Println;
  var b := a.ConvertAll(x->x+0.1);
  b.Println;
end.

Спасибо, уже хоть что-то! ))) По крайней мере можно хоть теперь заполнить вещественную матрицу случайными целыми …

Этого я точно не делал…

Просто напишите в своем примере var b := a.ConvertAll(x->x+0.0);

Заглянул в NumPy. Никогда меня Пайтон не увлекал, поскольку после давнишних мучений с позициями на перфокартах в Fortran-IV возникла стойкая идиосинкразия к языкам с синтаксисом, зависящим от отступов и позиций на строке исходного текста. Обнаружил две интересные функции - reshape, возвращающую массив с иным рангом (формой) и resize, выполняющую эту операцию с исходным массивом. Resize забраковал, будучи определенным образом солидарен с идеологией @Admin в части минимума “опасных” операций над исходными объектами в программе. Почему не полностью солидарен? Считаю, что любая идеология в языке программирования, если это не специализированный язык для особо надежных вычислений (как в свое время преподносился язык Ада, разрабатываемый по заказу министерства обороны США) не должна входит в противоречие с удобством программирования и тут поддерживаю высказанное выше @bravit А вот reshape() - именно то, что нужно “для полного счастья”. Ведь ничто не мешает написать a:=a.Reshape(…); В NumPy параметры reshape - это желаемые размерности возвращаемого массива, причем исходный массив рассматривается в порядке “по строкам”. Но есть занятная опция [,order=‘F’], которая рассматривает исходный массив в стиле Fortran, т.е. по столбцам.

А что, кстати, насчет современных реализаций языка Fortran? Оказывается, в нем тоже есть reshape(), только не в какой-то библиотеке, а прямо в реализации. И делает эта функция то же самое, что и в NumPy - меняет форму массива, На самом деле меня посетила мысль, что эту прекрасную функцию в NumPy просто “слизали” из Фортрана. А еще, есть у фортрановской reshape одна удобная, как теперь говорят, “фишка”. Если задать среди параметров количество элементов в некотором измерении в виде -1, то оно будет автоматически вычислено исходя из размеров исходного массива. Для двумерного массива это означает возможность явно задавать только длину по одному из измерений, что я нахожу весьма удобным.

Резюме: Современные реализации Fortran и библиотека NumPy для Python имеют функцию Reshape(), возвращающую исходный массив с измененной формой. В частности, она может сформировать массив нужной формы из одномерного массива. Может быть, все же стоит сделать такую же функцию (метод), работающую по следующим правилам:

  1. если имеется исходный одномерный массив или последовательность типа Т, функция (метод) возвращает массив типа Т с измененной формой;
  2. если исходный массив типа Т имеет больше одного измерения, он приводится к последовательности типа Т, а затем к этой последовательности применяется правило 1).
  3. если в исходном массиве или последовательности в соответствии с задаваемыми размерами остаются лишние элементы, они не используются (это позволяет делать некоторый аналог срезов и некоторые другие вещи)
  4. если в исходном массиве или последовательности в соответствии с задаваемыми размерами недостает элементов для заданного преобразования формы, возникает исключение. И это правильно, потому что нельзя заставлять программу фантазировать, что программист имел в виду и дополнять матрицу некими “нулями” или чем-то еще, а предварительно дополнить одномерный массив или последовательность до нужной длины ничто не мешает.
1 лайк

Ну то есть, если коротко, Вы хотите Reshape для одномерных массивов для преобразования их в двумерные?

Как минимум. А вообще Reshape и для двумерных тоже:

И потом, наверно не совсем правильно говорить, что я этого для себя хочу: у меня есть библиотека, в которой это уже давно реализовано, может не так, как надо для общего случая, может криво в чем-то, но работает). Я предлагаю добавить в язык Reshape, чтобы дать возможность работать с матрицами в функциональном стиле, а не переходить к традиционным циклам. Чтобы и тут по простоте и удобству написания программы Паскаль вдруг не проиграл тому же Питону.

Можно, конечно, все это и не делать, но вот информация, поясняющая причину наличия в языках программирования операций not, or, and, xor… а не одной единственной стрелки Пирса:

(по материалам Интернет)

Так есть же уже a.ElementsByRow. А a.Reshape с какими параметрами Вы собираетесь использовать чтобы преобразовать её к одномерному массиву?

  1. Reshape для двумерного массива можно использовать для того, чтобы на основе одного двумерного построить другой двумерный же, например, для a[10,2].записать a.Reshape(4,5) и получить массив [4,5].
  2. Reshape для преобразования двумерного массива в одномерный для a[4,3] может выглядеть как a.Reshape(12). Но двумерными останутся a.Reshape(1,12) и a.Reshape(12,1). Можно, например, первые 9 элементов от a[4,5] взять вызовом a.Reshape(3,3) или a.Reshape(9) поместить в a[1,9].
  3. И, конечно, главное - это Reshape для преобразования последовательности или массива а[n] в b[p,q]

a.Reshape(12) для преобразования в одномерный мне сильно не нравится. Я понимаю, что Вы решили объединить мух и котлет ради призрачной унификации. Претензии к этому решению - такие.

  1. Непонятно, по строкам или по столбцам это делать. В этом отношении a.ElementsByRow четко говорит, что делать. Только не говорите, что второй параметр перечислимого типа надо в конец тулить.
  2. Сильно не нравится, что можно взять не все элементы. Это для каких таких задач УНИВЕРСАЛЬНЫЙ (!) метод должен брать начальный кусок одномерного массива чтобы создать двумерный???