Версия 3.8.1

Вышла версия 3.8.1. Основное - атрибут [Cache] и модуль PlotWPF. Список изменений - здесь.

2 лайка

{$zerobasedstrings} это дубль {$string_nullbased+}? Или последний полностью заменили?


[Cache]

  1. Потоко-безопастность. Используйте System.Collections.Concurrent.ConcurrentDictionary.

  2. Сейчас вы по 2 раза ищите ключ, потому что используйте .TryGetValue.

  3. В этой программе кеш не работает:

    [Cache]
    function f1(x: byte): byte;
    begin
      Result := x;
      exit;
    end;
    

    Потому что exit пропускает сохранение результата в кэш.

Из за проблемы с exit наверное проще и эффективнее будет сгенерировать отдельную программу, а не пытаться лепить костыли. То есть из:

[Cache]
function fib(n: integer): integer := 
  if n in 1..2 then 1 
  else fib(n-1) + fib(n-2);

Лучше генерировать:

function fib(n: integer): integer; forward;
function fib_uncached(n: integer): integer := 
  if n in 1..2 then 1 
  else fib(n-1) + fib(n-2);
var fib_cache := new System.Collections.Concurrent.ConcurrentDictionary<System.Tuple<integer>, integer>;
function fib(n: integer): integer;
begin
  var cache_key := Tuple.Create(n);
  if fib_cache.TryGetValue(cache_key, Result) then exit;
  Result := fib_uncached(n);
  fib_cache[cache_key] := Result;
end;

Да и, это падает:

[Cache]
function f1(q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h: byte): byte := 0;
[Cache]
function f2: byte := 0;

Первое потому что Tuple не может иметь столько аргументов. В идеале вообще бы генерировать свою запись/класс для ключей.
А второе просто запретить бы…

1 лайк
Функции с атрибутом [Cache] должны быть определены глобально

По моему нет смысла запрещать [Cache] внутри класса/записи.
Возможность стрелять себе в ногу в обоих случаях одинаковая:

var a: byte;

[Cache]
function f1(b: byte): integer := a+b;

begin
  a := 2;
  f1(1).Println;
  a := 3;
  f1(1).Println;
end.
type
  t1 = class
    a: byte;
    
    [Cache]
    function f1(b: byte): integer := a+b;
    
  end;
  
begin
  var a := new t1;
  a.a := 2;
  a.f1(1).Println;
  a.a := 3;
  a.f1(1).Println;
end.

В то же время внутри классов у меня не редко встречается код вроде:

    private static KMP_Cache := new System.Collections.Concurrent.ConcurrentDictionary<string, array of StringIndex>;
    public static function KMP_GetHeader(str: string): array of StringIndex;

Где этот атрибут был бы очень кстати.

Лучше сказать - полностью заменили. В Delphi - ровно эта директива компиляции. Старый остался пока, но нигде в документации его нет

Не совсем. Экземплярные методы для того и предназначены чтобы менять данные-члены. Но понимаю, что в некоторых случаях хотелось бы.

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

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

Да, исправили

А как искать единожды?

Да, спасибо - здОрово, это исключение не пришло в голову.

На первых порах можно вообще запретить кешировать с exit

И кстати функции с yield тоже запретить

И помечать атрибутом этим не функции

Я же написал, и в том абзаце, и в примере ниже - используя .TryGetValue вместо .ContainsKey .

  1. Не обязательно, есть ещё случаи с инкапсуляцией вроде:
  Searcher = sealed class
    private search_target: string;
    public constructor(search_target: string) :=
    self.search_target := search_target;
    
    public function FindIn(text: string) :=
    text.IndexOf(search_target);
    
  end;
  1. Кроме экземплярных есть ещё статические методы. Им уже точно нечем отличаться от глобальных подпрограмм.

System.Collections.Concurrent.ConcurrentDictionary потоко-безопасен, чем он не подходит?

А сделать через дополнительную подпрограмму, как я привёл в примере, будет не быстрее, чем бежать всё запрещать?
Из за exit даже ничего не ломается, только по скорости не идеально эффективно…

