Помощь новичкам

В принципе, достаточно бесплатного (для некоммерческого использования) упаковщика исполняемых файлов, чтобы сжимать скомпилированные программы на PABC.NEТ. Меня полностью устраивает открытый UPX, но вот только

Win32/PE (исключая файлы, построенные на .NET Framework)

Да, тема установщика тоже важна для объёмных проектов, включая портативные версии, поэтому кроме Inno с доступными исходниками на Delphi, бывает достаточно 7zip, встроенного iexpress или даже wix

  1. есть ли для готового проекта опция вроде {$i- $r-} или on error resume next, чтобы игнорировать все исключения и ошибки?

  2. как правильно называется задача поиска, например, превращения слова “муха” в “слон” путём замены максимум одной буквы в каждом последующем слове из словаря или нахождение операторов между известными параметрами

A ? B ? C ? D ? E = F; где операторы могут быть +/-/*/:

Или лучше backtracking?

Спс

Вроде нет. Но есть возможность сделать глобальный try except в каждом потоке, который будет ловить каждое не пойманное исключение и сувать в логи.

По моему просто продолжать когда возникла ошибка не только невозможно в .Net, а ещё и опасно. Мало ли, какая утечка памяти возникнет из за этого, ещё комп перезагружаться придётся итоговому. Это хуже чем если программа только вылетит.

Называется не знаю, но для решения я бы использовал граф. И искал бы рекурсией, похожей на поиск пути. Вообще интересная задачка, сейчас попробую реализовать.

Поиск слова.zip

Слова взял из C:\Program Files (x86)\PascalABC.NET\Files\Databases\words.txt.

Сравнение сделал только общей части слов, если они разной длины - конец 1 из слов просто отбрасывает при сравнении. Иначе получаем 24323 не связанных друг с другом графов.

Так же, как другие поиск пути по графам делают я не искал, ибо надо сначала всегда самому попытаться сделать. В итоге получилось что путь в 6 звеньев (4 промежуточных) между слоном и люстрой занимает ~15 сек.

Сам алгоритм поиска находится в ПС.pas, в методе GraphNode.FindPath. Оптимизации были следующие:

  1. Для начала я, конечно, создал и сериализовал в файл графы, чтоб каждый раз заново их не собирать.

  2. После нахождения хоть какого то пути, при поиске более коротких - пути которые явно длиннее уже найденного отсекаются с помощью параметра max_depth.

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

Теперь внимание вопрос: что можно было сделать лучше? 15 сек, по моему, многовато.

P.S. @NRA ваш пример с мухой и слоном целые 4 с половиной минуты выполняется. В общем, есть что улучшать наверняка… Но где…

1 лайк

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

    function FindPath2(last: GraphNode): array of GraphNode;
    begin
      var prev := new HashSet<GraphNode>;
      var layers := new List<HashSet<GraphNode>>;
      var curr := new HashSet<GraphNode>;
      curr += self;
      prev += self;
      var lhn: GraphNode;
      
      var found := false;
      while true do
      begin
        var next := new HashSet<GraphNode>;
        
        foreach var hn in curr do
        begin
          
          foreach var n in hn.next do
            if prev.Add(n) then
            begin
              if n=last then
              begin
                lhn := hn;
                found := true;
                break;
              end;
              next.Add(n);
            end;
          
          if found then break;
        end;
        
        layers += curr;
        if found then break;
        curr := next;
      end;
      
      Result := new GraphNode[layers.Count+1];
      Result[layers.Count] := last;
      Result[layers.Count-1] := lhn;
      Result[0] := self;
      
      for var i := layers.Count-2 downto 1 do
      begin
        var l := layers[i];
        
        foreach var n in lhn.next do
          if l.Contains(n) then
          begin
            lhn := n;
            Result[i] := n;
            break;
          end;
        
      end;
      
    end;

И работает он меньше 0.1 сек, и для моего, и для вашего примера.

P.S.
А если ещё добавить в тип звена поле cost: integer - можно ещё раза в 2 всё ускорить:

    function FindPath3(last: GraphNode): array of GraphNode;
    begin
      var prev := new HashSet<GraphNode>;
      var curr := new HashSet<GraphNode>;
      curr += self;
      prev += self;
      var lhn: GraphNode;
      var lc := 1;
      
      var found := false;
      while true do
      begin
        var next := new HashSet<GraphNode>;
        
        foreach var hn in curr do
        begin
          
          foreach var n in hn.next do
            if prev.Add(n) then
            begin
              if n=last then
              begin
                lhn := hn;
                found := true;
                break;
              end;
              next.Add(n);
              n.cost := lc;
            end;
          
          if found then break;
        end;
        
        if found then break;
        curr := next;
        lc += 1;
      end;
      
      Result := new GraphNode[lc+1];
      Result[lc] := last;
      Result[lc-1] := lhn;
      Result[0] := self;
      
      for var i := lc-2 downto 1 do
        foreach var n in lhn.next do
          if n.cost=i then
          begin
            lhn := n;
            Result[i] := n;
            break;
          end;
      
    end;

Вот что пока получается, со всеми 3 методами:
Поиск слова.zip

1 лайк

Как правильно перезагрузить оператор вызова?

type
  t1 = class
    
    function operator() (a,b: byte) := a+b;
    
  end;

begin
  var a := new t1;
  var i := a(1,2);
end.

Вроде можно было, но это не точно…

И так тоже не работает:

type
  t1 = class
    
    property exec1[a,b: byte]: byte read a+b;
    property exec2(a,b: byte): byte read a+b;
    
  end;

begin
  var a := new t1;
  var i1 := a.exec1[1,2];
  var i2 := a.exec2(1,2);
end.

@Сергей, лично с подобным не сталкивался и думал заковырка в public static, но в доке MS упоминают скобки лишь в контексте индексации и приведения типов через ex/implicit, а задумка интересная. Через делегаты не пойдёт?

Смысл в том, чтоб объект можно было и вызвать, и при этом чтоб у него кастомные свойства были… А наследовать от делегатов нельзя.

Нет, это в C++ такое было. В NET нельзя

1 лайк

Уважаемые,

1) подскажите как правильно и без лишних прибамбасов вроде RegEx перезагрузить .Replace для строк, чтобы кроме указания искомого и замены можно было указывать кол-во удаляемых вхождений (cardinal?)

Пока такой вариант выкусывания подстроки
Function string.operator-(s1,s2:string):string;
begin
  if IsNullOrEmpty(s1) or IsNullOrEmpty(s2) then exit; //nothing
  var _pos := s1.IndexOf(s2,0);
  if (_pos >-1) then Result:= s1.Substring(0, _pos) + s1.Substring(_pos + s2.Length);
 // может есть что улучшить по новым канонам, кроме Left/Right или Result:=s1.Remove(_pos,s2.Length);?
end;

2) Как оптимальнее подсчитывать количество вхождений подстроки?

3) также пока не особо понятно почему в отличие от того же stringbuilder, перегрузка “плюса” через Concat не даёт ощутимых преимуществ при обработке массивов строк

Благодарю

Вот я не понимаю той смысловой нагрузки, которую Вы придаете взятым Вами же в кавычки словам в контексте программирования. По-моему, Вы (причем, уже не в первый раз) смешиваете алгоритм и его реализацию в конкретном языке.

Правильно с какой точки зрения? Алгоритмически правильно все, что приводит к нужному результату в рамках условия и поставленных ограничений. Программно правильно все, что реализует алгоритм на вычислительной установке в рамках имеющейся аппаратной и программной платформы.

Оптимально с какой точки зрения? Понимания кода, компактности кода, скорости выполнения программы, затратах на оперативную память, времени написания программы… ?

Спасибо, @Алекс, но там нет кавычек и, насколько я понимаю, полемику и умственные рефлексии хотя иногда и полезны, однако им место скорее в “Болталке” или оффтоп.

Разумеется, для решения большинства задач достаточно парадигмы структурного программирования, а в данном контексте (как ни странно) речь о современном подходе для PascalABC .NEТ

Да, извините, там КУРСИВ.

Что касается парадигмы структурного программирования, она никак не определяет ни правильности алгоритма (да и не может определять), ни оптимальности кода. Неправильно в структурном программировании то, что нарушает его концепцию - и только. Поэтому вопрос о “правильности” остается открытым".

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

Перегрузить*

function Remove(self: string; s: string; c: integer): string; extensionmethod;
begin
  var sb := new StringBuilder(self);
  //ToDo
  Result := sb.ToString;
end;

begin
  'abcabcabc'
  .Remove('b',2)
  .Print;
end.

Потому что:

s1 + s2 + s3

Это:

(s1 + s2) + s3

То есть:

Concat(Concat(s1, s2), s3);

То есть внутренний Concat создаст лишнюю строку.

А это вообще зря. Concat реализован через StringBuilder, что, хоть и быстро, но, думаю, внутренняя реализация сложения 2 строк должна всё же быть быстрее, ибо там через неуправляемую часть кода всё делается (если я правильно помню).


Вот это как раз в болталку стоило бы. Потому что слова красивые но по делу мало. Лучше бы, всё же, ответили, что вы имеете в виду под “оптимально”.

1 лайк

Можно спросить, зачем придумана вся эта путаница с индексацией строк, в одном случае они индексируются с 1, в другом с 0? Например:

begin
  var s := '15x + 17 = 0';
  s.IndexOf('x').Println;//2
  s[:s.IndexOf('x')].Println;//1
end.

я хочу выделить все символы строки до символа ‘x’, использую метод IndexOf, в методах считается, что строки индексируются с 0, потом беру срез и… а срез считает, что строки индексируются с 1. И поди запомни все это.

С 1 индексируется только в s[]. Это из за совместимости со старыми паскалями. Там, вроде, в s[0] хранилась длинна строки. Во всех остальных случаях индексация идёт человеческая, то есть с 0.

Она и теперь хранится там.

В строках с нуля нумерация производится:

  • в процедуре Delete;
  • в методах .Substring, Insert, .Remove, .ReverseString;
  • в методах, унаследованных от последовательностей и содержащих в имени слово “Index”.

С чего вдуруг?

begin
  var i := 'abc'[0];
  i.Print
end.

.Net строки хранят своё содержимое без всяких костылей. Или вы имели в виду в старых паскалях?

Легче всё же запомнить что только в [] нумерация с 1. Ибо вы ещё и не всё назвали (к примеру ещё есть .CopyTo), и даже так - запоминать тут многовато.

В старых, конечно. А в PascalABC.NЕТ при выводе в бинарный файл короткой строки Вы также можете обнаружить этот дескриптор.

.CopyTo - это .NET-экзотика, она даже в Справке не описана.

Ну это Вы увлеклись… подпрограмм, считающих символы в строке от 1, все же большинство.

1 лайк

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

Это каких?