Помощь - Поиск - Пользователи - Календарь
Полная версия: хеш-таблица
Форум «Всё о Паскале» > Pascal, Object Pascal > Задачи
*оля*
почему-то если хочешь записать повторно элемент, то не работает, хотя должен выводить сообщение, что такой элемент уже записан. И задача на наличие элемента, связанная с этой процедурой(MAKENULL), тоже не работает.
помогите пожалуйста найти ошибку.


 
uses Crt;
const  B = 30; 
type  
celltype = record 
element: string[255];
next: ^celltype
end; 
TABL = array[0..B-1]  of  ^celltype;

var
End_Menu: boolean;
ch: char;
x: string[255];
A: TABL;

Function  h ( x: string ): 0..B-1;
var   i, sum: integer; 
begin 
sum:= 0; 
for  i:= 1  to  length(x)  do 
sum:= sum + ord( x[i] ); 
h:= sum  mod  B ;
end; 				{ h } 


procedure  MAKENULL ( var A: TABL ); 
var   i: integer; 
begin 
for  i:= 0  to  B - 1  do 
A[i]:= nil 
end; 			


function  MEMBER ( x: string; var A: TABL ): boolean; 
var   current: ^celltype; 
begin 
current:= A[h(x)]; 
{ начальное значение current равно заголовку сегмента, 
которому принадлежит элемент х } 
while  current <> nil  do 
if  current^.element = x   then  MEMBER:=true 
else  current:= current^.next; 
MEMBER:=false		 { элемент х не найден } 
end; 			

procedure  INSERT ( x: string; var A: TABL ); 
var   bucket: integer; 	{ для номера сегмента } 
oldheader: ^celltype; 
begin 
if  not MEMBER(x, A)  then  begin 
bucket:= h(x); 
oldheader:= A[bucket]; 
new( A[bucket] ); 
A[bucket] ^.element:= x; 
A[bucket] ^.next:= oldheader ;
writeln('элемент добавлен');
end 
else writeln ('такой элемент уже записан');
end; 			


procedure  DELETE ( x: string; var A: TABL ); 
var  bucket: integer; 	current: ^celltype;  f: boolean;
begin 
bucket:= h(x);   f:= true;
if  A[bucket] <> nil  then  begin 
if A[bucket] ^.element = x  then 	{ x в первой ячейке } 
A[bucket]:= A[bucket] ^.next 	{ удаление х из списка } 
else   begin 	{ x находится не в первой ячейке } 
current:= A[bucket];  
{ current указывает на предыдущую ячейку } 
while  (current^.next <> nil ) and  f  do 
	if  current^.next^.element = x  then  begin 
		current^.next := current^.next^.next;  
		{ удаление х из списка } 
		f:= false { останов } 	end 
	else   { x пока не найден }  current:= current^.next 
end 
end 
end; 			


procedure PRINT;
var
i: integer;
current: ^celltype;
begin
for i:=0 to B do
begin
writeln;
write (i,':');
if A[i]<>nil then current:=A[i];
while current<>nil do
begin
write(current^.element, '  ');
current:=current^.next;
end;
end;
end;

Procedure Menu_1;
begin
clrscr;
End_Menu:=False;
repeat
writeln;
writeln;
Writeln('***********************************************');
writeln( '*****************Главное меню******************');
Writeln('***********************************************');
writeln('                                               ');
writeln('         выберите вид работы:                  ');
writeln('        0- вставка элемента                    ');
writeln('        1- Проверка на существование элемента  ');
writeln('        2- удаление элемента                   ');
writeln('        3- просмотреть все элементы            ');
writeln('        4- выход                              ');
writeln('***********************************************');

readln(ch);
Case ch of
'0': begin
writeln('введите эелемент для записи');
readln(x);
INSERT ( x, A ); 

end;
'1':
begin
writeln('введите элемент для поиска');
readln(x);
if  MEMBER ( x, A ) = true then writeln (' элемент существует')
else writeln('элемент не существует');
end;
'2': begin
writeln('введите элемент для удаления');
readln(x);
DELETE ( x, A ); 
writeln('элемент удален');
   end;
'3': begin   Print;  end;
'4': begin End_menu:=true;clrscr; Writeln('работа завершена, закройте программу!'); end;
end;
until End_Menu;
clrscr;
end;

BEGIN

MAKENULL ( A);
Menu_1;
end.

 
.
volvo
*оля*

Во-первых ,у меня к тебе вопрос: чем ЭТО компилируется? Просто чтоб знать...

