Ошибки PascalABC.NET

Partition - это не совсем то. Есть масса задач, когда надо протабулировать функцию от некоторого a c шагом h и до тех пор, пока очередная итерация аргумента не превысит b. А partition в общем случае изменит точки табуляции, потому что он не шаг держит, а разбивает весь интервал на равные отрезки. Например, попробуйте выполнить табуляцию для a от 0.3 с шагом 0.031 до 0.5, соблюдая именно шаг - в этой ситуации partition “ни о чем”.

В общем, это всего лишь аналог достаточно распространенного оператора цикла for x:=a to b step h (в нотации BASIC) или for x:=a step h until b еще в АЛГОЛ-60.

Хочется как-то “свернуть” цикл вида

var x:=a;
while x<=b do begin
   ...
   x+=h
   end;

Тот, на который нас обрёк Н.Вирт, кастрировав Алгол-60.

P.S. Не так давно переводил с Алгол-60 (там прекрасная библиотека исходников) процедуру обращения матрицы методом Гаусса-Жордана и когда встретился фрагмент вслух сказал очень непристойное слово, имея в виду как раз Н.Вирта…

Это Вы вначале попробуйте пройти от 1.0 до 2.0 с шагом 0.1. Я уверен - результат Вас неприятно удивит

Это - скрытая ошибка

Не удивит: я давно (лет так 45 примерно) знаком с фокусами неточного представления чисел в ЭВМ. Поскольку начинал работать с техникой, где даже ассемблера еще не было - только восьмеричные коды. Если у меня шаг нецелый, то пусть в меня сколько угодно кидают камни, но я поступаю так:

begin
  var (x,h):=(1.0,0.1);
  while x<=2+h/2 do begin
    Print(x);
    x+=h
    end
end.

Но реплика об очень непристойном слове для случая целочисленных параметров цикла все равно остается…

Ну ладно, есть же

begin
  foreach var x in Range(1,10,2) do
    Print(x)
end.

И вообще для генерации последовательностей есть

begin
  var (a,h) := (1.0,0.1);
  foreach var x in SeqGen(10,a,x->x+h) do
    Print(x)
end.

Я выкрутился, конечно, но все же…

var s:=Range(0,r)+Range(p,n-1);
foreach var k in s do
foreach var j in s do
  a[k,j]:=a[k,j]-b[j]*c[k];

Ну подождите, почему же выкрутились? По-моему, очень элегантно: в памяти последовательности не хранятся. И - тут нет шага, есть скачок

Выкрутился - потому что моделировал алголовские циклы (см. выше), а прямой замены нет, за что Н.Вирту “спасибо”. С другой стороны, нельзя в очередной раз не сказать настоящее спасибо разработчикам за прекрасные возможности, заложенные в эту версию языка.

В регулярных выражениях PCRE есть квантификатор {n,} (>=n повторений) и {,n} (<=n) С удивлением обнаружил, что PABC.NET не поддерживает второй :frowning:

begin var s := '123'; println(RegEx.isMatch('123', '\d{3,}')); //true println(RegEx.isMatch('123', '\d{,3}')); //false end.

Спасибо, давно искал))

А Вы проверили - библиотеки .NET его вообще поддерживают? Microsoft дает полное описание того, что поддерживается и в каком формате.

@admin Ошибки с примером в справке по индексированному слову where (поиск минимального эл-та в массиве):

  • использована несуществующая переменная max;
  • перепутан знак сравнения в операторе if, т.е. в текущем виде ф-ция будет искать максимальный элемент.

Кстати, кто-нибудь может мне объяснить, почему условие в виде if Result.CompareTo(a[i]) > 0 всегда возвращает true (и, соответственно, результатом функции будет просто последний элемент массива, а не ожидаемый минимальный), а если записать его в инверсии: if a[i].CompareTo(Result) < 0, то все работает как положено?

function MinElem<T>(a: array of T): T;
  where T: IComparable<T>;
begin
  Result := a[0];
  for var i := 1 to a.Length - 1 do
    if Result.CompareTo(a[i]) > 0 then
      Result := a[i];
