Версия PascalABC.NET 3.1

Вышла версия PascalABC.NET 3.1.

Что нового:

Язык программирования

  • Добавлен стандартный тип Complex и функция Cplx(re,im)
begin
  var c := Cplx(2,3);
  var c1 := c * Cplx(0,-1);
  c := c1 / c + c1;
  Println(c.Magnitude, c.Phase);
end.

  • Реализованы кортежи в виде (a,b,c+1)
  • Реализована операция индексации для кортежей
  • Реализованы типы кортежей в виде var t :(integer,string);
  • Реализовано кортежное присваивание (a,b,c) := (c,a,b)
begin
  var t: (string,integer);
  t := ('Иванов',16);
  Println(t[1],t[0]);
  var name: string;
  var age: integer;
  (name,age) := t;
  
  var a := 3;
  var b := 5;
  (a,b) := (b,a);  // Swap(a,b)
  Println(a,b);
  
  a := 1;
  b := 1;
  for var i:=3 to 20 do
  begin
    (a,b) := (b,a+b);
    Print(b); // Фибоначчи
  end;
end.
  • Реализованы множественные секции uses
  • Реализован новый синтаксис extension-методов (новое ключевое слово extensionmethod). Параметр расширяемого типа должен иметь имя Self
/// Применяет действие к каждому элементу последовательности
procedure &ForEach<T>(self: sequence of T; action: T -> ()); extensionmethod;
begin
  foreach x: T in Self do
    action(x);
end;

  • Реализован foreach по многомерным массивам

Стандартные модули

  • Реализованы операции ToHashSet и ToSortedSet для последовательностей
  • Стандартные Chr и Ord теперь работают с кодировкой Unicode. Для кодировки Windows реализованы ChrAnsi и OrdAnsi
  • В стандартные методы Reset, Rewrite, Append, OpenRead, OpenWrite, OpenAppend добавлен параметр - кодировка. Кодировка по умолчанию - 1251
begin
  var f : Text;
  Rewrite(f,'a.txt',Encoding.UTF8);
  f.Writeln('PascalABC.NET 3.1');
  f.Close
end.
  • Реализована операция in для динамических массивов, множеств HashSet и SortedSet, словарей и списков
begin
  var s := HSet(1,3,5);
  Println(5 in s);
  var a := Arr(1,3,5);
  Println(5 in a);
  
  var d := Dict(KV('бегемот',2),KV('крокодил',3));
  if 'крокодил' in d then
    d['крокодил'] -= 1;
  Println(d);
end.

  • Реализованы операции + * - < > <= >= = <> для HashSet
  • Реализованы операции += для множеств и списков
begin
  var a := HSet(1,3,5);
  a += 5;
  Print(a);
  var l := Lst(1,3,5);
  l += 5;
  Print(l);
end.
  • Введены новые стандартные короткие функции HSet, SSet, Lst
  • Добавлен новый стандартный учебный модуль ClientServer
  • Добавлены стандартные функции RoundBigInteger, TruncBigInteger, метод расширения string.ToBigInteger
  • Добавлены методы расширения char.ToDigit, char.Pred, char.Succ, char.IsDigit, char.IsLetter, char.Code, char.IsLower, char.IsUpper, char.ToLower, char.ToUpper
  • Изменен порядок параметров в стандартных функциях SeqGen, SeqFill, ArrGen и ArrFill.

Среда IDE

  • Уменьшено потребление памяти в Intellisense.
  • Реализована отладка в .inc-файлах (первый внешний pull-request от Slav76).

Другое

  • Сборка проекта и выполнение тестов в Linux.

По комплексным. Попробовал делать расчеты электрических цепей, отлично все, очень удобно. Единственное, было бы весьма приятно (хотя совсем не обязательно) увидеть достаточно часто встречающийся в расчетах модуль комплексного числа, доступный и как x.Magnitude, и как длинное и неочевидное для начинающих Complex.abs(x), в более привычном виде abs(x).

Конечно, пришлось писать процедуру для вывода комплексных чисел, потому что вывод вида (13.994972221248, -0.00590055678917983) гораздо удобнее и нагляднее, когда значения выглядят примерно так: 13.995-i0.006. И тут ностальгически вспомнился форматный вывод Фортрана или ПЛ/1…

