В принципе, достаточно бесплатного (для некоммерческого использования) упаковщика исполняемых файлов, чтобы сжимать скомпилированные программы на PABC.NEТ. Меня полностью устраивает открытый UPX, но вот только
Win32/PE (исключая файлы, построенные на .NET Framework)
Да, тема установщика тоже важна для объёмных проектов, включая портативные версии, поэтому кроме Inno с доступными исходниками на Delphi, бывает достаточно 7zip, встроенного iexpress или даже wix
есть ли для готового проекта опция вроде {$i- $r-} или on error resume next, чтобы игнорировать все исключения и ошибки?
как правильно называется задача поиска, например, превращения слова “муха” в “слон” путём замены максимум одной буквы в каждом последующем слове из словаря или нахождение операторов между известными параметрами
A ? B ? C ? D ? E = F; где операторы могут быть +/-/*/:
Вроде нет. Но есть возможность сделать глобальный try except в каждом потоке, который будет ловить каждое не пойманное исключение и сувать в логи.
По моему просто продолжать когда возникла ошибка не только невозможно в .Net, а ещё и опасно. Мало ли, какая утечка памяти возникнет из за этого, ещё комп перезагружаться придётся итоговому. Это хуже чем если программа только вылетит.
Называется не знаю, но для решения я бы использовал граф. И искал бы рекурсией, похожей на поиск пути. Вообще интересная задачка, сейчас попробую реализовать.
Слова взял из C:\Program Files (x86)\PascalABC.NET\Files\Databases\words.txt.
Сравнение сделал только общей части слов, если они разной длины - конец 1 из слов просто отбрасывает при сравнении. Иначе получаем 24323 не связанных друг с другом графов.
Так же, как другие поиск пути по графам делают я не искал, ибо надо сначала всегда самому попытаться сделать. В итоге получилось что путь в 6 звеньев (4 промежуточных) между слоном и люстрой занимает ~15 сек.
Сам алгоритм поиска находится в ПС.pas, в методе GraphNode.FindPath. Оптимизации были следующие:
Для начала я, конечно, создал и сериализовал в файл графы, чтоб каждый раз заново их не собирать.
После нахождения хоть какого то пути, при поиске более коротких - пути которые явно длиннее уже найденного отсекаются с помощью параметра max_depth.
Найденные пути к последнему искомому звену запоминаются в словаре, чтоб не перепросчитывать их много раз.
Теперь внимание вопрос: что можно было сделать лучше? 15 сек, по моему, многовато.
P.S. @NRA ваш пример с мухой и слоном целые 4 с половиной минуты выполняется. В общем, есть что улучшать наверняка… Но где…
Ой да, похоже - рекурсивный метод был плохой идеей. Вот с помощью присваивания каждому элементу расстояния от начальной позиции:
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
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, а задумка интересная. Через делегаты не пойдёт?
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, в другом с 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.
Как будто там есть хотя бы половина языка))) И это не экзотика, это даже не экстеншн-метод, а обычные и 1 из основных методов строк, так же как .Remove, к примеру.