Помощь - Поиск - Пользователи - Календарь
Полная версия: динам. списки. Стандартные операции
Форум «Всё о Паскале» > Pascal, Object Pascal > Задачи
Insomnia
идея задачи в том: что бы произвести со списком различные действия. Проблема в удалении элемента списка по имени. Пробовал различные условия и составление процедуры/ процедур, но что бы полноценно из любого места удалял эл-т не выходит. Как пр: "если ввожу 3 эл-та и удаляю последний - все хорошо. но если следом удаляю 2 то он впринцепи прогоняет и выводит ответ: но вмете с тем и вылетает." . Косяк, я предполагаю, в неккоректных условиях, т.е. выходит за пределы списка.
опишите подробней пожалуйста мои ошибки.

P.S.
остальные условия работают хорошо, но если заметите недочеты , укажите их, пожалуйста.
IUnknown
Цитата
Косяк, я предполагаю, в неккоректных условиях, т.е. выходит за пределы списка.
Правильно предполагаешь:

Цитата
if (p^.mName=dName) then
begin
writeln;
writeln('данные найдены в картотеке.Удаляем');
Q:=p;
p:=Q^.mNext; // <--- Угу...
Q^.mNext:=p^.mNext;
p^.mNext:=nil;
dispose(p);
writeln;
writeln('информация о ',dName ,' удалена');
end

... безоговорочное действие, правда? В P записать Q^.mNext, чего ж проще? А ничего, что этот самый Q^.mNext может быть nil? Значит, и P = nil после отмеченной строки? Стоп, а что ж дальше ты делаешь? Ух ты... Разыменовываешь p... Как интересно, особенно в случае когда p = nil. Программе ничего не остается кроме как вылететь...

На самом деле мне не понятно, во-первых, в чем тут преимущество списка с заглавным звеном. Объяснишь? Вот я лично в этом ничего, кроме неудобства (надо сдвигаться на 1 элемент прежде чем начать работать собственно со списком) не вижу. Зачем надо выделять лишнюю память?

Во-вторых, даже если с заглавным. К чему 2 ветки if then ... else ... ? Что, вот так:
procedure delete_person (var L: PPerson);
var
p, Q : PPerson;
dName: string;
begin
if Empty(L)= true then
writeln('извините, но список пуст')
else
begin
writeln('введите имя удаляемого клиента');
readln(dName);
LowCase(dName); // переводим введенное имя в нижний регистр
p := L;
while Assigned(p^.mNext) do
begin
if p^.mNext^.mName = dName then
begin
Q := p^.mNext;
p^.mNext := Q^.mNext;
dispose(Q);
writeln('информация о ',dName ,' удалена');
end
else
p := p^.mNext;
end;
end;
end;

- это слишком просто? smile.gif Ну, добавить еще проверку, что никто не был найден, и не был удален...

Дальше, по недочетам: удаление заглавного звена (да и вообще списка) где? При выходе из программы FPC выдает сообщение об утечке памяти, именно из-за этого злосчастного звена.

Печать пустого списка. Какое-то сообщение можно написать? Или достаточно молча, ничего не говоря, выйти из процедуры?
Insomnia
вот молотый компот. Как просто-то. Спасибо. очень простая и понятная реализация.

еще вопрос. как мне написать корректный ввод номера телефона ? для имени я сделал: вроде хорошо. В плане рациональности не уверен правда.
я думаю что бы перевести mPhone в string и тогда функция CheckPhone будет аналогична CheckName . В этом случае смогу так же вводить с пробелами (" пр: 12 34 456 " ). Или суш. скрытый от меня более рацио-ый вар-т проверки корректного ввода без глобальных изменений во всей программе ?

P.S. недочеты я поправил почти все, осталось очистку списка сделать, но это не сложно.
IUnknown
Цитата
еще вопрос. как мне написать корректный ввод номера телефона ?
Критерий корректности можно узнать? Если это, чтобы программа не вылетала при вводе "не числа" - то:

{--заполнение списка--}
procedure fill_spisok (var p: PPErson);
var
s, aName : string; aPhone : integer;
i, v : integer;
isOk : boolean;
begin
writeln('сколько персон собираетесь вписать?');
readln(v);
for i:=1 to v do
begin
writeln;
writeln('write a phone');
repeat
readln(s);

// Эта функция описана в SysUtils, подключи ее после Implementation
isOk := TryStrToInt(s, aPhone);
if not isOk then
writeln('Неправильно введен номер. Попробуйте еще раз');
until isOk;

writeln('write a name');
repeat
readln(aName);
LowCase(aName);
isOk := CheckName(aName)
if not isOk then
writeln('неправильно введено имя. Повторите ввод');
until isOk; // Зачем было функцию вызывать дважды?

writeln;
create_spisok(p, aName, aPhone);
end;
end;
прекрасно с этим справляется. Программа будет запрашивать ввод до тех пор, пока то, что пользователь введет не будет правильным целым числом. Можно там же, скажем, запретить отрицательные числа:
isOk := TryStrToInt(s, aPhone) and (aPhone > 0);
, или числа с маленьким количеством цифр (подумай сам как это сделать)...

Теперь о LowCase... На кой надо было писать свою функцию, переводящую строки в нижний регистр (которая работает и с кириллицей), если CheckName определи имя как неправильное при введении кириллицы? Воспользуйся стандартной LowerCase для латиницы (кстати, для кириллицы есть AnsiLowerCase, не надо велосипедов).