А во-вторых - в чем ошибка. Вот в этом:
Цитата
function  MEMBER ( x: string; var A: TABL ): boolean; 
var   current: ^celltype; 
begin 
current:= A[h(x)]; 
{ начальное значение current равно заголовку сегмента, 
которому принадлежит элемент х } 
while  current <> nil  do 
if  current^.element = x   then  MEMBER:=true 
else  current:= current^.next; 
MEMBER:=false		 { элемент х не найден } 
end;
Ну, и назови мне причину по которой это ВООБЩЕ когда-нибудь вернет что-нибудь, кроме False. Последняя строка функции выполнится в любом случае. Или перенеси MEMBER := False в самое начало функции, или выходи из нее принудительно, как только сделала присвоение MEMBER := True...

P.S. Не, первый вариант хуже, пользуйся вторым. Зачем бежать дальше по таблице, если уже известно, что X там присутствует...
*оля*
ой, да, точно, последняя строчка всегда выполняется, спасибо большое!

хм, правда я исправила, теперь сама по себе она работает, но почему-то все равно сообщение не выдает, а просто выходит из программы.

  if  not  MEMBER (x, A ) then writeln (' элемент не существует')
else writeln('элемент  существует');
.

можно так писать?

и после выполнения процедуры просмотра тоже программа закрывается(((

p.s: PascalABC.net
volvo
Цитата
p.s: PascalABC.net
С этого надо было начинать...

PascalABC.NET неправильно обрабатывает цикл
  while  current <> nil  do 
    if  current^.element = x   then  MEMBER:=true 
    else  current:= current^.next;
, в результате этот цикл становится вечным (можешь воспользоваться отладчиком и прогнать пошагово этот кусок программы). Причину пока не понял. Как разберусь - напишу.

Добавлено через 5 мин.
Блин... Что-то я торможу уже, спать надо больше, наверное. Он просто неправильно записан. Вот так нужно делать:
  while  current <> nil  do
  begin
    if  current^.element = x   then  MEMBER:=true;
    current:= current^.next;
  end;


А все знаешь почему? smile.gif Ты откуда этот алгоритм переписывала? Из Ахо? smile.gif Там же return используется, то есть, как только current^.element = x, функция сразу заканчивается. А в твоем варианте - она просто бегает по циклу вхолостую: если элемент else не отрабатывает, с этим же значением current идет новая итерация, соответственно опять ничего не меняется. Все, зациклились...
*оля*
что-то из лекций писала, что-то так) видимо, что так писала, там и ошибки)

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

единственное, что я заметила, что не хватает dispose, а ошибку не могу найти.
заранее спасибо)
volvo
Ну, смотри, у тебя (по сравнению с авторским алгоритмом) по-другому реализована процедура Delete. У Ахо она вот такая:

procedure Delete(x: string; var A: TABL);
var
  bucket: integer;
  current: ^celltype;
begin
  bucket:= h(x);
  if A[bucket] <> nil then
  begin
    if A[bucket]^.element = x then { x в первой ячейке }
      A[bucket] := A[bucket] ^.next { удаление х из списка }
    else
    begin { x находится не в первой ячейке }
      current := A[bucket];
      { current указывает на предыдущую ячейку }
      while current^.next <> nil do
        if current^.next^.element = x then
        begin
          current^.next := current^.next^.next; { удаление х из списка }
          exit; { останов - выходим из процедуры }
        end
        else current:= current^.next; { x пока не найден }
    end; { else }
  end; { if <> nil }
end; { Delete }
Вроде бы у тебя с F логика та же самая, но мало ли smile.gif

Во-вторых, у тебя будет вылет за границы массива в процедуре Print:
Цитата
for i:=0 to B do
begin
Почему до B, когда тип описан как TABL = array[0..B-1] of ^celltype; ?

Добавлено через 1 мин.
Цитата
единственное, что я заметила, что не хватает dispose
PascalABC.NET со сборщиком мусора, так что не особо критично.
*оля*
Цитата(volvo @ 15.11.2010 17:51) *


Во-вторых, у тебя будет вылет за границы массива в процедуре Print:
Почему до B, когда тип описан как TABL = array[0..B-1] of ^celltype; ?


да, это исправила уже, только забыла отредактировать сообщение, но все равно спасибо.

