Ссылки? В Pascal? А почему бы и нет!?

Здравствуйте форумчане и разработчики языка/среды PascalBAC.NET. Хочу затронуть тему о ссылках, но не о сталинских или интернет, а о ссылочных переменных (или далее по тексту просто ссылках). На первый взгляд кажется, что в языке Pascal как будто бы отсутствуют ссылки, но это ощущение кажущееся. Да, прямое объявление ссылок отсутствует, но они в том или ином виде присутствуют. В виде ссылок в Pascal представлены: динамические массивы (array of T), объекты классов (для объектных версий Pascal, таких как: Object Pascal в Delphi и PascalABC.NET), передаваемые аргументы в функцию по ссылке (var-параметры), а ещё, как это может показаться странным на первый взгляд, разыменованные указатели, т.е. указатели с операцией разыменования: <указатель>^. Таким образом, из всего многообразия представления ссылок в Pascal, может показаться, что в особой необходимости специальных ссылочных переменных нет, но с другой стороны, применение ссылок (специальных ссылочных переменных) в некоторых случаях может сделать код более читабельным (без лишних крышечек). Приведу конкретный пример. Допустим мы решили создать в PascalABC.NET собственный класс Vector, подобный классу Vector в стандартной библиотеке шаблонных типов (STL) в C++, а именно вектор с последовательным размещением элементов в памяти (как в массиве), но с возможностью быстрого добавления элементов, за счет аллокации памяти с некоторым запасом (это к вопросу: «чем может не угодить встроенный массив?»). Так как открытые массивы в PascalABC.NET отсутствуют, то память для последовательного размещения элементов нашего вектора Vector, будет являться массивом встроенного типа (array of T). При обращении по индексу к элементу массива встроенного типа (т.е. array of T) возвращается ссылку, т.е. для массива Arr: array of T; Arr[i] – фактически является ссылкой на i-элемент массива. Но при реализации индексного свойства (default index property) для нашего класса Vector в PascalABC.NET, аналогично тому как оно работает для встроенного массива, возникают сложности. Любое свойство класса реализуется через методы установки (setter) и получения (getter), но виду отсутствия типа ссылки (в языке Pascal) невозможно помощью индексного свойства вернуть ссылку на конкретный элемент вектора (соответствующий элементу в массиве т.е. памяти вектора). Если в реализации класса Vector setter-функция будет возвращать значение, а getter-устанавливать, то в этом случае (снаружи) мы не сможем сослаться на элементы вектора. К примеру, это может грозит тем, что такая функция как swap (обмен) работающая для элементов массива array of T, будет отказываться нормально работать (как хотелось бы) в отношении элементов вектора. Как решить подобную проблему? Можно сделать так, чтобы при доступе, с помощью индексного свойства, возвращался указатель на элемент массива (в этом случае для индексного свойства потребуется только setter-метод), но в этом случае для получения ссылки на элемент нужно применить разыменование и тогда вызов функции swap для обмена i и j-значений, будет выглядеть как swap(Vec[i]^, Vec[j]^). Применение крышечки обозначает разыменования указателя, т.е. получение ссылки на конкретный элемент в памяти (представленного массивом) вектора. Таким образом можно решить указанную проблему, но согласитесь, что доступ к элементам вектора через индексное свойство и использование дополнительной крышечки выглядит при этом не очень красиво и заманчиво. Если у разработчиков PascalABC.NET (языка и среды) вдруг возникнет желание ввести тип ссылочной переменной (тип ссылка), то я предлагаю определить его в виде: @<тип> (где <тип> – тип переменной на который ссылается ссылка). Соответственно объявление ссылочной переменой будет выглядеть так: var <ссылочная переменная>: @<тип> или более конкретно (на примере): var a: @integer (a – ссылочная переменная) Альтернативная запись передачи аргументов по ссылке в процедуру/функцию: var <аргумент>: @<тип>, что равносильно обычно записи: var <аргумент>: <тип> , или на конкретном примере: procedure Proc (arg: integer; res: @integer), равносильно: procedure Proc(arg: integer; var res: integer) При применении @ (операции взятия адреса) к ссылке должен возвращаться адрес объекта, на который ссылается ссылка, но не самой ссылки. Назначение ссылке адресата должно производиться через операцию присвоения (:=). Если ссылке назначается ссылка, то фактически ей должен назначаться адресат этой ссылки. Должна быть возможность переназначения адресата (в отличие от C++ где значение ссылки инициализируется раз и навсегда). При объявление ссылочной переменной (ссылки) должна быть возможность проинициализировать её (с помощью оператора присваивания). Если ссылка неинициализирована, то её значение должно быть = nil (в данном случае nil – не нулевой указатель, а обозначение того что ссылка не назначена). Должна быть возможно сделать ссылку назначенной путем присвоения ей nil.