Реализация CheckName не очень понятна:
function CheckName(var s: string): boolean;
var
i : integer;
begin
result := false;
for i := 1 to Length(s) do
if not (s[i] in ['a' .. 'z']) then exit;
result: = true;
end;
выглядит привычнее, и не будет идти по строке до упора, как только "поймает" первый неподходящий символ - тут же вернет "ложь"...

Я бы не стал хранить string в структуре TPerson. Если программа будет расширяться, и надо будет сделать сохранение в файл - столкнешься с проблемами, которых можно было бы избежать. String в Дельфи (в отличие от Паскаля) это не 255 char-ов, а указатель на класс. Можно использовать ShortString, она ведет себя подобно строкам Турбо-Паскаля. По той же причине я бы вообще не стал держать вместе информационную (имя и телефон) и вспомогательную (указатель на следующий элемент списка) часть. "Мухи - отдельно, котлеты - отдельно" (С). Если понадобится сохранять - то придется делать это поэлементно (или прибегать к грязным хакам), вместо того чтобы сделать все сохранение в одну строку.

+ ко всему я бы немного по другому реализовал работу со списком: написал бы функцию Search, которая по имени пользователя возвращала бы указатель на элемент списка, предшествующий искомому (или nil, если ничего не было найдено). Используя эту функцию можно будет упростить реализации delete_person и found_phone...
Insomnia
указатель на элемент списка?
если я хочу попросить "что лежит под 3 номером, и он вывести должен имя и телефон данного служащего" .
т.е. мне по сути надо пронумеровать элементы списка?
задать счетчик после каждого нового добавления или в ТPerson завести этот счетчик ?
----
IUnknown
Цитата
если я хочу попросить "что лежит под 3 номером, и он вывести должен имя и телефон данного служащего" .
А с какой, собственно, стати ты должен об этом спрашивать? Что такое "3 номер" вообще? Список - это не массив, в него ведь информация может добавляться произвольно, в любое место. То что сейчас было №3 через секунду может стать уже №4, а в третьем элементе может оказаться то, что раньше было во втором, или чего раньше вообще не было (я уж не говорю о сортировке, которая так перекроит тебе всю нумерацию, что ты потом будешь месяц разбираться, что где, если еще и поле "порядковый номер" сделаешь). Нельзя делать доступ к элементам списка по порядковому номеру. Только по ключу (уникальному ключевому полю). А ключ у тебя - это фамилия. Вот и делай поиск по фамилии. Никакого счетчика.

Цитата
указатель на элемент списка?
Да. Я имел в виду вот это:
function search(const L : PPerson; s : Tname) : PPerson; // Возвращается что? Указатель...
var p : PPerson;
begin
s := LowerCase(s); // переводим введенное имя в нижний регистр
s := AnsiLowerCase(s);

search := nil; // на случай, если ничего не найдется...
p := L;
while p^.mNext <> nil do
begin
if p^.mNext^.mName = s then
begin
search := p; exit;
end
else p := p^.mNext;
end;
end;
, а теперь, вместо того, чтобы "растворять" поиск в программе и размазывать его по нескольким местам, просто...

1) delete_person:
   case n of
1:
begin
writeln('введите имя');
readln(dName);
p := search (L, dName); // <--- !!!
if Assigned (p) then
begin
Q := p^.mNext;
p^.mNext := Q^.mNext;
dispose(Q);
writeln;
writeln('информация о ',dName ,' удалена');
end
else
begin
writeln ('данный служащий не найден в списке');
delete_person(L); // Вот этого бы я не делал. Рекурсии здесь на фиг не надо.
end;
end;


2) found_phone
   writeln('напишите имя нужного вам человека');
readln(fName);
p := search (L, fName);
if Assigned (p) then
begin
// действия при нахождении фамилии в списке
end
else // действия, когда фамилия не найдена

Структура программы упростится.

Хотя, ты делаешь ошибку многих. Я устал говорить об этом: не перемешивай интерфейс и логику программы. Функция delete_person - это функция, которая должна получить на вход список, где будет производиться поиск, и фамилию искомого клиента. И вернуть результат: True, если данные из списка удалены, и False - если произошло что-то внештатное (именно поэтому я обычно возвращаю Integer. Если 0 - то все нормально, другое значение сигнализирует о том, какая именно ошибка произошла). Все остальное, вся вот эта беседа с пользователем, здесь - явно лишнее. Она должна происходить в другом месте программы, перед вызовом delete_person пользователя надо попросить, чтоб он ввел фамилию для удаления, чтоб ввел номер действия (я бы это делал в модуле Interface_1, у него ж и название правильное, это ИМЕННО интерфейс с пользователем, вот тут:

  case n of
1: begin
writeln; // <--- Вот именно тут должна вводиться фамилия для удаления
delete_person(a);
// А тут - должен обрабатываться результат.
// Сама delete_person НИЧЕГО не должна печатать и запрашивать у пользователя
end;

), а не там, где происходит собственно удаление.

То же самое относится и к сортировке: какое отношение имеет
writeln ('воздух сортировать что ли ?')
к сортировке списка? bubble_sort получила несортированный список, после ее выполнения список стал упорядоченным. Всё! Никакой самодеятельности больше. Все остальное к логике не относится. Это - интерфейс пользователя. Хочется похохмить - верни из bubble_sort единицу при пустом списке и 2-ку при списке, состоящем из одного элемента, и сделай:

case bubble_sort(L) of
1 : writeln ('воздух сортировать что ли ?');
2 : writeln ('не будем мусолить одного служащего');
end;
там же, в Interface_1.
Insomnia
спасибо большое за помощь
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.