Страница 9 из 11

Re: Разбор примеров из книги

Добавлено: 24.11.2012 23:11:05
deka47
Извиняюсь, я туда еще не дошел был...

Re: Разбор примеров из книги

Добавлено: 25.11.2012 00:04:42
bormant
Например, так:

Код: Выделить всё

var
  s: string;
  i: integer;
  p: boolean;
begin
  write('Введите слово: '); readln(s);
  p := true;
  for i := length(s) div 2 downto 1 do
    if s[i] <> s[length(s) - i + 1] then begin
      p := false; break;
    end;
  if p then writeln('Слово - палиндром')
  else writeln('Слово - НЕ палиндром');
end.
или даже так:

Код: Выделить всё

function IsPalindrom(s: string): boolean;
var
  i: integer;
begin
  IsPalindrom := true;
  for i := length(s) div 2 downto 1 do
    if s[i] <> s[length(s) - i + 1] then begin
      IsPalindrom := false; break;
    end;
end;

begin
  write('Введите слово: '); readln(s);
  if IsPalindrom(s) then writeln('Слово - палиндром')
  else writeln('Слово - НЕ палиндром');
end.

Re: Разбор примеров из книги

Добавлено: 25.11.2012 00:36:56
deka47

Код: Выделить всё

 if s[i] <> s[length(s) - i - 1] then begin


Вы, наверное, имели ввиду

Код: Выделить всё

if s[i] <> s[length(s) - i + 1] then begin


А теперь вопрос, зачем мы делим строку на 2, мы обрезаем конец строки и средний символ?
Т.е. с слова "потоп" остается "по"? Но я не пойму логики, если так, то, к примеру слово "порок", тоже остается лишь "по", то почему он не выдает как палиндром? Это вопрос к Олегу, к решению задачи, которое предложено в книге.

Re: Разбор примеров из книги

Добавлено: 25.11.2012 00:50:18
Oleg_D
Если количество букв в слове НЕЧЁТНОЕ, то среднюю букву сравнивать саму с собой нет смысла.

Re: Разбор примеров из книги

Добавлено: 25.11.2012 00:55:26
deka47
Oleg_D писал(а):Если количество букв в слове НЕЧЁТНОЕ, то среднюю букву сравнивать саму с собой нет смысла.


Но я не пойму логики, если так, то, к примеру слово "порок", тоже остается лишь "по", то почему он не выдает как палиндром?

Re: Разбор примеров из книги

Добавлено: 25.11.2012 01:06:36
Oleg_D
Потому, что в выражении:

Код: Выделить всё

if arg[i] <> arg[Length(arg)-i+1]
сравниваются первая и последняя буква, вторая и предпоследняя и т.д. в цикле вплоть до средней буквы (но исключая её), если длина нечётная.
Завтра утром вам это станет ясно, как белый день. :D

Re: Разбор примеров из книги

Добавлено: 25.11.2012 01:11:31
deka47
Oleg_D писал(а):Потому, что в выражении:

Код: Выделить всё

if arg[i] <> arg[Length(arg)-i+1]
сравниваются первая и последняя буква, вторая и предпоследняя и т.д. в цикле вплоть до средней буквы (но исключая её), если длина нечётная.
Завтра утром вам это станет ясно, как белый день. :D



Минутку, а разве команда:

Код: Выделить всё

for i:=1 to Length(arg) div 2


Не делит, к примеру, слово с 13 символом на 2 и оставляет только первых 6 символов?
Ну 13 div 2 = 6, 15 div 2 = 7. Остаются же лишь 6/7 первых символов? Также слово "потоп", разделить его на 2, то останется первых два символа "по", и после этой команды

Код: Выделить всё

if arg[i] <> arg[Length(arg)-i+1]

Здесь уже будет браться не полная длина строки (5 символов), а уже урезанная в 2 символа (после div).

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

Re: Разбор примеров из книги

Добавлено: 25.11.2012 05:16:21
Vadim
Подскажите мне, вот сколько вы учились, чтобы понимать паскаль, как вы понимаете сейчас?

Он учился всю жизнь и до сих пор учится. ;)
У каждого время и способ учёбы свои. Равняться тут на кого-либо не стоит.

Re: Разбор примеров из книги

Добавлено: 25.11.2012 08:56:18
Paster Fob
deka47 всё просто.
Вот мой вариант:

Код: Выделить всё

function checkword(var arg:string):boolean;
var i,j:integer;
begin
  checkword:=true;
  j:=0;
  for i:=length(arg) downto 1 do begin
    while j<length(arg) do begin
      inc(j);
      break;
    end;
    if arg[i]<>arg[j] then begin
      checkword:=false;
      break;
    end;
  end;
end;

var s:string;

