Изменение семантики записи [1,2,2,3] - предложение

С высокой вероятностью в следующих версиях будет кардинально изменён смысл конструкций вида

[1,2,2,3]
[2..5, 8..12, 4..6]

Предложение заключается в следующем.

  1. [1,2,2,3] будет иметь тип не set of integer как сейчас, а array of integer. Это позволит писать
var a := [1,2,3]*2;
var aa := [[1,2,3],[4,5,6],[7,8,9]]

создавая одномерные массивы и массивы массивов простым синтаксисом

  1. В коде
var s: set of integer := [1,2,2,3];

запись справа будет трактоваться по-старому - как инициализатор множества

Коды

s := [1,2,2,3];
p([1,2,2,3]);

также будут трактоваться контекстно в зависимости от типа s и типа формального параметра. В сложных случаях нескольких перегрузок процедуры p предпочтение будет отдаваться массивам.

  1. При стандартном использовании
if i in [1,2,3]

никто не заметит изменения типа

  1. Перестанут работать следующие конструкции:
[1,2,3] + [2,3,4]
[1,2,3] - [2,3,4]
[1,2,3] * [2,3,4]

Над массивами такое будет делать нельзя. Точнее, для массивов определена сумма - и это - конкатенация. Это - самое большое зло этого изменения. Несовместимость с Delphi повысится

  1. Конструкции с диапазонами
[2..1000]

будут принадлежать к особому типу, не хранящему все значения. В результате запись

x in [2..1000]

станет работать существенно эффективно

  1. Конструкции с диапазонами
[2..1000,1,3,5,200..205]

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

Предлагаю обсудить

4 лайка

Первое впечатление сугубо положительное. По поводу “Несовместимость с Delphi повысится” - у меня давно сформировалось мнение, что кроме разработчиков эта пресловутая совместимость с Delphi никого не волнует.

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

На этом фоне конструкции вида

[1,2,3] + [2,3,4]
[1,2,3] - [2,3,4]
[1,2,3] * [2,3,4]

вполне бы могли работать в части их принадлежности массивам, когда из [1,2,3] + [2,3,4] формируется [1+2, 2+3, 3+4], а не [1, 2, 3, 2, 3, 4], либо [1, 2, 3, 4]. То, что в последовательности выглядит как слияние, могло бы для массива быть арифметической операцией над каждой парой элементов с одинаковым индексом.

Лично меня временами все же несколько печалит, что описав массивы a и b, я не могу указать что-то вроде b := 0; или b := 2.5 * a + Cos(Pi * a), несмотря на то, что тут семантически понятен скрытый цикл по массиву с присваиванием b[i] := 2.5 * a[i] + Cos(Pi * a[i]). Да это умел делать Бейсик, прошитый в 64-Кбайтных ПЗУ первых персоналках! Для ЭВМ второго и третьего поколения существовал Бейсик, в котором была группа операторов MAT, реализовывавших работу с матрицами! Ну да ладно, все это так… бурчание. Я давно понял, что разработчики внешне видят массивы как разновидность последовательностей, хранящихся в памяти и использующая индексы для доступа к элементу, посему арифметические операции над ними не проводятся, ибо a +b - это не суммирование, а слияние.

Думаю, если и менять смысл данных конструкций, то делать это мягко, выдавая сначала предупреждение, чтобы для пользователей не было неожиданностью. Но, сама идея с той точки зрения, что укорачивает в некоторых (в каких - нет?) случаях код - мне нравится. С другой стороны, не всё так радужно, как может показаться на первый взгляд, совместмость нарушится не только с Delphi, но и с Free Pascal, что в некоторой (в какой?) степени повлечет отторжение учителей от данного языка. Школьная программа не будет меняться сразу (возможно, ещё долго) из-за данной модификации PascalABC.NET, а значит, часть примеров программ перестанет работать из учебников. С другой стороны, книга по современному программированию от @RAlex имеется, и, думаю, в ней будет описано данное изменение, но, к сожалению, на эту книгу пока что учителя не ссылаются слишком часто и проблему “осовременивания” кодов программ придётся как-то решать.

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

Наш анализ показывает, что в школах конструкции [1,2,3] используются исключительно в контексте

x in [1,2,3]

а он будет продолжать работать. Я также не знаю учебников, в которых используется [1,2,3]*[4,5,6] - а это единственное, что перестанет работать. Кроме того, в школах уже мало кто работает во Free Pascal и Delphi.