end;

@admin В PABCSystem, строки 9152 и 9165 – опечатка в описании функций LastIndexMax:

  • /// Возвращает индекс последнего минимального элемента
  • /// Возвращает индекс последнего минимального элемента начиная с позиции start

+ во многих случаях пропущены запятые перед словом если в описаниях

@admin На мой взгляд функции LastIndexMin и LastIndexMax в вариантах с дополнительным параметром start вообще сейчас реализованы некорректно – они производят поиск в первой половине массива, т.е. от начала и до позиции start, а не с позиции start и до конца, как ожидается из их описания. Ну, или тогда замените имя start на finishAt, например.

Ну почему же.

function LastIndexMin<T>(Self: array of T; start: integer): integer; extensionmethod; where T: System.IComparable<T>;
begin
  var min := Self[start];
  Result := start;
  for var i:=start-1 downto 0 do
    if Self[i].CompareTo(min)<0 then 
    begin
      Result := i;
      min := Self[i];
    end;
end;

От позиции start до начала. То есть, мы стартуем с конца. По-моему, всё верно

Ну не знаю, Станислав Станиславович, может Вы, конечно, выросли на Ближнем Востоке ;), но для большинства людей естественный и ожидаемый порядок обработки последовательных данных – по нарастающей слева-направо / сверху-вниз, то есть в данном случае от нулевого элемента массива (или любого другого произвольного = start) и до последнего. Проход в обратном порядке – это всего лишь внутренняя особенность реализации конкретной функции, которая обычному пользователю не видна.

Давайте рассуждать так: с точки зрения пользователя есть одна функция LastIndexMax c необязательным параметром start на тот случай, если он захочет начать обработку не с 0-го элемента, а с любого другого. Если он опустит этот параметр (т.е. логически для него начнет с нуля), обработается весь массив. Но если он явно напишет a.LastIndexMax(0) – также начать с нуля, то будет обработан только нулевой элемент и результат всегда будет = 0. Когнитивный диссонанс обеспечен! :slight_smile:

Я указываю только на очевидное, на мой взгляд, противоречие между описанием/заголовком ф-ции (и соотв. ожидаемым результатом) и фактическим результатом. Разумеется, имеет право на существование и текущая реализация, если в ней больше практическго смысла, но в таком случае описание нужно скорректировать, например, так:

/// Возвращает индекс последнего максимального элемента, начиная с позиции 0 вплоть до позиции stopAt
function LastIndexMax<T>(Self: array of T; stopAt: integer): integer; extensionmethod; 

Интересно, что думают на этот счет другие коллеги?

Вот код из .NET:

begin
  var l := Lst(5,5,2,2,5,4,5,6,5,7,5,7);
  Print(l.LastIndexOf(5,3));
end.

Он находит последнюю пятерку начиная с индекса 3.

Та же идея.

Максимум что я могу для Вас сделать - это назвать имя параметра не start, а index

Как я и говорил, такой подход вполне допустим, если он соответствует описанию функции. В данном случае – полностью соответствует. Обратите внимание на разницу:

/// Осуществляет поиск указанного объекта и возвращает отсчитываемый от нуля индекс последнего вхождения в диапазоне элементов списка System.Collections.Generic.List'1, 
начиная с первого элемента и до позиции с заданным индексом.
'/// Возвращаемое значение: Отсчитываемый от нуля индекс последнего вхождения элемента
function LastIndexOf(item: T; index: integer): integer;
/// Возвращает индекс последнего минимального элемента начиная с позиции start
function LastIndexMax<T>(Self: array of T; start: integer): integer; ...

Пускай даже параметр будет называться нейтрально – index, лишь бы не start в данном случае, хотя stopAt здесь был бы интуитивно понятнее, ИМХО.

З.Ы. Кстати, вторая строчка из Comment-Doc (Возвращаемое значение:) у меня не отображается в подсказке Intellisense, так и должно быть?

1 лайк

Еще одна опечатка в PABCSystem.pas, строка 9055:

/// Возвращает максинимальный элемент function Max<T>...