а насчет той ошибки, исправила, но все равно, работает, работает, а потом перестает. обычно перестает работать, если я ввожу элементы, чтобы в строчке было больше 2х, потом удаляю несколько, потом ввожу один снова, потом снова удаляю....и так еще раз 10...и все, не работает(
TarasBer
А где в процедуре Delete вызов Dispose?

> if MEMBER ( x, A ) = true then

Не надо так писать, ты травмируешь этим мою психику, пиши

if MEMBER ( x, A ) then

Добавлено через 1 мин.
Или в ABC.NET отменили Dispose? Типа само уберётся. Тогда это хреновая среда для обучения.

Добавлено через 12 мин.
Переписал на D7 (исправив озвученные в этой теме ошибки), 10 раз добавил и удалил слово abacaba, всё работает.
*оля*
ну так работает, да.
но преподаватель ее всегда минут 10 мучает и в конце концов она перестает работать.
TarasBer
Наверное, из-за отсутствия Dispose память кончается, не знаю.
Я-то переписал удаление так:




procedure  DELETE ( x: string; var A: TABL ); 
var
  bucket: integer;
  current, tmp: pcelltype;
  f: boolean;
begin
  bucket:= h(x);
  f:= true;
  if  A[bucket] <> nil  then  begin
    if A[bucket] ^.element = x  then 	{ x в первой ячейке }
      A[bucket]:= A[bucket] ^.next 	{ удаление х из списка }
    else   begin 	{ x находится не в первой ячейке }
      current:= A[bucket];
      { current указывает на предыдущую ячейку }
      while  (current^.next <> nil ) and  f  do
        if  current^.next^.element = x  then  begin
          tmp := Current^.Next;
          current^.next := current^.next^.next;
          Dispose(tmp);
          { удаление х из списка }
          f:= false { останов }
        end else   { x пока не найден }
          current:= current^.next
    end
  end
end; 		
	



(И вообще, надо не 10 минут руками программу мучать, если уж на то пошло, а автоматически тестировать.)
Упс, русские буквы не так скопировались.
volvo
Цитата
обычно перестает работать, если я ввожу элементы, чтобы в строчке было больше 2х, потом удаляю несколько, потом ввожу один снова, потом снова удаляю....и так еще раз 10...и все, не работает(
Лог программы (что и в каком порядке вводишь, чтобы воспроизвести ошибку) - в студию.
*оля*
например так:
ввод: 20 словНажмите для просмотра прикрепленного файла
далее удаляю слова: ролл, рор, пп (т.е. те, что в одной строчке) и получаю что-то непонятное: Нажмите для просмотра прикрепленного файла
volvo
Цитата
далее удаляю слова: ролл, рор, пп (т.е. те, что в одной строчке) и
все, как и должно быть, все слова из этой строки один за одним удаляются без проблем, сбоев не наблюдаю. Может, ты чего-то там еще изменяла? Присоединила бы исходник полностью (в виде PAS-файла)
Гость
Напиши тело программы вот так:


BEGIN

assign(input, 'hash.txt');
reset(input);

MAKENULL ( A);
Menu_1;
close(input);
end.


Создай файл hash.txt, напиши в нём что-то такое:

Код

0
abc
0
acb
0
bca
0
bac
0
aaa
0
abb
0
bab
2
bac
2
bac
2
bac
3
3
3
3
3
3
3
3
3
4

Найди, какое содержимое файла вызывает глюки и пришли его сюда.
*оля*
Цитата(volvo @ 15.11.2010 19:57) *

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



там почти то же самое, что в первом сообщении, только названия переменных другие)

Нажмите для просмотра прикрепленного файла
Гость
Ну так что с автоматическим тестом?
Я вот написал, как это можно организовать.


Кстати, я забыл, надо ещё вместо

if A[bucket] ^.element = x  then 	{ x в первой ячейке }
      A[bucket]:= A[bucket] ^.next 	{ удаление х из списка }



написать


if A[bucket] ^.element = x  then begin 	{ x в первой ячейке }
      tmp := A[bucket];
      A[bucket]:= A[bucket] ^.next 	{ удаление х из списка }
      Dispose(tmp);
end

dron4ik
У мя есть нормальная хеш таблица...могу скинуть.

Добавлено через 1 мин.
посмотри там их даже несколько..
*оля*
Цитата(Гость @ 15.11.2010 21:49) *


Кстати, я забыл, надо ещё вместо

if A[bucket] ^.element = x  then 	{ x в первой ячейке }
      A[bucket]:= A[bucket] ^.next 	{ удаление х из списка }



написать


if A[bucket] ^.element = x  then begin 	{ x в первой ячейке }
      tmp := A[bucket];
      A[bucket]:= A[bucket] ^.next 	{ удаление х из списка }
      Dispose(tmp);
end





в прикрепленном файле это уже исправили)