Print() удобен тем, что он разделяет пробелом выводимые значения, но это хорошо лишь для целых величин и, возможно, символов. А для вещественных это зло… Вот если бы имелся какой-то PrintReal, выводящий вещественные (с автоприведением к ним целочисленных) значения, у которого первый параметр задавал бы число знаков в дробной части (в целой не надо, потому что Print’ом таблицы все равно не рисуют), это было бы превосходно.

Пусть имеется такая программа:

// PascalABC.NET 3.1, сборка 1167 от 12.02.2016
begin
  var s:=Seq(6,2,8,1,4,9,3,7,5);
  var hs:=HSet(s);
  ss.Println
end.

Тут все понятно, просто и работает. А как просто и понятно сформировать набор hs, взяв каждый второй элемент из s? Или, взяв из s элементы стоящие по порядку седьмым, третьим, вторым и пятым? Ведь для типа IEnumerable индексного свойства не предусмотрено… Преобразовать s в массив и написать примерно так:

// PascalABC.NET 3.1, сборка 1167 от 12.02.2016
begin
  var s:=Seq(6,2,8,1,4,9,3,7,5);
  var a:=s.ToArray;
  var hs:=HSet(a[8],a[4],a[3],a[6]);
  hs.Println;
end.

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

// PascalABC.NET 3.1, сборка 1167 от 12.02.2016
begin
  var s:=SeqGen(20,1,t->t+1); // 1,2,3, ... 20
  var a:=s.ToArray;
  var hs:=HSet(a[1]);
  var i:=3;
  while i<20 do begin
    hs:=HSet(hs.Concat(HSet(a[i]))); // несуразно как-то
    Inc(i,2);
    end;
  hs.Println
end.

Ну, пока у нас нет срезов, - надо использовать фильтрацию с индексом.

begin
  var s := Seq(6,2,8,1,4,9,3,7,5);
  var hs := HSet(s.Where((x,i)->i mod 2 = 0));
  hs.Println
end.

Спасибо за красивое решение! Использовать лямбду с двумя параметрами, а работать только с одним в голову не пришло. Только для “каждый второй” надо условие на <> поменять или просто взять Odd(i). Тут же счет с нуля идет, а не с единицы.

Попутно вопрос возник. Или я чего-то не понимаю - и тогда все эти SeqGen - обезьяна с гранатой, или я все понимаю правильно, и это ошибка.

begin
  var s:=SeqGen(7,1,t->t+1);
  s.Select((x,i)->Rec(x,i)).Println;
  s.Select((x,i)->Rec(x,i)).Println;
end.

Выдача: 
(1,0) (2,1) (3,2) (4,3) (5,4) (6,5) (7,6)
(8,0) (9,1) (10,2) (11,3) (12,4) (13,5) (14,6)

А теперь так:

begin
  var s:=Seq(1,2,3,4,5,6,7);
  s.Select((x,i)->Rec(x,i)).Println;
  s.Select((x,i)->Rec(x,i)).Println;
end.

И выдача: 
(1,0) (2,1) (3,2) (4,3) (5,4) (6,5) (7,6)
(1,0) (2,1) (3,2) (4,3) (5,4) (6,5) (7,6)

Я понимаю. что лямбда t->t+1 “толкает” к наращиванию генерируемых значений на единицу (она с этой целью и писалась), понимаю, что механизм вычисления членов последовательности “ленивый” (и это отлично), но почему при повторном обращении к последовательности игнорируется установка начального значения в единицу? Когда SeqRandom - там вопросов нет, но тут-то?

Ну, это ошибка в реализации. Неожиданная - на ровном месте. Ищем.

11 posts were split to a new topic: О статических и динамических массивах

Ошибку с последовательностями исправили

Ну, это ошибка в реализации. Неожиданная - на ровном месте. Ищем.

Еще один “сюрпризец”. Неожиданный, раньше работало нормально.

// PascalABC.NET 3.1, сборка 1171 от 15.02.2016
type
  tM=array[1..3,1..3] of integer;