Free Pascal - не надо забывать - это язык предельной совместимости с Delphi - свои расширения у него если и есть, то где то на задворках. То есть, Free Pascal = Delphi

1 лайк

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

1 лайк

А может лучше всегда создавать диапазон, когда присвоение / передача параметром идёт не к 1 из типов: array of T, HashSet<T> и set of T? Для создания лично массивов уже есть функция Arr.

Ну, вообще никто не мешает перегрузить операторы - и * для массивов, возвращающие HashSet<>.


Про старые множества: Раз вы всё же создаёте нормальные диапазоны - может заменять set of T на HashSet<T> при компиляции?

На сколько я знаю - кеширование множества в if x in [1,2,3] было последней причиной давать им особый тип.

Думаю, что будут при этом проблемы обратной совместимости (насколько серьёзные или нет - судить не буду). Следует проверить схожесть интерфейсов set of T и HashSet<T>, чтобы определить что именно придётся менять в старых кодах программ, модулей и библиотек при переходе на новую версию компилятора. Но, разумеется, в какой-то мере эту проблему могут “закрыть” специально написанные методы расширения HashSet<T>, “дополняющие” в каком-то смысле интерфейс HashSet<T> до set of T, чтобы меньше кода пришлось менять пользователям. Но, при таком подходе “замусорится” Intellisence данными методами-расширения, добавленными ради обратной совместимости. Кроме того, ими не закрыть, например, отсутствие имеющихся полей и свойств (если они есть), являющихся частью интерфейса, set of T в HashSet<T>.

У set of T нету нормального интерфейса, он весь в процедурном стиле работает. То есть вместо методов, свойств и т.п. - глобальные подпрограммы. Если компилятор будет конвертировать set of T в HashSet<T> - даже в PABCSystem ничего менять не придётся.

В таком случае - нет вопросов.

Речь не идёт об этом. Запись [1,2,3] для массивов гораздо более привлекательна, поскольку массивы используются в десятки раз чаще. К тому же и выводятся они в квадратных скобках, а множества - в фигурных.

Обращаю внимание, что переменную s: set of T не проблема куда-то конвертировать - речь идёт о трактовке литеральной константы. И - константа [1…1000] перестанет всякий раз разворачиваться в 1000 элементов.

Перечитайте первый пост.

1 лайк

PascalABC.NET приближается к Python - там тоже списки так, как здесь планируется массивы, можно инициализировать.

Не приближается. Вы сами говорите, что это - списки. А списки - не массивы - у них совсем другая семантика и представление в памяти

Внешне, по синтаксису. О семантике я речи не вёл. Это как, C# и C++ внешне похожи, а если внимательней посмотреть, то разница немалая.

Внешне да. Но - обращаю внимание, что синтаксис в Паскале такой есть. Я веду речь об изменении его семантики.

begin
  var a := new integer[] (1, 2, 3);
  var b := [1, 2, 3];
end.

Будет ли особо большая разница в скорости при такой инициализации? И как тогда определится тип b? Сейчас это компилируется и b имеет тип set of integer.

Красивее - да. Но как я и сказал - для этого уже есть Arr. И он ещё и лучше, потому что можно явно указать тип элементов.

Я внимательно прочитал, все посты от всех.

Дело не в этом. Если делать через диапазонный тип - запись [1,2,3] легко в конструкторе диапазона превратить в то же самое что [1..3], без особых ударов по производительности.

И тогда if x in [...], к которому все больше привыкли, будет работать не менее эффективно чем в старой реализации. С единственной поправкой - теперь диапазон надо кешировать ручками. Хотя кеширование тоже не сложно реализовать, тупо скопипастить код от set of T.


Ну и собственно насчёт реализации диапазонов - предлагаю сортированное дерево единичных диапазонов.

То есть разворачивание [1,3..5,7] в:

     o
    / \
(1..1) o
      / \
 (3..5) (7..7)

При этом если добавить в диапазон значение [2] - элементы [1] и [3..5] склеятся и получится [1..5].

Сейчас [1,3…5,7] тупо разворачивается во все элементы в виде хешсета

Тип b теперь будет array of integer. Разницы в скорости не будет. Или это будет заменено на вызов Arr

1 лайк

Сейчас оно разворачивается в set of integer, который уже содержит все элементы в виде HashSet + свои данные, вроде верхней и нижней границ.

Нет, Arr вызывать точно не надо. new T[]( ... ) хотя бы быстрее раза в 2, потому что не создаёт лишнюю копию массива.

Зачем, кстати, вообще она создаётся?