dron4ik, спасибо, правда без исходного текста мне эти программы не помогут.
Хотелось бы найти ошибку))
dron4ik
там же есть все исходники в прикрепленном файле blink.gif или ты все таки хочешь сама сделать своё?)
Гость
Интереснее (и полезнее) врубиться, где ошибка в старых исходниках, вместо того, чтобы новые писать. Охота за ошибками весьма увлекательна, зачем лишать себя такого интересного занятия?
*оля*
да, хотелось бы эту программу доделать по возможности. в общем пыталась и так переписать:


procedure  Udalenie ( x: string; var A: TABL ); 
var  nomerSegm: integer; 	ukaz,ud: ^zapis;  
begin 
nomerSegm:= h(x);   
if  A[nomerSegm] <> nil  then  begin 
if A[nomerSegm] ^.element = x  then 	 
A[nomerSegm]:= A[nomerSegm] ^.next 	  
else   begin 	
ukaz:= A[nomerSegm];
ud:=ukaz^.next;  
while  (ukaz^.next <> nil )  do 
	if  ud^.element = x  then 
	begin
		 ukaz^.next := ud^.next; 
		 dispose(ud); exit;
	end
	else   begin 
	    ukaz:= ukaz^.next ;
	    ud:=ud^.next; 
	       end;
end 
end 
end; 			


.

и все равно не то(((

Добавлено через 9 мин.
если при ошибке выводит в строчке слова ненужные: "элемент добавлен, такой элемент уже записан...."
так может это не в процедуре удаления, а в процедуре добавления ошибка?
volvo
Ну вот не могу я заставить программу вылетать, и все тут... до 200 слов вводил, удалял по 15 раз каждую из строк хеш-таблицы, начинал заполнять снова, опять удалял, НЕ ВЫЛЕТАЕТ. Запускал и из оболочки, и "Без связи с оболочкой".

В самой программе тоже есть несколько моментов, которые я бы исправил:
1) у тебя в одном месте тип данных описан как string[255], а в другом - как string. Если в Турбо Паскале это одно и то же, то в PascalABC.NET это совсем не так. Поменяй везде на shortstring. Несоответствие типов может тоже привести к проблеме.
2) сбой может быть связан с тем, что в процедуре Print значение Current не всегда определено. В частности, при обработке первой строки таблицы, там может оказаться любой мусор, из-за чего программа может и вылететь (сама переменная Current описана локально, значит - не инициализируется nil-ом)
3) как всегда - мое любимое изменение в программе: переменные должны иметь минимальное время жизни, то есть, описывать их желательно как можно ниже по тексту. Поэтому я бы в Print тоже передавал массив, а описание самого массива A перенес бы ниже.
4) ну, и косметическое изменение цикла Repeat/Until в Menu_1, избавляемся от лишней переменной.

В общем, под спойлером - моя программа, которую я гонял больше 4-х часов (удаление делать не стал, попробуй погонять эту программу, НИЧЕГО в ней не изменяя, ни единого символа). Сбой воспроизвести не удалось ни при каких условиях. Приводи ТОЧНУЮ ПОСЛЕДОВАТЕЛЬНОСТЬ действий для того, чтоб увидеть, наконец, этот сбой.

Спойлер (Показать/Скрыть)
*оля*
поступаю так же,как и в тот раз: ввожу элементы. Если использовать Crt, то картинка получается такая же, как и в тот раз Нажмите для просмотра прикрепленного файла

ну а если не менять ни одного символа, то в окне вывода получается следующее: пока программа работает хорошо, только ввожу элементы и после каждого нажимаю просмотр

 
0:ррр 
1:
2:
3:
4:ороро 
5:
6:оро 
7:
8:ро 
9:

 


далее ввожу еще один элемент "про" и таблица сбивается:

 

0:ррр 
1:
2:
3:
4:ороро 
5:
6:оро 
7:про 
8:㳮￿9:



в общем, если дальше вводить, он две в одну строчку почему-то вводит:

8:ролл 㳮￿9:пр

всегда с 8ой и 9ой строчкой проблемы.
volvo
Издеваешься? Я просил ТОЧНУЮ последовательность!!! Точную, понимаешь? В каком порядке ты вводишь все эти элементы? При вводе в произвольном порядке (картинка - такая же, как и у тебя вверху) ввожу "про" - сбоя НЕТ!!! Этот элемент корректно заносится в 7 строку таблицы... Что я делаю не так? В общем, давай закончим на этом обсуждать мифологию, надоело. Уточняй номер сборки PascalABC.NET, какой у тебя установлен .NET, собственно, и ТОЧНУЮ ПОСЛЕДОВАТЕЛЬНОСТЬ действий - в студию. Пока всего вышеперечисленного не будет - я больше здесь не отвечаю.
TarasBer
Так что насчёт автоматического тестирования (см пост 15)?
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.