begin
  var a:array[1..3,1..3] of integer:=
    ((1,2,3),(4,5,6),(7,8,9));
  Writeln('a:  ',a);
  var b:tM:=((1,2,3),(4,5,6),(7,8,9));
  Writeln('b:  ',b);
end.

Результат выполнения программы:

a:  [[1,2,3],[4,5,6],[7,8,9]]
b:  ([[1,2,3],[4,5,6],[7,8,9]],1,3)

В сборке 1172 ошибка устранена.

Известно, что для нахождения суммы членов последовательности можно использовать метод .Sum, для нахождения среднего арифметического - метод .Average. А вот с произведением все оказывается далеко не так красиво. То ли его решили изначально в LINQ не вводить из-за возможности неконтролируемого арифметического переполнения в операции умножения целых (обычно в целях эффективности контроль переполнения разрядной сетки не делается), то ли еще какая-то есть причина, - но так, или иначе, метода .Prod у нас нет. И приходится выкручиваться. Поскольку разработчикам лучше, чем кому бы то ни было, известна внутренняя “кухня” реализации, хотелось бы получить рекомендацию, как эффективнее (и красивее) находить произведение членов числовой последовательности. И необязательно даже по критерию отбора: ведь его при необходимости можно организовать через предшествующий .Where.

Product это, как и Sum, как и многое другое, это экземпляр свёртки (fold, в Линке это называется Aggregate). Примерно так.

a.Aggregate(1, (prod, x) -> prod * x);

(Надеюсь, с синтаксисом нигде не напутал, WDE не работает, чтобы проверить.)

Есть известная байка на схожую тему в мире функционального программирования.

3 лайка

fac n = product [1…n] - вот это как раз то, чего бы хотелось увидеть))) А байку оценил, “внушаить” (с)

И - да, спасибо, работает выражение.

Гхм, такого количества способов я не видел. Спасибо! :smile: Интересный материал.

Print() удобен тем, что он разделяет пробелом выводимые значения, но это хорошо лишь для целых величин и, возможно, символов. А для вещественных это зло… Вот если бы имелся какой-то PrintReal, выводящий вещественные (с автоприведением к ним целочисленных) значения, у которого первый параметр задавал бы число знаков в дробной части (в целой не надо, потому что Print’ом таблицы все равно не рисуют), это было бы превосходно.

В .Net это делается переопределением метода ToString или добавлением строки форматирования к тому же complex.ToString("{0:MyComplex}"). Не много замарочливо, но возможно каждому самому под себя делать любой вывод в зависимости от типа Возможно ли это реализовать тут?

Всё, что есть в библиотеке .NET, будет работать и тут

Обновился сегодня до последней версии и заметил одну странность - есть у меня один проект WinForms - компилируется он успешно, а при запуске падает по System.ArgumentNullException: Value cannot be null.

Внутренняя ошибка компилятора в модуле [pabcnetc.exe] :'System.Exception: System.ArgumentNullException: Value cannot be null.
Parameter name: key
   at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
   at System.Collections.Generic.Dictionary`2.ContainsKey(TKey key)
   at PascalABCCompiler.NETGenerator.ILConverter.ConvertFromTree(IProgramNode p, String TargetFileName, String SourceFileName, CompilerOptions options, String[] ResourceFiles)
   at PascalABCCompiler.CodeGenerators.Controller.Compile(IProgramNode ProgramTree, String TargetFileName, String SourceFileName, CompilerOptions options, Hashtable StandartDirectories, String[] ResourceFiles)
   at PascalABCCompiler.Compiler.Compile()'

и потом уже ничего не собирается. при этом собранный exe’шник запускается и работает нормально, проблема только при попытке запуска прямо из IDE. раньше это работало.

Это странно. У нас все проекты WinForms работают

А это пасхалочка от разработчиков такая? промахнулся и качай в 10 раз больше?) а я нашел) Случайно увидел что ссылка при наведении не целиком подсвечивается.

Тут вот у @Ret_ReT была проблема с запуском приложения с использованием CRT, я попробовал запустить свой проект winForms без связи с оболочкой, появилась консоль ProgramRunner.exe с единственной строкой - программа завершена, нажмите любую клавишу… - как будто запускается пустая программа типа begin end. То есть вроде бы ничего общего, но на мысли наталкивает.