Логин:     Забыли пароль?
 
Пароль:   Регистрация

Форум / Программирование - В помощь новичкам и любителям.  

В помощь новичкам и любителям.


Страницы: «1» «2» «3» «4» «5» «6» «7» «8» «9» «10»
Gooddy
Gooddy
3-ий класс
Сообщения: 84
[Сообщение #21] 16 июня 2011, 10:58
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 у каждой конструкции либо использовать редакторы, позволяющие сворачивать код.

Чисти код! Чисти код! Чисти код!
min@y™
min@y™
Доктор наук
Сообщения: 400
[Сообщение #22] 16 июня 2011, 11:36

Цитата (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. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
min@y™
min@y™
Доктор наук
Сообщения: 400
[Сообщение #23] i 16 июня 2011, 13:29

Цитата (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. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
bugmenot
bugmenot
3-ий класс
Сообщения: 88
[Сообщение #24] 16 июня 2011, 21:41

Цитата (Gooddy):

В паскале ставить для каждой конструкции begin-end будет адом.

Ни Вирт, ни Йенсен не обещали, что будет легко :)

Цитата (Gooddy):

либо использовать редакторы, позволяющие сворачивать код

Так не пойдёт. При хороших инструментах и желании можно сделать качественным любой говнокод. Так что оценивать надо при минимальных усилиях со стороны читателя. Практическим минимумом является листинг в книге, ограниченный только отступами.

А вот еще тема для писания: нубы по каким-то своим причинам (я не зоолог и не знаю почему) постоянно лепят глобальные переменные в приложениях VCL. Причем подпрограммы чаще всего оказываются в правильной приватной области видимости, а какая-нибудь несчастная сумма оказывается приткнута в том же объявлении, где и указатель на экземпляр класса формы.

виконання програми розпочинається з того самого мiсця, де призупинилося.

Gooddy
Gooddy
3-ий класс
Сообщения: 84
[Сообщение #25] 16 июня 2011, 23:44
bugmenot: Использование областей видимости, методов и полей и прочих вкусностей это вообще отдельная тема =)

Чисти код! Чисти код! Чисти код!
Gooddy
Gooddy
3-ий класс
Сообщения: 84
[Сообщение #26] 22 июня 2011, 20:56
Так устал физически, что хочется напрячься интеллектуально.

Типы.

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
Gooddy
3-ий класс
Сообщения: 84
[Сообщение #27] 23 июня 2011, 18:03
Всё нормально? Чего никто не отписывается?

Чисти код! Чисти код! Чисти код!
min@y™
min@y™
Доктор наук
Сообщения: 400
[Сообщение #28] 24 июня 2011, 08:17

Цитата (Gooddy):

Всё нормально? Чего никто не отписывается?

На счёт использования переменной типа Byte в качестве параметра цикла for - это ты загнул. Советую посмотреть в окне CPU.

Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
min@y™
min@y™
Доктор наук
Сообщения: 400
[Сообщение #29] 24 июня 2011, 08:40
Берём кусок кода:
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. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
Gooddy
Gooddy
3-ий класс
Сообщения: 84
[Сообщение #30] 24 июня 2011, 14:21
Цитата:
"Хотя замена типа с Integer на Byte не даст такой большой прирост в скорости, она точно убережёт читателей вашего кода от размышлений, почему же вы выбрали тип Integer"

Я клоню к тому, что тут советы по тому как писать правильный код а не тот, что быстро работает.

Чисти код! Чисти код! Чисти код!

Страницы: «1» «2» «3» «4» «5» «6» «7» «8» «9» «10» (всего страниц: 10, текущая: 3)
Всего сообщений: 96 (сейчас показаны: с 21 по 30)

Перейти в раздел:


 © 2004 - 2024, Delphi.int.ru
Версия форума: 1.10 (19.01.2010)
RSS Delphi.int.ru Expert Код
Выполнено за 0.07 сек.
Обратная связь