Ах, зачем я не лужайка!
Почему я не лужок!
   (Юлий Ким)

Ах, зачем Паскаль не С++ ? :stuck_out_tongue_winking_eye:

Vector в .Net называется List, и в отличии от C++ - это что то необычное, для .Net языка иметь свое определение массива и строки, в каждой программе)))

Я не до конца уверен, но в .Net доступ к элементам массива по индексу вроде реализован через метод записи и метод чтения, которые всегда инлайнятся. То есть по сути как любое другое индексное свойство.

Возможно, указатели - это всё ещё те же самые указатели из C++ и ссылки там тоже через указатели реализованы, на сколько я знаю (в отличии от ссылок на классы, они на много сложнее устроены). Работать может не так удобно, но всё зависит от того что вам надо. Если свойство описано по простому - его инлайнит, то есть i := my_vector[5] на прямую присвоит i значение внутреннего массива, по соответствующему индексу, вместо того чтоб вызывать функцию поставленную на read (get).

А не на оборот?

Swap тут дело не в том, в Swap элементы передаются как var-параметры, поэтому свойства с ней не работают. А массив работает не из за ссылок, а из за того что он встроенный, и компилятор точно может сказать что передавать то что получаем из его индексного свойства как var-параметр безопасно. Но на замену Swap есть другие способы.

Ну, вы можете сделать ещё 1 свойство, не дефолтное но тоже индексное, и оно уже будет возвращать указатель а не элемент. Но это только в случае, если вам так уж приспичило использовать Swap. Потому что для разные случаев - есть разные замены для него.


По моему, то что вы предлагаете - это лишь по другому выглядящий указатель. Единственное преимущество - не надо писать лишний знак ^. Вот только в C++ их ввели потому что там указатели нужны часто. И по той же причине существует operator->. В .Net такие случаи специфичны и редки, раз всё то же самое работает и через указатели - в ссылках мало смысла.