begin
  writeln('введите слово');
  readln(s);
  if checkword(s) then
    writeln('это слово палиндром')
  else
    writeln('слово не является палиндромом');
  readln;
end.

Re: Разбор примеров из книги

Добавлено: 25.11.2012 11:50:00
Oleg_D
deka47 писал(а):Минутку, а разве команда: for i:=1 to Length(arg) div 2 не делит, к примеру, слово с 13 символом на 2 и оставляет только первых 6 символов?

Нет, не делит. Здесь строка никак не затрагивается. Делится одно число -- длина строки -- на другое число, и получается предел для цикла.
deka47 писал(а):У меня к вам еще один личный вопрос, вот я застреваю на таких задачках, с меня что-то будет? Потому-что я решил поступить на программирование, но не знаю получится ли что-то. Кажется что я туплю в каждой задаче и достаю вас здесь... Подскажите мне, вот сколько вы учились, чтобы понимать паскаль, как вы понимаете сейчас?

Vadim писал(а):Он учился всю жизнь и до сих пор учится.

Это точно. :D У каждого свой путь, кто-то тупит, тупит, а потом прорывает его, горизонт проясняется. Что тут посоветовать? Настойчивость и упорство -- хорошо. Но порой лучше не упираться рогом в одну задачу или вопрос, пройти немного вперёд, через 3-4 дня вернуться и вновь почитать. Обычно это помогает. И ещё экспериментировать надо, пробовать разные варианты решений. По моим ощущениям "песни" можно освоить за 6-12 месяцев. Когда я учился, никаких книг по Паскалю не было, пользовались скупыми техническими руководствами.
Paster Fob писал(а):

Код: Выделить всё

    while j<length(arg) do begin
      inc(j);
      break;
    end;

Мудро накручено, не сразу понял. :D
Проще так:

Код: Выделить всё

   if j<length(arg) then Inc(j);
А ещё проще:

Код: Выделить всё

   Inc(j);
поскольку IF тоже лишний. К тому же в вашем решении цикл вдвое длиннее. Ну, опыт -- дело наживное. :)

Re: Разбор примеров из книги

Добавлено: 25.11.2012 11:54:19
bormant
deka47 писал(а):

Код: Выделить всё

 if s[i] <> s[length(s) - i - 1] then begin

Вы, наверное, имели ввиду

Код: Выделить всё

if s[i] <> s[length(s) - i + 1] then begin

абсолютно верно.
deka47 писал(а):А теперь вопрос, зачем мы делим строку на 2, мы обрезаем конец строки и средний символ?
Т.е. с слова "потоп" остается "по"? Но я не пойму логики, если так, то, к примеру слово "порок", тоже остается лишь "по", то почему он не выдает как палиндром? Это вопрос к Олегу, к решению задачи, которое предложено в книге.
Мы с самой строкой ничего не делаем. length(s) возвратит нам число -- длину строки, пусть n. Затем, мы будем проверять условие палиндромности исходя из его определения, сравнивая 1-ю букву с n-ной, 2-ю с (n-1) и т. д. То есть, сравниваем [i]-ю с [n-i+1]-ой. Как только найдем неравенство, условие палиндромности нарушено и можно завершать проверку. Перебрав индексы от 1 до (n div 2) заметим, что дальше продолжать нет смысла: при четном количестве одна половина уже полностью была сравнена с другой, а при нечетном -- остался один один символ, который сравнивать с самим собой смысла не имеет. Но если продолжить до конца -- на результат это не повлияет, просто работа будет проделана дважды: [1] с [n] и [n] с [1] и т.д.
Еще один мрмент: если прямой цикл без дублирования: от 1 до n div 2 (или наоборот), то обратный будет от n до (n+1) div 2 (или наоборот), например:
для 6: 1-3, 6-3
для 7: 1-3, 7-4
...

Re: Разбор примеров из книги

Добавлено: 25.11.2012 12:41:38
Paster Fob
Oleg_D писал(а):
Paster Fob писал(а):

Код: Выделить всё

    while j<length(arg) do begin
      inc(j);
      break;
    end;

Мудро накручено, не сразу понял. :D
Проще так:

Код: Выделить всё

   if j<length(arg) then Inc(j);
А ещё проще:

Код: Выделить всё

   Inc(j);
поскольку IF тоже лишний. К тому же в вашем решении цикл вдвое длиннее. Ну, опыт -- дело наживное. :)


М-да,поторопился.Вот исправленный вариант.

Код: Выделить всё

function checkword(var arg:string):boolean;
var i,j:integer;
begin
  checkword:=true;
  j:=0;
  for i:=length(arg) downto length(arg) div 2 do begin
    inc(j);
  if arg[i]<>arg[j] then begin
      checkword:=false;
      break;
    end;
  end;
end;

var s:string;

