Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Интернет для Delphi-программиста


Информационный Канал Subscribe.Ru

Интернет для Delphi программиста.

Выпуск : № 27


Здравствуйте уважаемые подписчики рассылки "Интернет для Delphi программиста". Данная рассылка предназначена для всех кого интересует Delphi, здесь будут выкладываться ссылки на различные ресурсы интернета так или иначе связанные с Delphi: книги, исходники, программы... Изучайте Delphi один из лучших языков программирования!!!


ЗАДАТЬ ВОПРОС :

Правила рассылки: 
1. Не присылайте ответов на вопросы типа "да, нет".  
2. Если отвечаешь на вопрос - то отвечай подробно с примерами (желательно с исходником примера).
3. Тема вопросов - программирование на Delphi.
Внимание авторам: - Я не указываю ваши адреса из-за спама, но кто хочет, чтобы его e-mail был - пишите, иначе только имя(ник).
Отправить вопрос


Новые вопросы.


Вопрос № 54 задаёт: Михаил Ответить
Написал программу, которая работает с базами данных Access (ADO).
Возникла необходимость использовать ее с такими же базами, но защищенными паролем. Ввожу логин и пароль базы данных при создании строки подключения - появляется сообщение об ошибке.
Подскажите в чем дело.
Вопрос № 55 задаёт: Ирина Ответить
Здравствуйте! Подскажите чайнику, как при щелчке на записи(или кнопки, неважно) в одной DBGrid перенести эту запись в другую DBGrid(чистую)?
Вопрос № 56 задаёт: Крези.ru Ответить
Здравствуйте всем !!! Давал советы,но вот и самому понадобился совет.
А вопрос такой: есть функция работы с компонентом TEdit.
Вот она:
procedure TComBox.EditCom1KeyPress(Sender: TObject; var Key: Char);
begin
pole1 := editcom1.text;  //в поле1 помещаем символ введёный в editcom1.text
if pole1 = '' then          // проверяем на пустую стоку, нет символа в
переменной Pole1
begin
 MessageBox(handle,PChar(' Поле ввода пустое, попытайтесь ещё раз
!!!'),PChar('  Сообщение !!!'),0);  //оповещаем об этом
 EditCom1.Text := '';                      //очищаем поле
 EditCom1.SetFocus;                     //устанавливаем курсор в начало
 exit;                                               //выходим
end;
if length(pole1) = 4  then   //опять какие то проверки и установки
begin
EditCom2.SetFocus;
end;
end; // всё
Так вот, обьсните кто нибудь.Почему в строковую переменную Pole1 не помещается символ введённый с клавиатуры.После первого прохода,почему-то пустая строка.После второго прохода введённый символ есть.Как добится,чтобы после первого нажатия, символ всё таки помещался в переменную.Может у кого такая проблема возникала.Заранее благодарен.А то уже 2 дня бьюсь и ни чего.
Вопрос № 57 задаёт: Rashid Ответить
Подскажите как можно bmp рисунки "собрать" в avi файл и наоборот получить кадры из avi файла ?
Заранее благодарен за ответ.

Ответы.

Вопрос № 45 задаёт: Алмаз Рафитович Ответить
Как из своей программы запустить другую?
Отвечает: dmitriy  _dmi3_@excite.com
Очень просто, можно даже тремя способами:
1-й , 2-й
Использовать WinApi. Наиболее подходит две функции:
WinExec(CmdLine: PChar; CmdShow: Word): Word; 
Наиболее простой но менее єфективный и устаревший способ
Паpаметpы:
CmdLine: Командная стpока для выполнения пpикладной задачи (заканчивающаяся пустым 
символом).Например путь 'C:\test.txt' или передавать свою переменную pchar (path) 
CmdShow: Опpеделяет, как будет изначально отобpажаться окно пpикладной задачи:
SW_HIDE - Прячет окно и переводит в активное состояние другое окно.
SW_MINIMIZE - Минимизирует окно и активизирует окно верхнего уровня в списке менеджера окон.
SW_RESTORE - Действует так же, как и SW_SHOWNORMAL.
SW_SHOW - Активизирует окно и выводит его в текущей позиции и текущего размера.
SW_SHOWDEFAULT - Активизирует окно и выводит его с использованием текущих умолчаний.
SW_SHOWMAXIMIZED - Активизирует окно и выводит его с максимально размером.
SW_SHOWMINIMIZED - Активизирует окно и выводит его в виде пиктограммы.
SW_SHOWMINNOACTIVATE - Выводит окно как пиктограмму; бывшее активныь в данный момент окно 
остается активным.
SW_SHOWNA - Выводит окно с учетом его состояния в данный момент; активное в данный момент 
окно остается активным.
SW_SHOWNOACTIVATE - Выводит окно в его прежней позиции и прежнего размера; активное в данный 
момент окно остаета активным.
SW_SHOWNORMAL - Активизирует окно и выводит его на экран. Если окно было увеличено или 
уменьшено до пиктограммы, то система Windows восстановит начальное положение и размер окна.
SW_SHOWSMOOTH - Выводит окно так, чтобы оно меньше всего перекрывалось с другими окнами.

Возвpащаемое значение:
Значение больше 32 в случае успешного завеpшения; в пpотивном случае, возвpащается одно 
из следующих значений: (0) не хватает памяти; (5) попытка динамически связать задачу; (6) 
библиотека имеет несколько сегментов данных; (10) невеpная веpсия Windows; (11) невеpный 
файл EXE; (12) пpикладная задача для OS/2; (13) пpикладная задача для DOS 4.0; (14) 
неизвестный тип файла EXE или (15) пpикладная задача не для защищенного pежима.

function ShellExecute(hWnd: HWND; Operation, FileName, Parameters,
Directory: PChar; ShowCmd: Integer): HINST;
Примечание надо в uses добавить ShellApi
ocess: THandle;
end;

Параметры:
hWnd: Хендл родителя запускаемого приложения. Может быть равно нулю, используется приложение 
соответсующее данному формату файла по умолчанию. 
Operation: Строка определяющая команду для исполнения. Может содержать:
"open" - открыть файл определенный параметром FileName.
"print" - напечатать файл определенный параметром FileName.
"explore" - открыть папку определенную параметром FileName.
Если параметр Operation равен nil, то по умолчанию выполняется операция "open".
FileName: Определяет имя файла или папки для открытия или печати. Функция может запускать 
файл на исполнение или документ на печать.
Parameters: определяет параметры передаваемые при запуске исполняемого приложения. 
Бессмысленно его использовать при запуске документа. Параметр можеть быть равен Nil.
Directory: опеределяет каталог по умолчанию(рабочий каталог,'').

Более сложная function ShellExecuteEx(lpExecInfo: PShellExecuteInfo):BOOL;
Параметры:
lpExecInfo: указатель на структуру TShellExecuteInfo которая содержит и получает информацию 
о запускаемом приложении.
Описание (см. Help..Windows SDK): 
TShellExecuteInfo = record
cbSize: DWORD;
fMask: ULONG;
Wnd: HWND;
lpVerb: PAnsiChar;
lpFile: PAnsiChar;
lpParameters: PAnsiChar;
lpDirectory: PAnsiChar;
nShow: Integer;
hInstApp: HINST; { Optional fields }
lpIDList: Pointer;
lpClass: PAnsiChar;
hkeyClass: HKEY;
dwHotKey: DWORD;
hIcon: THandle;
hPr
3-й самый простой, 
Для этого надо импортировать Microsoft Shell Controls & Automation Type Library. 
В меню Project..Import Type Library 
Выберите Microsoft Shell Controls & Automation (version 1.0). 
Нажмите Install... 
На панели компонентов, в закладке ActiveX появится несколько компонентов.
Shell (он нам и нужен) и SearchCommand. Бросаем на форму Shell. Для нас важен 
Shell.Open(vDir:OleVariant), запускает проложения, документы, при неизвестном формате 
открывается окно выбора программы:
Shell.Open('C:\Program Files\Microsoft Office\Office10\WINWORD.EXE') 
Кроме этого с помощью Shell можно открывать очень много стандартных Windows'ких диалогов. 
Например:
//Показываем окошко завершения работы Windows 
Shell1.ShutdownWindows; 

//Показываем окно поиска файлов 
Shell1.FindFiles; 
//Минимизируем все окна на рабочем столе 
Shell1.MinimizeAll; 
Вопрос № 48 задаёт: #One® Ответить
Как добраться до количества CD приводов, назначенных им букв и типу ? Например, на закладке Win 3.1 есть DriveComboBox. Нужно отображать только CD ROM'мы, а не всё подряд.
Отвечает:  Крези.ru
Как найти CD-ROM диск,я делаю так и вот функция:
function GetFirstCDROM:string;
   {возвращает букву 1-го привода CD-ROM или пустую строку}
  var
   w:dword;
   Root:string;
   i:integer;
  begin
   w:=GetLogicalDrives;
   Root:='#:\';
   for i:=0 to 25 do begin
    Root[1] := Char(Ord('A')+i);
    if (W and (1 shl i))>0
    then if GetDriveType(Pchar(Root)) = DRIVE_CDROM then begin
     Result:=Root[1];
     exit;
    end;
   end;
   Result:='';
  end;
Вопрос № 50 задаёт:  wvw Ответить
Пишу программу "Редактор настроек Windows" на подобии "WinSEr", подскажите пожалуйста как в свойствах "Пароли" под Win98 в закладке "Удаленное управление" программно добавить определенного пользователя или удалить его?
Отвечает: Крези.ru
Скачай Reestr Tweak XP в рассылке N26 - это исходник установки значений в реестре. Написана на Delphi7. И всё встанет на свои места.
Вопрос № 51 задаёт: TIM Ответить  
У меня есть вопрос: как можно запретить запись в Журнал (History) и (или) очистить при выходе именно через реестр? Пробовал NoRecentHistory но не работает или это не правильно и что озночает этот параметр - NoDriveTypeAutoRun?
Помогите если вас не затруднить.
Заранее благодарен!
Отвечает: Крези.ru
Попробуй через флаги типа Boolean или Dword путём проверки значения: Если True - запретить запись в Журнал ,and if False - разрешить запись в Журнал и естественно 0 - разрешить запись,1-запретить запись или наоборот,всё зависит от алгоритма программы.
Вопрос № 52 задаёт:  Алмаз Рафитович Ответить
Как сделать так, чтобы при перемещении файла методом Drag&Drop или &Dock в ListBox в нем появлялась строка с именем и путем к этому файлу.
Заранее благодарен.
Отвечает: Skyscraper Recluse
Первый способ: источник файлов на самой форме, что позволяет
использовать родной механизм Delphi для обеспечения операции.

Пусть на форме имеются ListBox1: TListBox и FileListBox1:
TFileListBox.

FileListBox1.DragMode := dmAutomatic;

После этого перетаскивание из FileListBox1 инициируется автоматически
при действиях пользователя. Значение этого свойства по умолчанию
(dmManual) требует определённых действий.

Основной код касается приёмника drag'n'drop - ListBox1.

Когда пользователь "таскает" над ListBox1, возникают события
OnDragOver. Обработчик события получает в качестве параметров
состояние перетаскивания (State): движение мыши внутрь, внутри или наружу
данного контрола) - и флаг допустимости перетаскиваемой информации
(Accept). По умолчанию Accept содержит true, поэтому в данном случае,
когда особой обработки не требуется, тело можно оставить и пустым.

//=============================================================================

procedure TForm1.ListBox1DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  if Source is TFileListBox then
    Accept := true
  else
    Accept := false;
end;

//=============================================================================

Когда пользователь "бросает", возникает событие OnDragDrop.
Обработчику передаётся ссылка на объект Source: TObject, из которого
происходит перетаскивание. В данном случае код будет такой.

//=============================================================================

procedure TForm1.ListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
  if Source is TFileListBox then
  begin
    ListBox1.Items.Add((Source as TFileListBox).FileName);
  end;
end;

//=============================================================================


Второй способ позволяет принимать перетаскивание файлов из других
окон, и реализуется с помощью WinApi (а конкретно, ShellApi).

Прежде всего, необходимо зарегистрировать окно, которое будет
принимать файлы, в качестве такового для Windows. Делается это
функцией DragAcceptFiles(hWnd, fAccept), где hWnd - дескриптор окна,
fAccept имеет тип BOOL и указывает на то, принимает окно файлы или
нет. Вызов функции целесообразно делать на OnCreate формы.

Во-вторых, потребуется собственно обработка получаемых файлов. Когда
пользователь перетаскивает файлы из Проводника, форма получает
сообщение WM_DROPFILES; lParam его не используется, а wParam содержит
дескриптор некоей внутренней структуры, необходимой для работы с
перетаскиваемыми файлами. Естественно, в конце работы его нужно
закрыть функцией DragFinish().

Пусть на форме имеется листбокс ListBox1: TListBox.

//=============================================================================

uses (* ... *), ShellAPI;

type
  Form1 = class(TForm)
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    // ...
  public
    procedure OnWMDropFiles(var M: TMessage); message WM_DROPFILES;
    //.
  end;

implementation

procedure TForm1.OnWMDropFiles(var M: TMessage);
var
  CountFiles: integer;  // Количество файлов.
  i: integer;
    // Переменные для работы с функциями WinApi.
  cch: integer;     // Размер буфера под имя файла.
  hDrop: integer;   // Декскриптор сброшенных файлов.
  lpszFile: Pchar;  // Имя файла.
  nFileBuf: integer;  // Размер буфера для имени файла.
begin
  hDrop := M.WParam; // Получение дескриптора операции.

  CountFiles := DragQueryFile(hDrop, $ffffffff, nil, cch);
    // Вызов данной функции со вторым параметром $ffffffff возвращает
    // общее количество перетаскиваемых файлов.

  for i:=0 to CountFiles-1 do
  begin
    nFileBuf := DragQueryFile(hDrop, i, nil, cch) + 1;
      // При таком вызове происходит обращение к i-тому файлу из сброшенных.
      // Возвращаемое функцией значение при третьем параметре nil является
      // размером буфера, необходимого для хранения имени файла.
    GetMem(lpszFile, nFileBuf); // Выделение памяти.
    DragQueryFile(hDrop, i, lpszFile, nFileBuf);
      // Получение имени очередного файла.
    ListBox1.Items.Add(lpszFile);
      // Добавление имени файла в список.
    FreeMem(lpszFile, nFileBuf);
      // Освобождение памяти.
  end;

  DragFinish(hDrop);  // Завершение работы с перетаскиваемыми файлами.
end;

//-----------------------------------------------------------------------------

procedure TForm1.FormCreate(Sender: TObject);
begin
  DragAcceptFiles(ListBox1.Handle, true);
    // Регистрация окна листбокса как способного принимать дроп-файлы.
end;

//=============================================================================

Возможно, будет вообще полезной функция DragQueryPoint(), позволяющая
получить позицию курсора, в которой файлы были "брошены".
     
Отвечает: turusov
Если я правильно понял, файлы из вне

private
  procedure WMDropFiles(var Msg: TMessage); message wm_DropFiles;

-------
implementation

procedure TfrmMain.WMDropFiles(var Msg: TMessage);
var
  Filename: array[0..256] of Char;
begin
  DragQueryFile(THandle(Msg.WParam),0,Filename,SizeOf(Filename));
  {}
  try
    //Строка ниже, это Ваш обработчик полученных файлов. Это пример
    Execute(ExtractFilePath(FileName),ExtractFileName(FileName));
  finally
    DragFinish(THandle(Msg.WParam));
  end;
end;  
     


Статья:   Хеш-таблицы. Автор: Delist. Сайт: http://www.noil.pri.ee/
После написания второй части статьи посвящённой алгоритмам сортировки, на мой e-mail, а также на форум, поступило несколько сообщений с просьбой рассказать о хеш-таблицах. Но так как материал этот достаточно объёмный, описать его в двух словах было довольно затруднительно, к тому же в тот момент мои знания в этой области были не достаточно полными, я решил несколько отложить ответ и посвятить этому отдельную статью.

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

Введение

Начать, наверно, нужно с самого определения хеширования и хеш-таблиц.
Хешированием называется преобразование ключа элемента в значение индекса, выполняется данный процесс при помощи функции хеширования. Эта функция определяет местоположение элемента в таблицы на основе значения данного элемента.
Хеш-таблица, соответственно, массив, используемый для хранения элементов, с которым используется значения индекса.
Для того что бы лучше понять предназначение хеш-таблиц, давайте вспомним алгоритмам поиска в отсортированном массиве. Там функция делала предположение о возможном местонахождении элемента, и если получала положительный результат, то возвращала индекс элемента, если же найденный ключ не являлся нужным, то делалось следующее предположение уже с учётом, предыдущего. В хеш-таблице, мы будем заранее определять местоположение элемента, чтобы при необходимости его найти, мы не тратили много времени на перебор всех значений, а находили элемент с первой попытки, однако следует отметить, что это бывает возможно не всегда. В лучшем варианте время выполнения будет равно О(1) в худшем О(n), где n - это количество элементов в таблице.
Итак, рассмотрим следующий случай. Предположим, что нам надо разместить числа от 1 до 1000 в хеш-таблице. Для начала вы создаёте массив длинной 1000 элементов и каждое его значение устанавливаете равным нулю. Теперь для того, чтобы добавить в таблицу число 112, вы находите соответствующий элемент в таблице и присваиваете ему значение равное 112. Чтобы найти, вставленный элемент вам надо всего лишь обратиться к нужной записи в массиве. Таким образом вы сможете вставлять, удалять и находить нужные элементы всего за один шаг.
К сожалению, в реальной жизни значения чисел намного больше, чем в рассмотренном выше примере и выделить для массива необходимую память не всегда будет рационально. Так например, если у нас есть 10 000 чисел в пределе от 0 до десяти миллиардов, то создавать таблицу с таким количеством элементов будет не то что не актуально, но по крайней мере глупо. И если бы нам удалось взглянуть на такой массив, то он оказался бы пустым больше чем на 99%.
Избежать подобной ситуации и найти наиболее оптимальное отношение длины таблицы к количеству записей в ней позволяет специальная хеш функция. Если в вашей фирме 300 работников и вы хотите сохранить личный номер каждого, то можете объявить массив из 450 элементов, куда и внести все данные. Но почему именно 450, ведь можно было бы создать хеш-таблицу из 300, куда тоже всё поместиться. Однако на практике оказывается, что чем больше пустых ячеек в хеш таблице тем больше её производительность, наилучшим соотношением считается 2 : 3, или на две заполненные ячейки одна пустая. Но никто вам не мешает поэкспериментировать и с другим коэффициентом загруженности.
Ещё одна проблема, с которой мы столкнёмся, это то что у двух различных ключей значение хеша совпадут и возникнет так называемая конфликтная ситуация
Из всего вышесказанного, становиться ясно, для того чтобы построить полноценную хеш-таблицу нужно два алгоритма, первый, это сам алгоритм хеширования, а второй алгоритм для разрешения возможных конфликтных ситуации. По понятным причинам, нам придётся производить и ряд других действий: вставлять, удалять, искать элементы или выяснять их отсутствие. Хеш-таблица позволяют делать это очень быстро, если конечно она не перегружена элементами. Всё это мы и рассмотрим с данной статье, но алгоритм который необходимо рассмотреть в первую очередь это хеширование.

Функции хеширования.

Этой функцией будем называть подпрограмму, которая будет принимать значение ключа и возвращать его хеш. Притом, если у нас таблица состоит из n элементов, то хеш должен быть в пределах от 0 до n-1. Очевидно, что возвращаемое значение должно быть целым не отрицательным число. Однако о типе данных, которые будут передаваться в функцию пока ничего, не говорилось, и действительно они могут быть любые.
Функция хеширования должна для похожих элементов, создавать разные значения хеша, внешне совершенно не похожие на исходный результат. Иными словами функция хеширования должна быть подобна функции генерации случайного числа и к тому же универсальна.
Простейшим случаем будет нахождения хеша от простого числа. Самое первое что приходит на ум в этом случае это использование оператора деления по модулю. В случае, если таблица содержит n значений и надо туда добавить число k, то необходимо найти значение k mod n и если оно отрицательное, то прибавить n. Для того чтобы элементы были распределены более однородно в качестве длинны таблицы нужно использовать простое число.
Но как поступать, если значение индекса является не числом, а скажем строкой? Для этого необходимо преобразовать строку в целочисленное значение и после чего использовать полученное число для деления по модулю. Самый простой способ это получить длину строки, однако у него есть свой недостаток. Так как, большинство слов состоят из 4-12 букв, то это повлечет за собой появление большого количество повторяющихся значений хеша и как следствие снижение производительности всей таблицы, что недопустимо для серьёзных проектов. Вследствие чего необходимо как-то изменить функцию хеширования.
Более лучший результат даст функция, которая для деления по модулю использует сумму всех ASCII-значения слова. Данная функция называется простой функцией хеширования.

Простая функция хеширования.

Как и было сказано выше подпрограмма будет получать в качестве параметра какое-либо слово и количество элементов таблицы, а в итоге мы будем получать хеш этого слова.

function SimpleHashFunc(Key: string; HTableHigh: integer): integer;
var
  Hash: longint;
  i: integer;
begin
Hash:=0;
for i:=0 to length(Key) do
  Hash:=((Hash*19) + ord(Key[i])) mod HTableHigh;
Result:=Hash;
if Hash<0 then
  Hash:=Hash+ HTableHigh;
end;


В начале мы устанавливаем значение хеша равным нулю. Далее в цикле получаем значение определённого ASCII-символа и прибавляем его к хешу умноженному на небольшое простое число. скажем 19 и в конце делим на длину таблицы. Все эти математические действия позволяют достичь наибольшего эффекта случайности. И в случае, если результат получился отрицательный, то прибавляем к нему длину таблицы, так как с числами меньшими нулям, работать будет неудобно.

Функции хеширования PJV

Рассмотренная хеш функция на практике, даёт довольно неплохой результат, однако существует несколько других функций, которые превосходят данную по количеству производимых математических операций, и как следствие к лучшему результату.
Одной из таких функций является функция хеширования созданная П.Дж.Вейнбергером. Её также называют ELF-хешем (Executаble and Linking Format или формат исполняемых и компонуемых модулей).

function TForm1.PJWHash(const Key: String; TableHigh: Integer): integer;
var
  Hash, G: Longint;
  i: integer;
begin
hash:=0;
for i:=1 to length(Key) do begin
  Hash:=(Hash shl 4)+ord(Key[i]);
  G:=Hash and longint($F0000000);
  if (G<>0) then
    Hash:=(Hash xor (G shl 24) xor G);
  end;
  Result:=hash mod TableHigh;
end;


И хотя код этой функции длиннее, она содержит много операций AND, XOR, MOD, которые быстро выполняются процессором. Поэтому проблем с производительностью этой функции возникнуть не должно.
Но даже столь сложная функция, может сгенерировать для различных ключей одинаковые значения хеш, поэтому алгоритм обработки конфликтов не отпадает.
Тут может возникнуть вопрос, а можно ли написать функцию которая для определённого значения ключей будет генерировать только уникальные значения. Теоретически, это возможно. Однако, как узнать что функция не возвращает повторяющихся значений? Только из практики. К тому же, если изменяться входящие значения, функция может оказаться вовсе не универсальной. Так что проще написать алгоритм обработки конфликтов, чем ломать голову над идеальной функцией хеширования.

Разрешение конфликтов посредством линейного зондирования.

На мой взгляд самое сложное в этом методе это его название, всё остальное предельно просто и понятно. Основная суть этого алгоритма это вставить нужный элемент в позицию, номер которой вернула хеш-функция, и если эта позиция занята, то на одну ниже, если же и та позиция занята, то исследовать список пока не найдётся свободное место.
Перед написанием самих функций и процедур, лучше всего, проследить процесс построения хеш-таблиц, к тому же мы сможем уловить все нюансы и избежать грубых ошибок в коде программы.
Предположим, что мы решили внести в хеш таблицу имена всех наших знакомых, создали массива из 100 элементов и предварительно заполнили его нулями. И теперь надо внести первое значение, предположим это будет "Максим", вычисляем хеш этого слова и вставляем его в таблицу, к примеру хеш был равен 97. После вставки таблица вблизи этого элемента выглядит так:

96: пусто
97: Максим
98: пусто

При вставки следующего имени "Иван" может произойти, такая ситуация, что хеш будет равен также 97, и наша программа, заметя что эта позиция уже занята, вставит его на следующее за ним 98-е место. И таблица будет выглядеть уже по другому:

96: пусто
97: Максим
98: Иван
99: пусто

Если следующий вставляемый элемент будет иметь хеш 97 или 98, то он займёт 99-ю позицию и будет достигнут конец таблицы, а этого допускать нельзя по понятным причинам, ведь после этого наша программа может обратиться к сотому элементу, а такого в таблице нет и произойдёт ошибка. Для этого необходимо несколько увеличить размер таблицы, так как этот пример тренировочный и скорость выполнения алгоритма на глаз совершенно не заметна, в примере увеличиваю значение таблицы не на некоторое простое число, а просто на 10 элементов. После проведения такой операции местоположения всех вставленных элементов кардинально поменяются и скорее всего уже не будут образовывать кластер, которым называется группа элементов расположенных друг за другом, притом между ними нет свободных ячеек.

Кластеры

Появление кластеров влечет за собой один неприятный эффект - снижение производительности всей таблицы, и чем их больше и они массивнее, тем больше это заметно. Поэтому очень важно иметь хорошую функцию хеширования, которая позволит сократить их появление. Однако полностью устранить их невозможно и чем таблица больше, тем больше шанс появления кластеров.
Это очень легко доказывается математически.
Предположим, что у вас есть пустая хеш таблиц из N элементов, вероятность того что первый элемент займёт любую позицию m равно 1/N. При вставке второго элемента, вероятность того что два элемента образуют кластер, будет равна сумме вероятностей, то что элемент m-1, m+1, или элемент попадёт в позицию m и займёт место m+1 или 1/N+1/N+1/N=3/N. Продолжая подобные рассуждения, найдём, что вероятность образования кластера из 3-х элементов - 5/N, из 4-х - 7/N, и так далее.
Так, например, если таблица заполнена на половину, то вероятность того что мы попадём с первого раза в незанятую ячейку 50% и вероятность, того что мы найдём свободное место со второй попытки тоже 50%. Но если в таблице один большой кластер, то вероятность попадание в пустую ячейку по-прежнему 50%, а вот если попадём в кластер, то свободное место будем искать очень долго.
Дональд Кнут в своих книгах показал, что общее количество зондирования до обнаружения свободного элемента приблизительно равно ½(1+1/(1-k), где коэффициент k есть отношения числа элементов в таблице к общему размеру таблицы.

Удаление элементов из хеш таблицы с линейным зондированием.

На первый взгляд эта задача кажется простой. Надо найти хеш элемента и по нему вычислить его местоположение в таблице, но подобное размышление может привести к серьёзному сбою в работе всей таблицы. Давайте посмотрим пример.
У нас уже есть некая таблица, её мы и будем использовать. Для наглядности используется следующий формат представления данных: Номер элемента в таблице: Сам элемент (Его хеш):

86: пусто (-)
87: Максим (87)
88: Иван (87)
89: Сергей (88)
90 пусто (-)

Теперь удалим из таблицы имя Иван, для этого как и говорилось ранее при помощи специальной функции получим его и хеш 87 и позицию в таблице 88. После чего 88 записи хеш-таблицы присвоим пустую строку. И наша таблица будет выглядеть следующим образом:

86: пусто (-)
87: Максим (87)
88: пусто (-)
89: Сергей (88)
90: пусто (-)

Теперь попытавшись найти в таблице имя Сергей, функция получит его хеш 88, обратимся к 88-ой ячейке и увидит, что она пуста вернёт ответ, что такого элемента нет, что будет совершенно не правильно.
Есть два пути решения подобной ситуации, или помечать ячейки как удалённые или перемешать элементы в промежутке от удалённого элемента до ближайшей пустой строки в соответствии с их хешем. В примере таким промежутком будет массив всего из одного значения, а именно 88.
В том случае, если количество данных в таблице не большое рациональней использовать второй вариант, так как перестановка элементов займёт ничтожное время, в то время как хранение ячеек помеченных как "удалённые" будет негативно сказываться на производительности таблицы. Поэтому если вы выберите первый вариант, то придётся позаботиться о функции, которая будем время от времени очищать таблицу от таких ненужных элементов, а остальные расставлять в соответствии с из хешем.

Изменение размера хеш-таблиц.

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

Практическая реализация.

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

Перед тем, как приступить к написанию вышеописанных функции, надо сделать некоторые приготовления - объявить две глобальных переменных:

HashTable: array of String;
 KeyCount: Integer;


Как вы уже поняли первая переменная есть динамический массив из строк, которая используется как хеш-таблица, а в переменной KeyCount будет храниться количество элементов хеш-таблицы, нет не всех элементов, а только тех, которые несут хоть какую-то смысловую нагрузку и не являются пустыми строками.
Для того чтобы вставлять, искать и удалять элементы из хеш таблицы нам потребуется одна очень полезная функция, которая будет получать в качестве единственного параметра значение и возвращать нам его местоположение в таблице. По сути дела эта готовая функция поиска, но её будем использовать и для других целей. Код можете увидеть ниже:

function TForm1.IndexOf(const key: String): Integer;
var
  index, FirstIndex: Integer;
begin
index:=PJWHash(key, high(HashTable)+1);
FirstIndex:=index;
while true do begin
  if HashTable[index]='' then begin
    Result:=-1;
    exit;
    end
  else begin
    if key=HashTable[index] then begin
      Result:=index;
      exit;
      end;
    end;
  index:=index+1;
  if index=high(HashTable)+1 then
    index:=0;
  if index=FirstIndex then begin
    result:=-1;
    exit;
    end;
  end;
end;


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

Функция вставки нового элемента.

Можно сказать, что основная работа уже, сделана, осталось только пожинать плоды и осталось написать только несколько простых для понимания функций. И одной из них будет процедура вставки нового элемента. Вот её код:

function TForm1.InsertK(const key: string): integer;
var
  index, hashValue, i: Integer;
begin
index:=IndexOf(key);
Result:=-1;
if index<>-1 then begin
  Showmessage('Элемент уже содержится в таблице!!');
  exit;
  end;
hashValue:=PJWHash(key, high(hashTable)+1);
if HashTable[hashValue]<>'' then begin
  for i:=hashValue+1 to high(HashTable) do begin
    if HashTable[i]='' then begin
      hashValue:=i;
      Break;
      end;
    end;
  end;
HashTable[hashValue]:=key;
result:=hashValue;
KeyCount:=KeyCount+1;
if hashValue=high(HashTable) then
  ResizeHTable(High(HashTable)+10);
end;


В функции объявлены три переменные числового типа: index - для того чтобы можно было своевременно узнать о том, что элемент уже содержится в таблице, hashValue - для хранения хеша вставляемого элемента и переменная i для запуска цикла for. В случае, если элемент уже содержится в таблице то функция вернёт -1 и выведет соответствующее сообщение.
Далее мы получаем хеш элемента и если нужная позиция уже занята, то следуем вниз по таблице, до того момента пока не найдём пустую позицию и не займём её.
В конце следует проверка того не достигнут ли конец таблицы, тогда увеличиваем её размер.
В сущности, мы написали функцию, которая в точности повторяет, всё то что было сказано о ней в теоретической части.

Удаление элемента из таблицы.

Эта функция удалит не нужный нам элемент из таблицы и вернёт его бывший индекс в таблице.

function TForm1.DeleteK(const key: string): Integer;
var
  i, index:integer;
  tmp: String;
begin
index:=IndexOf(key);
Result:=-1;
if index=-1 then begin
  exit;
  end;
if HashTable[index]='' then begin
  Result:=-2;
  exit;
  end;
HashTable[index]:='';
Result:=index;
KeyCount:=KeyCount-1;
for i:=index+1 to high(hashTable) do begin
  if HashTable[i]='' then exit;
  tmp:=HashTable[i];
  HashTable[i]:='';
  InsertK(tmp)
  end;
end;


В начале как обычно, получаем индекс элемента в таблице, если он равен -1, то такого элемента нет в таблице и удалять его бессмысленно. Далее проверяется значение элемента в позиции index и если он равен пустой строке, то его удалять тоже незачем, теоретически такого не может быть, но как показала практика лишних проверок не бывает и того, что не может быть теоретически у программиста, обязательно произойдет у пользователя.
После всех проверок, наконец-то, очищаем элемент, и переопределяем позиции всех элементов расположенных вниз по кластеру.

Хочу заметить, что писать ещё одну процедуру поиска не имеет смысла, так как она уже написана и остаётся, только передать нужный параметр в функцию IndexOf и получить результат.

Изменение размера таблицы.

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

procedure TForm1.ResizeHTable(newSize: integer);
var
  TmpTable: array of string;
  i, index: integer;
begin
if newSize then begin
  ShowMessage('Размер новой таблицы должен быть'#13'больше чем количество элементов в ней');
  exit;
  end;
SetLength(TmpTable, KeyCount);
index:=0;
for i:=0 to high(HashTable) do begin
  if HashTable[i]<>'' then begin
    TmpTable[index]:=HashTable[i];
    index:=index+1;
    end;
  end;
ClearHTable;
SetLength(HashTable, NewSize);
for i:=0 to high(TmpTable) do
  InsertK(TmpTable[i]);
end;


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

Ещё хочу отметить, что перед использованием таблицы, надо выделить под неё память методом SetLength и значение всех ключей установить равным пустой строке. Когда работу с таблицей, необходимо прекратить, то выделенную память желательно освободить.

Это всё что я хотел рассказать по поводу хеш-таблиц. Ваши замечания и предложения, просьба оставлять на форуме. Если что-то не получилось, или возникли какие-то затруднения в процессе написания, то вы всегда можете посмотреть готовый пример, загрузив исходник


Компоненты:   

KOL и MCK

Библиотека для замены VCL. Основное отличие от VCL: компактность получаемых программ. MCK - визуальная часть KOL библиотеки(Вы сможете использовать все удобства RAD технологий от Delphi)

JCL - JEDI библиотека компонентов

Самый крупный и бесплатный пакет компонентов. Большинство из них простое расширение стандартных, но есть и действительно полезные компоненты, упрощающих программирование.

D2XX USB Drivers for Delphi

Этот компонент позволяет отправлять и принимать данные с USB порта. Работа схожа с COM портом

Resource File Unit

Компоненты, который может выдрать ресурсы из уже готовых программ. С его помощью самому очень просто можно создать программу наподобие Resource Hacker или Resource Workshop
Для чтения и записи используются потоки и класс TStream. Это очень удобно для создания редактора для ресурса любого формата. Есть все необходимые методы для получения списка доступных ресурсов и выбора любого из них.
Чтение из файла сделано достаточно эффективно, можно было бы и лучше, но и так чтение/запись происходит достаточно быстро. Есть все необходимые методы для определения типа ресурса и его параметров.
Недостаток - нет примера использования, и приходится разбираться с работой модуля самостоятельно, что отнимет немало времени.

Drag and Drop Component Suite

Чтобы программа соответствовала правилам юзабилити, в ней везде, где только возможно нужно использовать технологию «перетащи и отпусти». Некоторые юзеры пользуются только мышкой и не признают клавиатуру, а в некоторых местах эта технология действительно удобна и необходима.
Поддержка технологии Drag&Drop на уровне стандартов Windows. Это значит, что если вы тащите файл, то его можно тащить не только внутри программы, но и на рабочий стол, в проводник или в любую другую программу. Можно таскать файлы, папки, текст, картинки URL адреса. Поддержка буфера обмена. Автоматический скроллинг окна, над которым тащиться объект. Поддержка операций «Копировать», «Переместить» и «Создать ярлык» при перетаскивании файлов.
Недостатки:
1. Отсутствуют человеческие и понятные иконки для компонентов на палитру компонентов.
2. Сложно разобраться с работой.

Информация о процессоре

Компонент позволяет узнать наиболее полную информацию о процессоре.

JanButtonsV

Набор кнопок различных форм

Красивый ProgressBar

Это компонент для отображения хода выполнения задач с громадным количеством возможностей.
Куча настроек, их даже невозможно перечислить. Полосы могут быть не только горизонтальными, но и вертикальными. Таким образом, легко сделать индикатор громкости или частот звукового файла; Всевозможные градиенты, улучшающие восприятие; Полный исходник с программкой примером; За время тестирования недостатков не выявлено.

Office Assistant

Компонент позволяет добавить в приложение "помощника" как в MS Office
В компоненте есть куча готовых ассистентов и есть программа для создания собственных персонажей. Может отображать рядом с персонажем лампочку, по нажатию которой появляется подсказка дня. У ассистента может быть сколько угодно (я предела не встретил) различных анимаций. Работает быстро и стабильно.
Ассистенты выполнены в виде aal файлов, но реально это файл ресурсов res. Переименуй и редактируй в рестораторе.
Хранение ассистента в ресурсовом файле является и недостатком - твоего ассистента сможет использовать любой другой программист в своих приложениях. Так что советую подумать о защите своих прав.
Отличный компонент и необходим любой большой и сложной шароварной программе. Пользователи не читают документаций и не открывают хелпов, а ассистентом обязательно воспользуются. Подсказки дня в виде отдельных окон - прошлый век. Пора переходить на ассистентов. Только не надо совать ассистента в программы типа "Блокнот". А то представь себе рабочий экран, где 20 окон и 20 ассистентов . Это любого взбесит.
Скриншот 1 Скриншот 2
В дополнение к компоненту есть ряд готовых ассистентов: Ссылка 1, ссылка 2, ссылка 3, ссылка 4, ссылка 5, ссылка 6.
А также редактор персонажей

ACM Components

Одной из самых сложных задач является кодирование звуковых данных и если это делать вручную, то мозги могут сварится. Чтобы не парить программистов, в Microsoft придумали ACM фильтры, с помощью которых можно преобразовывать формат.
Это компоненты, а их использование намного проще. Все необходимое реализовано в качестве методов. В качестве примера показано сжатие данных и передача по сети. Так что если ты не знал, как передаются звуковые данные, качай и учись.
Если хочешь написать программу аудио конференций или просто IP телефон для общения с друзьями по локалке, то компонент можно использовать в качестве отправной точки. Но, не смотря на заточенность компонента под данную задачу, в нем еще не хватает подавления эха. Все, что будет звучать в колонке будет попадать в чувствительный микрофон, поэтому придется использовать узконаправленный микрофон или наушники вместо колонок.


Интересные и полезные сайты по Delphi: Если Вы хотите, чтобы Ваш сайт был в этом разделе пишите.

http://www.noil.pri.ee/ - Здесь вы можете почитать статьи, скачать исходники и компоненты, пообщаться на форуме.


Немного юмора:  :))

Настоящий программист на вопрос:
- " Можешь ли ты это сделать..."
Ответит: "ДА" , а потом подумает как.


65 тыс ошибок - ерунда: 7 лет по 24 патча в день. Win2k


После шатдауна засветилась надпись:
"Питание в вашем доме подготовлено к выключению, удаленный сеpвеp дал подтверждение, можете не выключать свой компьютер, W95 сделает все сама, только отойдите на безопасное
расстояние......9...8......7......6............0


Три главных добродетели программиста - лень, гордыня и нетерпение (c)Ларри Уолл.


Спрашивает девочка у фана:"как перезагрузить компьютер ?"
Ответ:"Ctrl + Shift + Reset".


Если в кране нет воды, удали с винта винды.


Дружественная рассылка:

Рассылки Subscribe.Ru
Программирование на Delphi


Все кто хочет изучить Delphi и реально научиться писать свои программы, ЦПИ "Эверест" поможет Вам.
Всё, что Вам нужно это компьютер и доступ к интернету - для получения уроков.

10 причин в пользу платного обучения в ЦПИ "Эверест"…

1. Когда Вы платите деньги- появляется дополнительный стимул против лени: надо учиться, ведь деньги уже уплачены….
2. Учась платно, получаете удобный для Вас график работы.
3. Весь необходимый справочный материал Вы получите в свое время и на русском языке.
4. Используя интернет в качестве бесплатной библиотеки, Вы получаете все ее минусы:

  • трата времени на поиск необходимого материала (а это потерянные деньги и время). А у Вас есть лишние время и деньги?;
  • отсутствие гарантии, что Вы "осилите" данный материал, ведь пишут его, в основном, не педагоги- профессионалы, а программисты- профессионалы, а они пишут для таких же, как они. А Вы программист- профессионал?
  • отсутствие системности в скачиваемом материале (ведь человек, писавший для Вас материал, не знает, чем Вы владеете). А Вы обладаете системой знаний по Delphi?;

5. Стоимость обучения одного месяца в ЦПИ "Эверест" сравнима с ценой хорошей книги. Но часто ли Вам попадались книги, рассчитанные именно на Вас. Мы же работаем индивидуально.
6. Автор книги или магазин не несет никакой ответственности за то, поняли ли Вы материал или нет, мы же закрепляем за каждым курсантом преподавателя, курирующего Вас.
7. Освоив программирование в Delphi - Вы освоите:

  • основы настоящего программирования- структурного и процедурного программирования ;
  • систему работы с базами данных и SQL- запросами, а это одно из самых перспективных направлений в программировании;
  • язык программирования ObjectPascal, что позволит Вам легко перейти, при желании, на С или Паскаль;
  • работу с компьютерной графикой;
  • при желании - основы низкоуровневого программирования ( Ассемблер).

8. А это значит, что …Мы предлагаем получить "высшее образование" - профессию программиста всего за 1 год и 144 доллара, любой ВУЗ попросит в 3 раза больше за один только семестр.
9. Вы получаете самый практический курс в сети, поскольку теория дается только тогда, когда она действительно необходима…
10. Учиться у нас легко и просто. Весь материал доступен и простым людям, не имеющим никогда дел с программированием….


По всем вопросам обращайтесь ко мне.

Если вы встретили в интернете интересный сайт или статью, да и вообще, что угодно связанное с Delphi, поделитесь ссылкой.
Если можете написать статью связанную с Delphi - присылайте с радостью выложу.
Давайте поможем друг другу!  Архив рассылки.

Предложения, критику и пожелания пишите на e-mail.


Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: comp.soft.prog.delphiinternet
Отписаться
Вспомнить пароль

В избранное