P.S. Попробуйте использовать какую то разметку. Я, к примеру, ставлю пустые строчки чтоб отделить абзацы + выделяю код с помощью знака `, вот так: `код`. Выглядит как: код. А то стенку текста очень сложно читать, я несколько раз сбивался, после написание ответа к определённому фрагменту, приходилось в этой стенки выискивать где закончил. А так - есть ещё много других видов разметки. Если интересно - прочитайте про markdown (он тут реализован) + потыкайте на разные кнопки над окном ответа, в котором вы пишете.

Во всех языках есть что то хорошее. И хотя в данном случае вы правы, но ваша мысль никак не обоснована. Конечно, ваши цитаты интересно читать, но почему бы не вкладывать более точную мысль в своё сообщение чем “все кто тянут что то из C++ - еретики”.

1 лайк

В целом я с вами согласен. Пример пользовательского класса Vector<T> и функции swap<T> приведен для примера, чтобы показать, что невозможно сделать пользовательский класс в PascalABC.NET с доступом по индексному свойству аналогично как это работает для класса array of T. Несмотря на то, что класс array of T, встроенный и поддерживается компилятором, я считаю уместным иметь возможность его повторения (с пользовательским расширением его возможностей). Также я считаю, что ссылки — это не прихоть и не желание притянуть Pascal к C++, как понял RAlex. Вообще, мне язык Pascal кажется весьма удобочитаемым, а в приложении его к платформе .NET, так вообще сказка. В своей рабочей деятельности, я его планирую использовать для прототипирования при разработке алгоритмов ЦОС (беспроводная связь).

Ну так вы и можете его сами сделать. Я же говорю, сделайте второе индексное свойство, которое не будет дефолтным и будет возвращать указатель. Тогда его можно использовать в Swap.

Я вот не понимаю. Я вот пишу что-то вроде MyFunk(a:Integer; var CalcV:Intger):Integer; И в любом учебнике написано, что я передаю CalcV по ссылке. И это те же самые яйца, как, если бы я передавал @CalcV и работал через CalcV^, т. е. работал с указателем.

И вообще никогда не запаривался, ссылка это или указатель. Работаю в Delphi с классами, знаю, что они передаются всегда по ссылке, больше мне ничего знать не нужно.

Но всегда приятно делать несто то же самое, но другое.

При применении @ (операции взятия адреса) к ссылке должен возвращаться адрес объекта, на который ссылается ссылка, но не самой ссылки.

Ну скажем так, Delphi, ХЗ с какой версии, если пропустить ^, вполне понимает, что надо раз переменная объявлена, указатель, то и трактовать ее надо так, даже если кодер ошибся.

Любое свойство класса реализуется через методы установки (setter) и получения (getter), но виду отсутствия типа ссылки (в языке Pascal) невозможно помощью индексного свойства вернуть ссылку на конкретный элемент вектора (соответствующий элементу в массиве т.е. памяти вектора

Чё??

Ну уж только не еретики. “Ересь” - сугубо церковный термин, если Вы посмотрите в толковом словаре его значение. Поэтому не нужно употреблять терминов, значение которых категорически не вяжется с контекстом. Хотя… если для Вас PascalABC.NET стал религией, можете продолжать))))

Сударь, вы не в пивнушке среди своих друзей, выбирайте выражения, пожалуйста!

Зачем повторять системную коллекцию? Лучше Вы все равно не сделаете. Просто наследуйте от нее и пишите свои расширения.

Я пытался выбрать, но получилось выдавить из себя только это.

Хотя. Если ввиду отсутствия ссылок невозможно эту самую ссылку вернуть, то всё логично. :slight_smile:

Я в курсе, но это не значит что я говорил про веру. У меня было ещё несколько вариантов как написать, я выбрал самый короткий. Можно и разжевать, так чтоб придраться - не было вариантом: Вы говорите так, как будто самая идея что то тянуть чуть то из C-языков - не может быть хорошей. А это не так, случаи разные бывают.

Идея @sanek84z не высосана из пальца. В целом я не считаю её правильной, но именно моменты к которым вы придрались - были логичны.

Это, мягко говоря, не совсем так. На мой взгляд, нехорошая идея - тянуть из С++ указатели, от которых, слава богу, Pascal практически сумел избавиться. Указатели, которые по большей части нелигитимны в .NET-среде настолько, что даже сама Microsoft назвала их в C# небезопасными, т.е. обладающими возможностями наделать программисту проблем. Их можно реализовать, как правильно тут заметили, из академического интереса, но выдавать за жизненно необходимое - я с этим не согласен. Я уже писал про это “пацанское желание порулить”, из которого проистекают желания ссылок и оптимизаций" и не хочу повторяться. Ну не повезло вашему поколению, не успели порулить тумблерами и переключателями с пульта большой ЭВМ, стоящей столько, что за эти деньги можно дом построить… ну что делать? Да только одно - пытаться самому рулить программным кодом. Но это пройдет, поверьте.

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

1 лайк

Прочитал ваши комментарии. Со всеми вами в целом согласен и уважительно отношусь в организации языка Pascal и его подходам. Вообще, я хотел обратить прежде всего ваше внимание на некоторое дополнительное визуальное удобство (если бы вместо указателей использовались ссылки), даже не смотря на то что у указателей в языке Pascal и так ограниченное применение. Вообще, применение указателей в Pascal мне казалось не всегда уместным, хотя в том же C, C++ их наличие естественно, хотя и приводящее порой (или даже часто, обычно по невнимательности) к ошибкам. Я считаю, что ссылки для Pascal были бы более уместными (чем существующие указатели), так как ссылочные переменные могли бы в большинстве случаев пользователем (программистом) рассматриваться как псевдоним (alias) какого-либо объекта (обычной переменной, переменной типа записи, объекта класса). Но это на мой взгляд.

1 лайк

Ну, смотрите, вы можете сделать так:

type
  r1=record end;

begin
  var a := new r1;
  var o:object := a;
end.

Тут a - это сама запись. А o - это .Net ссылка на неё. Прошу ещё обратить внимание на то, что C++ ссылки это только украшение для указателей. А в .Net - они устроены сложнее. К примеру, когда они ссылаются на что то не находящееся на стеке - они могут менять свой адрес, в процессе работы сборщика мусора. Логично предположить что они будут медленнее указателей.

type
  r =record f: Integer; end;
  
begin
  var a := new r;
  a.f := 10;
  var o: object := a;
  a.f := 20;
  
  println(o);
  println(a);
  
end.

Результат работы:

(10) 
(20)

Вот как… Ну, это всё равно зовётся ссылкой))
Я так никогда не делал, но теперь буду знать что и не надо)))

А такой вариант работает:

type
  r =class f: Integer; end;
  
begin
  var a := new r;
  a.f := 10;
  var o: object := a;
  a.f := 20;
  
  println((o as r).f);
  println(a.f);
  
end.

результат работы: 20 20

Ну да, в первом случае происходит упаковка value-типа в объект, и, похоже, value-тип, на всякий случай, копирует. А во втором - вы лишь меняете тип указателя (то есть ссылки а не указателя, именно .Net ссылки).

Судя по описанию абстрактного класса System.Array, а именно методов GetValue и SetValue, я подозревал что ссылки в .NET реализованы как System.Object. Но меня терзает один вопрос: такая ссылка (как System.Object) не обязательно ли будет размещается динамически в памяти, или JIT-компилятор достаточно умен в этом отношении и не всегда размещает такую ссылку в динамической памяти, тогда, когда её можно разместить в стеке программы? Если это так, то я спокоен.

Ну, это нарушает принцип ООП. Свойство может задаваться только через сеттеры и никак иначе. Реализовывайте свой метод Swap, который бы менял местами значения в массивах.