begin
  writeln('введите слово');
  readln(s);
  if checkword(s) then
    writeln('это слово палиндром')
  else
    writeln('слово не является палиндромом');
  readln;
end.

Re: Разбор примеров из книги

Добавлено: 25.11.2012 14:05:42
bormant
Paster Fob,
я переписал бы так:

Код: Выделить всё

function checkword(const arg: string): boolean;
var i, j: integer;
begin
  checkword:=true;
  j := (length(arg) + 1) div 2 + 1;
  for i := length(arg) div 2 downto 1 do begin
    if arg[i]<>arg[j] then begin
      checkword:=false; break;
    end;
    inc(j);
  end;
end;
3 отличия:
1) список параметров: переменная arg внутри функции не меняется, поэтому, если компилятор поддерживает const в списке параметров, то нет повода использовать var;
2) в цикле "for i := выражение1 to/downto выражение2 do" выражение1 вычисляется единожды, а выражение2 -- на каждой итерации цикла (если только оптимизатор не умеет выводить его независимость от тела цикла). В данном примере это несущественно, для коротких строк length(s) -- это всего лишь byte(s[0]), но вот операция деления, пусть и целочисленного, намного более ресурсоёмка.
3) в предварительном выполнении inc(j) мало смысла, начальному значению j вне цикла приходится давать значение заведо уменьшенное на 1, а это не способствует лёгкости понимания кода.

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

Добавлено спустя 15 минут 36 секунд:
Но мне более нравится другой вариант, вообще без делений и каких бы то ни было расчётов, просто идём навстречу с разных концов слова, сравнивая символы. Если встретились, то палиндром:

Код: Выделить всё

function IsPalindrom(s: string): boolean;
var
  i, j: integer;
begin
  i := 1; j := length(s);
  while (i < j) and (s[i] = s[j]) do begin
    inc(i); dec(j);
  end;
  IsPalindrom := i >= j;
end;


Добавлено спустя 28 минут 33 секунды:
deka47 писал(а):как бы сделать так, чтобы "запоминались" все местоположения?
Например, если речь идёт о показе отличающихся символов:

Код: Выделить всё

procedure ShowPalDiff(const s: string; MarkSame, MarkDiff: char);
var
  i, j: integer;
  p: boolean;
begin
  writeln(s);
  j := length(s); p := true;
  for i := 1 to length(s) do begin
    if s[i] = s[j] then write(MarkSame)
    else begin
      write(MarkDiff); p := false;
    end;
    dec(j);
  end;
  if p then writeln(^M'Это палиндром.', '':length(s)-14)
  else begin
    writeln; writeln('Это НЕ палиндром.');
  end;
end;

var
  s: string;
begin
  write('Введите слово: '); readln(s);
  ShowPalDiff(s, '_', '^');
end.
И прогоны:

Код: Выделить всё

Введите слово: pascal
pascal
^_^^_^
Это НЕ палиндром.

Введите слово: potop
potop
Это палиндром.

Re: Разбор примеров из книги

Добавлено: 25.11.2012 17:54:54
deka47
Разобрался со всеми вариантами задачи, с inc вообще гениально, как по мне! Спасибо за помощь.

bormant писал(а):Перебрав индексы от 1 до (n div 2) заметим, что дальше продолжать нет смысла: при четном количестве одна половина уже полностью была сравнена с другой, а при нечетном -- остался один один символ, который сравнивать с самим собой смысла не имеет.


Т.е. мы отсекаем первую часть "строки". Мы сравнивамем 1 с последним, 2 с последним минус 1, 3 с последним минус 2. А потом уже нету смысла сравнивать последний минус 1 с вторым? Выходит слово "потоп", мы оставляем "оп" и сравниваем: "п"="п", "о"="о", верно? Буква "т" уже не затрагивается, потому что нету разницы какая она. Я верно понял?

Re: Разбор примеров из книги

Добавлено: 25.11.2012 18:58:35
bormant
deka47 писал(а):Т.е. мы отсекаем первую часть "строки". Мы сравнивамем 1 с последним, 2 с последним минус 1, 3 с последним минус 2. А потом уже нету смысла сравнивать последний минус 1 с вторым? Выходит слово "потоп", мы оставляем "оп" и сравниваем: "п"="п", "о"="о", верно? Буква "т" уже не затрагивается, потому что нету разницы какая она. Я верно понял?
Да. Только "отсекаем" -- это в переносном смысле, строку мы никак не модифицируем, только берём из неё символы по индексу. Дело в чём, если мы в качестве инструмента выбрали по каким-то причинам цикл for, мы должны указать начальное и конечное значения счётчика цикла. Счётчиком нам достаточно пробежать только половину, посколько вторую половину мы будем получать "зеркально" от противоположного конца строки.

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