Форум Программирование В помощь новичкам и любителям. |
|
bugmenot: Это известная проблема паскаля и его производных. Дело в том что в си и потомках блоки находятся в { } и их ставить совершенно легко. В паскале ставить для каждой конструкции begin-end будет адом. Хороший вариант это заменить конструкции на аналогичные из ады. if x>y x:=y; end while True if not False < (True and False) if 2 * 2 = 4 Large; Compound; Statement; end; end; end; Another; Стало всё отлично, но мы работает с готовым языком, поэтому приходится либо ставить begin-end у каждой конструкции либо использовать редакторы, позволяющие сворачивать код. Чисти код! Чисти код! Чисти код! | ||||||
|
Цитата (Gooddy): В паскале ставить для каждой конструкции begin-end будет адом. А вот и нет! В delphi есть Autocomplete, я его активно эксплуатирую. Чтобы у меня набрался begin | <---- и курcор поставился вот сюда, end;я нажимаю клавишу "b", а затем Ctrl + J. Причём, давно делаю это на автомате за 0,2-0,3 секунды. Ввод такого шаблона if | <---- курcор then begin end else begin end;- набрать "ifte" и Ctrl + J. Если набрать "class" и нажать Ctrl + J, то вылезет вот такая заготовка: // Класс-... T| = class private // Поля // Функции доступа к полям/свойствам // Технологические функции public // Конструктор и деструктор constructor Create; destructor Destroy; override; // Методы // Свойства // События end;Ну, и т.д. Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! | ||||||
|
Цитата (IlluminatI): Научи Хорошо, покажу на примере потомка TListItem. Допустим, у тебя есть ListView, а к каждому TListItem тебе надо привязать некие данные, к примеру, вот такие: var IntValue: Integer; FileName: string; Rect: TRect; Window: HWND; Node: TTreeNode; Pages: TList; Придумывал на ходу и от балды. Естественно, данные могут быть любые и в любом количестве в зависимости от поставленной задачи. Привязать эти данные можно с помощью свойства TListItem.Data: Pointer, если набросать вот такую структуру: type PMyData: ^TMyData; TMyData = record IntValue: Integer; FileName: string; Rect: TRect; Window: HWND; Node: TTreeNode; Links: TList; // <--- дочерний объект, для примера end; Как, когда и где будут динамически создаваться функцией (New()) и убиваться (Dispose()) экземпляры такой структуры, а также вызов конструктора и деструктора дочернего объекта (Links.Create(), Links.Free()), и где их указатели будут присваиваться свойству Data нужного Item-a - является личным геморроем самого программиста, а количество вылетающих Access violation и утечек памяти прямо пропорционально кривизне его рук. Обращение к полям такой структуры может выглядеть примерно вот так: var Item: TListItem; D: TMyData; begin Item:= ListView.Selected; if Assigned(Item) and Assigned(Item.Data) then begin D:= Item.Data^; with D do begin IntValue:= 100501; FileName:= 'C:\Порно\Гитлер_и_обезьяна.avi'; Node:= TreeView.Selected; Links.Add(PageControl.Pages[0]); Rect.Left:= -273; // и т.д. end; // with end; // if end; Можно, конечно, структуру (record) заменить на класс. Будет намного изящнее и не нужно будет думать о создании/убиении дочернего объекта (FLinks): interface type TMyClass = class private FIntValue: Integer; FFileName: string; FRect: TRect; FWindow: HWND; FNode: TTreeNode; FLinks: TList; public constructor Create; destructor Destroy; override; property IntValue: Integer read FIntValue write FIntValue; property FFileName: string ...; property Rect: TRect ... ; property Window: HWND read FWindow; property Node: TTreeNode read FNode write FNode; property Links: TList read FLinks; end; implementation constructor TMyClass.Create; begin // инициализация полей FLinks:= TList.Create(); // дочерний объект FIntValue:= 100500; with FRect do begin Left:= ... ; Top:= ... ; Right:= ...; Bottom:= ...; end; FWindow:= FindWindowEx(...); FNode:= nil; FileName:= 'C:\Порно\Чиччолина_и_карданный_вал_от_камаза.avi'; end; destructor TMyClass.Destroy; begin FLinks.Free(); // дочерний объект inherited; end; Обращение к свойствам класса будет тогда выглядеть примерно так: begin if Assigned(ListView.Selected) and Assigned(ListView.Selected.Data) then with TMyClass(ListView.Selected.Data) do begin IntValue:= 100501; FileName:= 'C:\Порно\Дик_Адвокат_и_11_телепузиков.avi'; Node:= TreeView.Selected; Links.Add(PageControl.Pages[0]); Rect.Left:= -273; // и т.д. end; // with end; Здесь, однако, остаётся проблема выделения и освобождения памяти под сами экземпляры класса, а также присваивание указателей свойству TListItem.Data в нужных местах. И это, конечно, не есть комильфо, и, поэтому, ИМХО, проще написать наследника от TListItem, который будет содержать все нужные поля и свойства. Также к нему можно прикрутить необходимые методы и даже события! Ну, а выглядеть всё это хозяйство будет примерно так: interface type TMyItem = class(TListItem) private FIntValue: Integer; FFileName: string; FRect: TRect; FWindow: HWND; FNode: TTreeNode; FLinks: TList; public constructor Create(AOwner: TListItems); override; destructor Destroy; override; property IntValue: Integer read FIntValue write FIntValue; property FFileName: string ...; property Rect: TRect ... ; property Window: HWND read FWindow; property Node: TTreeNode read FNode write FNode; property Links: TList read FLinks; end; implementation constructor TMyItem.Create(AOwner: TListItems); begin inherited Create() // инициализация полей FLinks:= TList.Create(); // дочерний объект FIntValue:= 100500; with FRect do begin Left:= ... ; Top:= ... ; Right:= ...; Bottom:= ...; end; FWindow:= FindWindowEx(...); FNode:= nil; FileName:= 'C:\Порно\Буратино_и_циркулярная_пила.avi'; end; destructor TMyItem.Destroy; begin FLinks.Free(); // дочерний объект inherited; end; Теперь для доступа к данным не надо заморачиваться со свойством Data и выделением/освобождением памяти. Обращение к свойствам: begin if Assigned(ListView.Selected) then with TMyItem(ListView.Selected) do begin IntValue:= 100501; FileName:= 'C:\Порно\Терморектальный_криптоанализ.avi'; Node:= TreeView.Selected; Links.Add(PageControl.Pages[0]); Rect.Left:= -273; // и т.д. end; // with end; Добавлять такие Item-ы в TListView придётся не методом TListItems.Add(), а вручную. Примерно вот так: var Item: TMyItem; begin Item:= TMyItem.Create(ListView.Items); ListView.Items.AddItem(Item); end; Кстати, для потомков TTreeNode ситуация упрощается наличием события TTreeView.OnCreateNodeClass, в обработчик которого можно подставлять тот класс-наследник TTreeNode, который нужен. Для более удобной работы с классом TMyItem можно написать также наследника от TListItems, изменив метод Add() и все необходимые остальные, а также наследника от самого TListView. Где-то тут на сайте я вываливал несколько модулей с примерами про наследников TPageControl, TTreeView и т.п. З.Ы. Весь код в этом посте является непроверенным текстом, написанным прямо в редакторе браузера. З.З.Ы. Сам пост не является наставлением, как правильно писать программы. Так пишу программы я, мне так удобно. А вы пишите так, как хотите. З.З.З.Ы. После предварительного просмотра поста я заметил, что скрипт подсветки кода поломал всё моё форматирование, понавставлял переносов на след. строку и удалил символы слэша. Как это побороть - не знаю. На этом всё. Жду критики. Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! | ||||||
|
Цитата (Gooddy): В паскале ставить для каждой конструкции begin-end будет адом. Ни Вирт, ни Йенсен не обещали, что будет легко :) Цитата (Gooddy): либо использовать редакторы, позволяющие сворачивать код Так не пойдёт. При хороших инструментах и желании можно сделать качественным любой говнокод. Так что оценивать надо при минимальных усилиях со стороны читателя. Практическим минимумом является листинг в книге, ограниченный только отступами. А вот еще тема для писания: нубы по каким-то своим причинам (я не зоолог и не знаю почему) постоянно лепят глобальные переменные в приложениях VCL. Причем подпрограммы чаще всего оказываются в правильной приватной области видимости, а какая-нибудь несчастная сумма оказывается приткнута в том же объявлении, где и указатель на экземпляр класса формы. виконання програми розпочинається з того самого мiсця, де призупинилося. | ||||||
|
bugmenot: Использование областей видимости, методов и полей и прочих вкусностей это вообще отдельная тема =) Чисти код! Чисти код! Чисти код! | ||||||
|
Так устал физически, что хочется напрячься интеллектуально. Типы. I. Использование слишком больших переменных. Посмотрим на такое безобразие: var i: Integer; begin for i:=0 to 100 do ... end; Для переменной i вполне хватило бы даже типа Byte, т.к. Byte вмещает в себя значения от 0 до 255. Integer же вмещает значения от -2 млрд до 2 млрд (округлено). var i: Byte; begin for i:=0 to 100 do ... end; Хотя замена типа с Integer на Byte не даст такой большой прирост в скорости, она точно убережёт читателей вашего кода от размышлений, почему же вы выбрали тип Integer; Вывод: если известно в каких пределах будет колебаться значение переменной, используйте минимальный тип. II. Выбор слишком маленького типа для вводимых данных. Данные которые могут быть введены, например числа для сложения, должны быть такого размера, чтобы удовлетворять нуждам пользователя. Возьмём такой пример: var A, B, Difference: Byte; begin ReadLn( A ); ReadLn( B ); Difference := A - B; WriteLn( Difference ); end; Такой код возможно удовлетворит ваши потребности, если число A больше или равно числу B. В противном случае грозит ошибка переполнения или неверный результат (что ещё хуже). Более того, мы не можем вводить отрицательные числа, или числа более 255! Исправим код: var A, B, Difference: Int64; begin ReadLn( A ); ReadLn( B ); Difference := A - B; WriteLn( Difference ); end; Теперь мы можем вводить отрицательные числа, и числа больше чем 255. Намного. Но тип Int64 всё равно ограничен. Он принимает значения от -9223372036854775808 до 9223372036854775807. Возможно это намного больше чем нам нужно. Тогда необходимо поправить тип до Integer или даже до Word. Вывод: если пользователю может потребоваться ввести большое число, такая возможность должна присутствовать. III. Использование "массивов" переменных. У многих новичком можно встретить такое безобразие: var
a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13: Byte;
И для того, чтобы например обнулить n-ый элемент они пишут такую процедурку: procedure Set_An_Zero( n: byte ); begin case n of 1: a1 := 0; 2: a2 := 0; 3: a3 := 0; 4: a4 := 0; 5: a5 := 0; 6: a6 := 0; 7: a7 := 0; 8: a8 := 0; 9: a9 := 0; 10: a10 := 0; 11: a11 := 0; 12: a12 := 0; 13: a13 := 0; end; Код взят из реального источника и подвергнут минимальному редактированию. Такое дело решается просто: массив! (для начала рекомендую прочитать про массивы в любом учебнике по pascal\delphi) var a: array of byte; begin SetLength( a, 13 ); .. a[n] := 0; //Установить An в 0 ... end; Вывод: если имеются N>2 связанных переменных одного типа и назначения, они должны быть реализованы в виде массива. IV. Тип перечисление. Часто есть какой либо параметр-разделитель, который может принимать 5-10 значений, скажем Карточная масть = ( Пики, Трефы, Червы, Бубны ) Многие новички реализуют это так: var Card: String; begin Card := 'Clouds'; //Трефы с англ. Card := 'Diamonds'; //Бубны с англ. Card := 'Hearts'; //Червы с англ. ... end; Но тогда на хранение масти уйдёт 5-8 битов! Мало того что легко будет опечататься и компилятор это пропустит. Более продвинутые пишут так: var Card: byte; begin Card := 0; //Трефы. Card := 1; //Бубны. Card := 2; //Червы. ... end; Но в таком коде легко запутаться, он непонятный и можно ввести например масть 99 которой не существует. Правильно делать так: var Card: ( Clouds, Diamonds, Spades, Hearts ); begin Card := Clouds; Card := Diamonds; end; А также всплыла дополнительная тема. Для сложных типов (название которых превышает одно слово, необходимо составлять свой идентификатор. Супер правильно будет так: type TCard = ( Clouds, Diamonds, Spades, Hearts ); var Card: TCard; begin Card := Clouds; Card := Diamonds; end; Этот тип называется "перечисление", и он освобождёт от недостатков двух предыдущих методов: он понятен, он не даёт ошибиться или выйти за границы типа и он занимает всего 1 байт в памяти. Вывод: используйте тип перечисления вместо строк или чисел, когда это необходимо. V. Параллельные массивы. Часто нам нужно записывать какие-либо связанные между собой данные. Например фамилии, курсы, тип обучения студентов. Новички сделают так: var StudentLastName: array of string; StudentCourse: array of byte; StudentType: array of ( Free, Cost ); Но правильно будет использовать записи (record)! type TStudent = record LastName: String; Course: Byte; Type: ( Free, Cost ); //По хорошему компилятор ругнётся, но так лучше end; var Students: array of TStudent; С виду преимуществ у данного подхода нет. Только больше кода. Но преимущества есть и их много: 1. Все параметры студента объединены в одну структуру. 2. При добавлении/удалении элемента мы имеем дело с одним массивом. 3. Программа стала понятнее. 4. При большем числе параметров была бы куча неуправляемых массивов. Вывод: если у вас имеется два параллельных массива, элементы которых связаны логически, лучше объединить эти массивы с помощью записей. VI. Неправильное оперирование логическим типом (boolean). Многие новички (и я не так давно) грешат неправильным использованием типа Boolean. Для начала поясню. Тип Boolean имеют переменные этого типа, а также выражения вида x>y, x=y, (x>10) and (x<100) а также многие свойства компонентов. Предположим мы хотим узнать больше ли x чем y и отметить флажок CheckBox1 если это так. Многие пишут так: if x > y then CheckBox1.Checked := true else CheckBox1.Checked := false; И это работает так как надо. Но постойте. Какой тип имеет x>y? Логический. А CheckBox1.Checked? Логический. В итоге можно и нужно писать так: CheckBox1.Checked := ( x > y ); Скобки при этом можно опустить. Хорошо, а если нам необходимо сделать наоборот? Отметить флажок если x не больше чем y? Тут есть два выхода: изменить условие и инвертировать его с помощью оператора not. CheckBox1.Checked := ( x <= y ); CheckBox1.Checked := not ( x > y ); Эти две записи идентичны. Вывод: не используйте оператор if для установки значений логических переменных. VII. Множества. Я приведу лишь два примера использования множеств. Их намного больше, но до других вы сможете легко додуматься самостоятельно. Предположим нам нужно проверить входит ли x в промежуток от 0 до 50. Можно записать это так: if ( x >= 0 ) and ( x <= 50 ) then Но короче и лучше будет записать это так: if x in [0..50] thenГде [0..50] - множество целых чисел от 0 до 50. Хотя множества позволяют писать вещи намного короче, есть ряд ограничений: -множество должно описывать перечислимый тип (числовой или символьный), -множество не может содержать значений меньше 0 или больше 255. Тем не менее чаще всего необходимы именно такие проверки. Второй вариант. Необходимо проверить, что символ это оператор (+, -, *, /). if c in ['+', '-', '*', '/'] then Представьте как это выглядело бы без множеств! В общем случае множества можно записывать с интервалами ( ['A'..'Z'] ) с последовательностями ( ['.', ','] ) или с их комбинациями - ( ['A'..'Z', '.', ',', '0'..'9'] ). Экспертам: вспоминайте какие ещё типы часто забываются или используются неправильно. Чисти код! Чисти код! Чисти код! | ||||||
|
Всё нормально? Чего никто не отписывается? Чисти код! Чисти код! Чисти код! | ||||||
|
Цитата (Gooddy): Всё нормально? Чего никто не отписывается? На счёт использования переменной типа Byte в качестве параметра цикла for - это ты загнул. Советую посмотреть в окне CPU. Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! | ||||||
|
Берём кусок кода: procedure MakeVector(var X: TVector; const ALength: Integer); var Index: Byte; //Index: Integer; begin SetLength(X, ALength); for Index:= 0 to ALength - 1 do X[Index]:= Random(10); end;А теперь, как грицца, найди 100500 отличий и скажи, в чём тут выигрыш: Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! | ||||||
|
Цитата: "Хотя замена типа с Integer на Byte не даст такой большой прирост в скорости, она точно убережёт читателей вашего кода от размышлений, почему же вы выбрали тип Integer" Я клоню к тому, что тут советы по тому как писать правильный код а не тот, что быстро работает. Чисти код! Чисти код! Чисти код! | ||||||
Перейти в раздел:
© 2004 - 2024, Delphi.int.ru |
Версия форума: 1.10 (19.01.2010) |
Выполнено за 0.07 сек. |