Неплохо было бы еще добавить в список изменений по системной библиотеке о поддержке многоязычного вывода в консоль по умолчанию (т.е. про переход на вывод в UTF-8 вместо однобайтовой системной кодировки). Упомянув при этом о необходимости в старых версиях Windows дополнительно настроить консольный шрифт, поддерживающий Юникод (напр., Lucida Console), в настройках cmd.exe (и для пункта Defaults, и для Properties).

Для некоторых пользователей это может быть полезно.

P.S. Консоль Windows нормально поддерживает только 65K подмножество символов UCS-2/BMP (т.е. до U+FFFF) и простой рендеринг юникод-глифов: корректный вывод составных символов, лигатур и суррогатных пар не гарантируется (особенно до Win10 Creators Update).

Это надо делать и тестировать. А сейчас ошибка. Стандартный ход в таких случаях - делается с ограничениями, а потом ограничения снимаются.

Упомянул - спасибо. Меня честно говоря пугает это. У нас во многих школах еще Windows XP стоит или Windows 7/

Тормознутый потому-что. Лучше пометить поле атрибутом ThreadStatic

1 лайк

Да вроде нормально все у XP c выводом в консоль в этом плане (кроме нюансов, упомянутых выше), сейчас вынужденно сижу на ней. Lucida Console – родной майкрософтовский шрифт, входит в стандартную поставку XP и Win7, на более поздних версиях идет нативный Consolas. При необходимости быстрой перенастройки на многих машинах можно простенький батник написать для замены консольного шрифта у всех профилей.

Я тут другой странный баг сегодня заметил:

  1. При выводе в UTF-8 во внешнюю консоль все работает нормально (кроме азиатских символов, которых просто нет в стандартном шрифте):

console_UTF-8

Но при этом в служебном окне вывода внутри IDE у меня в тесте одновременно (!) появилось сразу 3 разных шрифта, два из которых не моноширинные, хотя в настройках редактора у меня дефолтный моноширинный Courier New. Из-за этого выравнивание столбцов теперь иногда не работает и мультиязычный текст выглядит довольно забавно:

output_UTF-8

Если же выставить в коде принудительно System.Console.OutputEncoding := System.Text.Encoding.GetEncoding(1251), то во внешней консоли остаются только русские и простые латинские буквы (как и ожидалось)

console_1251

а внутри среды шрифт становится правильным (одинаковым и моноширинным), но с самой кодировкой уже возникает какой-то глюк (русские буквы пропадают!). Та же история с любыми другими однобайтовыми кодировками ANSI – остается видна только латиница:

output_1251

Где-то мешается двойная перекодировка?

Исходник для сравнения отображения шрифтов и кодировок.

unicode test 2.pas (1.2 КБ)

  1. Еще было бы неплохо добавить в настройки размера шрифта промежуточные нечетные значения (как минимум 9, 11, 13, 15) – иногда этого очень не хватает для оптимальной настройки редактора при нестандартном dpi, масштабировании или другом шрифте.

  2. И чтобы два раза не вставать – желательно отображать в списке только моноширинные шрифты, остальные отфильтровывать (можно по чекбоксу), а то там много лишнего “мусора”.

1 лайк

Про шрифт окна вывода я уже писал, вот ещё 1 пример:

## uses OpenGL;
Mtr2d.Identity.Println;

При чём так нормально:

## uses OpenGL;
''.Println;
Mtr2d.Identity.Println;

Всё же нужно перед каждым exit вставлять пополнение словаря

Сейчас в дистрибутивах используются 2 версии .NET – v4.0 для Windows XP/Vista и v4.7.1 для всего остального под Windows. При этом XP и Vista совместимы с .NET 4.6, а .NET 4.8 поддерживает Win 7 и выше. Есть какие-то объективные причины не включать в дистры более актуальные версии .NET или просто никто не обращал внимание?

1 лайк

Это давняя история. Помнится, XP не поддерживала 4.5 в ранних версиях. 4.5 отличается от 4.0 очень мало.

4.8 тоже мало отличается от 4.7.1. Это можно поменять. Но при замене у массы пользователей могут быть проблемы. Ввиду незначительности этого мы это не делаем.

Следующая остановка - Net Core, но там проблемы с генерацией кода по вине Microsoft.

Исправили - используем .TryGetValue

Решили проблему с exit, оттестировали - проверяйте