Ошибки компилятора PascalABC.Net

“Невозможно вывести типы-параметры generic-подпрограммы ReadElements (укажите типы-параметры явно)” Или может быть я неправильно пишу эту функцию?

begin
  var f1: file of integer;
  Rewrite(f1, 'a.dat');
  Write(f1, 1, 2, 3, 4, 5);
  f1.Close;
  ReadElements('a.dat').Println;//ошибка
end.

Ну, вам правильно пишет, надо указывать параметры шаблона явно:

function f1<T>: T := default(T);

begin
  var a := f1<byte>;
end.

Тут уже выдаёт другую ошибку. Проблема в том что компилятор думает что вы сравниваете указатель на функцию f1 и типа byte оператором <. А затем сравниваете результат с ничем. Поэтому пишет что ожидалось выражение вместо ; то есть то с чем можно сравнить.

В этом случае нужно экранировать первый <, то есть поставить перед ним & (этот знак под семёркой в англ раскладке). Тогда компилятор не сможет считать < оператором сравнения, и всё скомпилируется правильно.

1 лайк

Да, так работает ReadElements&<integer>('a.dat').Println;

begin
  loop 10 do
  begin
    var i: integer;
    writeln(i);
    i += 1;
  end;
end.

i обнуляется только в первой итерации цикла. @Admin, это ошибка?

В1 лекции повышение квалификации преподавателей об этом что-то есть https://youtu.be/U65-SebSaj4?t=5430

Там переменной присваивается сразу новое значение, поэтому обнуление и не нужно.

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

Это внутренности реализации компилятора. Знание их не должно быть обязательным для программирования.

Рассмотрим попытку написать следующий код:

begin
  var i:=1;
  i.Println;
  begin
    var i:=2; // компилятор выдает ошибку
    i.Print;
    Inc(i);
    i.Println
  end;
  i.Println
end.

Ошибка следующая: Внутриблочные переменные не могут иметь те же имена, что и переменные из блока верхнего уровня

А почему, собственно? Создавая Паскаль, Н.Вирт оглядывался на языки Algol-60/68, в разработке которых он принимал участие. В языке Algol любой блок begin ... end наделялся правом управлять памятью описанных в нем объектов программы. Описали переменную - можете пользоваться. Из блока вышли - все, переменная исчезла. Не то, чтобы ее скушал какой-то сборщик мусора - она отдавалась на нужды программы и могла быть отдана под какой-то иной объект в следующем блоке. Другими словами, локализация переменных в блоке была действительно локализацией. Коллизии имен не возникало, потому что вне блока его переменные не были видны, а внутри блока локальное объявление имело приоритет. И вот это программистское счастье Н.Вирт порушил “одной левой”, потребовав все объекты программы описывать в специальном разделе. В Паскале Н.Вирта вся требуемая память выделялась компилятором разово и статически. Зато эффективно. но… неудобно. В Turbo Pascal попытались поизвращаться и создали кошмарный механизм работы с управляемой памятью. В Delphi это дело подправили и появились нормальные динамические массивы.

Я не знаю, чем руководствовались разработчики PascalАВС.NET реализовав возможность описывать переменные посредством var в любом месте программы, и не обеспечив локализацию описанных переменных в блоке. Может быть, эффективностью программы? В самом деле, если использовать механизм динамической памяти, строгая локализация потребует при каждом входе в блок выделять память, а при выходе - освобождать. Представим себе переменную, описанную внутри составного оператора во вложенном цикле… Конечно, это все так, фантазия. Разработчики, если сочтут нужным, расскажут как оно на самом деле было. Но факт остается фактом: в PascalАВС.NET память в программной единице выделяется один раз, невзирая на местонахождение var. И, если переменная не инициализирована, компилятор не делает никаких “телодвижений”. Сама среда .NET при выделении памяти всегда производит инициализацию. С этой позиции, я думаю, понятно поведение кода, который привел @Bronislav

Нет ну “почему?” - понятно. В IL коде нету внутри-блочных переменных, они всегда описываются в начале подпрограммы как в старых паскалях. Другое дело - надо всё же чётко решить:

@Admin это нормально или всё же должно быть исправлено? Возможно надо больше настаивать на том что переменные надо всегда инициализировать, потому что так:

begin
  loop 10 do
  begin
    var i: integer := 0;
    writeln(i);
    i += 1;
  end;
end.

Всё уже работает как ожидалось.

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

В C# аналогично:

static void Main(string[] args)
        {
            int i;
            {
                int i;
            }
        }

Локальная переменная или параметр с именем “i” нельзя объявить в данной области, так как это имя используется во включающей локальной области для определения локальной переменной или параметра

Изначальный вопрос было не про то, а про вот это:

begin
  loop 10 do
  begin
    var i: integer;
    writeln(i);
    i += 1;
  end;
end.

А в чём тут проблема?

Локальные переменные автоматически не инициализируются - программист должен это понимать

Да, это ошибка программиста

NET класс Random() конфликтует с Паскалевской функцией. Есть способ обойти или только вплавь?

System.Random
PABCSystem.Random

Спасибо.

PABCSystem.pas(8490) : Ошибка времени выполнения: Заданное приведение является недопустимым.

begin
  var a := Seq(1, 3, 5); a.Println;
  var b := a.Cast&<real>;
  b.Println;
end.

Program2.pas(3) : Ошибка времени выполнения: Заданное приведение является недопустимым.

begin
  var a := Seq(1, 3, 5);
  var b := a.Cast&<real>().ToArray;
  b.Println;
end.

Не путайте operator explicit и as. .Cast выполняет именно as.

Разница в том - что as работает для классов-наследников друг-друга.

А operator explicit это обычная функция, посыпанная тонким слоем сахарка (то что её можно вызывать через real(i)). Она может быть перегружена (вроде даже экстенш-методом), но поэтому же - компилятор должно явно указать в IL коде какая перегрузка вызывается. Поэтому шаблонную функцию используя его не напишешь.

P.S. Да, вот через extensionmethod:

function operator explicit(self: System.Type): integer; extensionmethod;
begin
  Result := self.GetHashCode;
end;

begin
  var t := typeof(object);
  integer(t).Print;
end.

Я не разбираюсь в классах, пока еще, просто взял пример из справки по Cast, там один в один, добавил вывод и оно выдало ошибку. То есть вывести через Print или Write эти элементы последовательности(?) нельзя. А как их вывести тогда, ну и написали бы в справке вывод, чтоб понятно было. Мне вот не понятно.