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

Книга адресована школьникам средних и старших классов, желающим испытать себя в «олимпийских схватках». Может быть полезна студентам-первокурсникам и преподавателям информатики.

Модераторы: Oleg_D, Модераторы

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

Сообщение deka47 » 25.11.2012 22:48:02

Блин, вот я дурак.

Это же мы в цикле for просто делаем предельное число в двое меньше. А не режим саму строку, значение строки не меняется. Я уже понял, спасибо, теперь спокоен!
deka47
новенький
 
Сообщения: 33
Зарегистрирован: 07.10.2012 22:43:26

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

Сообщение deka47 » 28.11.2012 02:35:23

Опять к вам:
Код: Выделить всё
function Datas: string;
var h, w, s: string;
    k, i, j: integer;
begin
read (FileIN, s);
k:=0;

repeat
for i:=1 to length (s) do
    if s[i]=' ' then begin k:=k+1; j:=i;
       if k=3 then break;
end;
until k=3;

h:='';
for j:=i to length(s) do h:=h+s[i];

datas:=h;
end;



begin
assign (FileIN, 'C:\Users\IN.txt');
reset (FileIN);
while not eof (FileIN) do begin
writeln(datas);  readln(filein);
end;
close (FileIN);
end.


В двух словах, придумал сам себе программу, есть n слов, нужно вывести все после первых трех (скажем есть 5 слов, нужно 2 последнии), слова написаны через пробел и не одна строка в файле, а несколько.
В рипит: я проганяю строку, если нахожу пробел, то inc(k) и присваиваю j значение i на месте пробела. Когда будет k=3 заканчиваю цикл.
И перехожу к for-to-do там от места окончание (на каком i закончилась предыдущая строка), я от того места считываю символы и записываю в h.
У меня что-то не рабобает. Возможно, код кривой, долгий, тупой, но пожалуйста, поправьте его на моей основе. Если можете то сделайте более простой, чтобы я разобрал его.
deka47
новенький
 
Сообщения: 33
Зарегистрирован: 07.10.2012 22:43:26

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

Сообщение bormant » 28.11.2012 10:09:31

deka47 писал(а):придумал сам себе программу, есть n слов, нужно вывести все после первых трех (скажем есть 5 слов, нужно 2 последнии), слова написаны через пробел и не одна строка в файле, а несколько.

Как вариант:
Код: Выделить всё
const
  words_to_skip = 3;
var
  s: string;
  i, w: integer;
begin
  w := 0; { счётчик слов }
  writeln('Введите текст, для завершения в Linux Ctrl+D, в Windows/DOS - Ctrl+Z:');
  while not eof do begin
    readln(s);
    if w < words_to_skip then begin { выделяем слова, разделитель - пробел }
      i := 1;
      while (w < words_to_skip) and (i <= length(s)) do begin
        while (i <= length(s)) and (s[i] = ' ') do inc(i); { пропустим пробелы }
        if i <= length(s) then begin { если строка не кончилась, мы в начале слова ... }
          while (i <= length(s)) and (s[i] <> ' ') do inc(i); { ... найдём конец слова ... }
          inc(w); { ... и увеличим счётчик слов }
        end;
        if w = words_to_skip then begin { все слова найдены ... }
          { ... пропустим пробелы и выведем остаток строки }
          while (i <= length(s)) and (s[i] = ' ') do inc(i);
          if i <= length(s) then writeln('> ', copy(s, i, length(s)));
        end;
      end;
    end else { выводим введённое как есть }
      writeln('> ', s);
  end;
end.
Текст можно вводить с клавиатуры, можно подать содержимое файла на вход через перенаправление ввода ("<"), запустив в командной строке что-то вроде:
Код: Выделить всё
> имя_откомпилированной_программы < имя_файла_с_данными
Или дописать соответствующие assign()/reset() в начале и close() в конце.

Добавлено спустя 12 минут 15 секунд:
deka47 писал(а):
Код: Выделить всё
k := 0;
repeat
  for i := 1 to length(s) do
    if s[i] = ' ' then begin
      k := k + 1;
      j := i;
      if k = 3 then break;
    end;
until k = 3;


Что будет, если в строке s нет 3-х пробелов? Правильно, k не достигнет значения 3. Когда в этом случае завершится цикл repeat until k = 3; ?
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

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

Сообщение Paster Fob » 17.12.2012 17:36:59

Что-то не могу понять смысл задачи в главе 48 раздел "А слабо" б):
...Напишите функции для циклического сдвига слова влево и вправо...

Это как сделать? Слово ведь состоит из байтов,1 символ = 1 байт.Нужно сделать циклический сдвиг каждого байта в слове или как?
Аватара пользователя
Paster Fob
постоялец
 
Сообщения: 188
Зарегистрирован: 22.02.2011 21:53:36
Откуда: Новосибирск.

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

Сообщение Oleg_D » 17.12.2012 18:45:32

Разница будет только в числе циклов: 8 - для байта, 16 - для Word, 32 - для Longint.
Oleg_D
постоялец
 
Сообщения: 390
Зарегистрирован: 09.05.2011 11:28:36

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

Сообщение Paster Fob » 17.12.2012 20:54:41

Oleg_D писал(а):Разница будет только в числе циклов: 8 - для байта, 16 - для Word, 32 - для Longint.


С числами всё ясно.Но со словом возникают непонятки.например я вожу слово "да" . получаем 2 байта без учёта нулевого.
10100100 10100000 ,сдвигаем влево ==> 01001001 01000001 и получаем сдово "IA" .Или нужно сдвигать каждый байт отдельно?
10100100 10100000 ,сдвигаем влево ==> 01001001 01000001 правда в данном примере результат такой же как и в первом.Так что ли?
Или вы всё-таки вы имели ввиду число,а не слово?
Аватара пользователя
Paster Fob
постоялец
 
Сообщения: 188
Зарегистрирован: 22.02.2011 21:53:36
Откуда: Новосибирск.

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

Сообщение Oleg_D » 17.12.2012 22:12:26

Ой, извините, я сначала не понял вопроса.
Под словом программисты обычно понимают некое машинное слово, которое, в зависимости от разрядности процессора, составляет 1, 2, 4 или более байтов.
Другими словами это числа, а не строки символов. См. также стр. 219, там я сказал об этом.
Oleg_D
постоялец
 
Сообщения: 390
Зарегистрирован: 09.05.2011 11:28:36

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

Сообщение Paster Fob » 18.12.2012 10:51:48

Вот что у меня получилось.Написал для типа byte.
Код: Выделить всё
function testbit(arg,bit:byte):boolean;
begin
  testbit:=(arg and (1 shl bit))<>0;
end;

function writenum(arg:byte):string;
var
  s:string;i:byte;
  c:char;
begin
  s:='';
  for i:=1 to 8 do begin
    c:=char ((arg mod 2)+ord('0'));
    s:=c+s;
    arg:=arg div 2;
  end;
  writenum:=s;
end;

procedure rotatebyte(var n:byte;c:char);
var flag:boolean;
begin
  case c of
    'l' : begin
            flag:=testbit(n,7);
            {$R-}
            n:=n shl 1;
            {$R+}
            if flag then
              n:=1 or n;
            end;
    'r' : begin
            flag:=testbit(n,0);
            n:=n shr 1;
            if flag then
              n:=128 or n;
            end;
    end;
end;

var
  num:byte;
  ch:char;

begin
  writeln('введите число от 0 до 255');
  readln(num);
  if not (num in [0..255]) then
    writeln('не корректный ввод');
  writeln(writenum(num));
  writeln('сдвиг влево или вправо l/r ? ');
  repeat
    readln(ch);
    if  not (ch in ['l','r']) then
      writeln('не корректный ввод');
  until ch in ['l','r'];
  rotatebyte(num,ch);
  writeln(writenum(num));
  readln
end.


Почему пришлось для сдвига влево отключать проверку диапозонов иначе если число больше 127 ,то runtime error 201?
Аватара пользователя
Paster Fob
постоялец
 
Сообщения: 188
Зарегистрирован: 22.02.2011 21:53:36
Откуда: Новосибирск.

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

Сообщение Oleg_D » 18.12.2012 11:08:32

Не запускал, но глазами проверил, всё нормально вроде бы.
А отключать проверку диапазонов надо потому, что при сдвиге влево числа, содержащего 1 в старшем бите, как раз и происходит такое "нарушение". Но в данном случае мы понимаем причину и допускаем это "нарушение" сознательно.
Oleg_D
постоялец
 
Сообщения: 390
Зарегистрирован: 09.05.2011 11:28:36

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

Сообщение bormant » 18.12.2012 17:42:13

Можно много короче:
Код: Выделить всё
function rotatebyte(n: byte; right: boolean): byte;
begin
  if right
  then rotatebyte := (n shr 1) or (n shl 7)
  else rotatebyte := (n shl 1) or (n shr 7);
end;

function byte2bin(b: byte): string;
var
  i: integer;
  s: string[8];
begin
  s := '';
  for i := 7 downto 0 do begin
    s := chr(ord('0') + b and 1) + s;
    b := b shr 1;
  end;
  byte2bin := s;
end;

var
  number: byte;
  direction: char;
begin
  write('Введите число [0..255]: '); readln(number);
  repeat
    write('Введите направление сдвига (l/r): '); readln(direction);
    if not (direction in ['l', 'r']) then writeln('*** Ошибка: некорректный ввод');
  until direction in ['l', 'r'];
  writeln('Было:  ', byte2bin(number));
  writeln('Стало: ', byte2bin(rotatebyte(number, direction = 'r')));
end.
Тестовый прогон:
Код: Выделить всё
Введите число [0..255]: $a5
Введите направление сдвига (l/r): l
Было:  10100101
Стало: 01001011


Добавлено спустя 21 минуту 50 секунд:
И ещё замечание по поводу отключения проверки диапазонов (range check). Безопаснее это делать как-то так:
Код: Выделить всё
function rotatebyte(n: byte; right: boolean): byte;
begin
{$IFOPT R+}{$DEFINE RANGE_ON}{$R-}{$ENDIF}
  if right
  then rotatebyte := (n shr 1) or (n shl 7)
  else rotatebyte := (n shl 1) or (n shr 7);
{$IFDEF RANGE_ON}{$UNDEF RANGE_ON}{$R+}{$ENDIF}
end;
В таком случае, если файл собирается с {$R+}, отключение подействует только на нужный участок кода. Если файл собирается с {$R-}, не произойдёт включения опции на остаток файла, как это было бы в первоначальном примере с незащищённой {$R+} после кода работы со сдвигами. Иными словами, состояние директивы компиляции {$R} вне интересующего нас кода изменено не будет.
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

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

Сообщение Oleg_D » 18.12.2012 21:08:56

bormant писал(а):В таком случае, если файл собирается с {$R+}, отключение подействует только на нужный участок кода. Если файл собирается с {$R-}, не произойдёт включения опции на остаток файла, как это было бы в первоначальном примере с незащищённой {$R+} после кода работы со сдвигами. Иными словами, состояние директивы компиляции {$R} вне интересующего нас кода изменено не будет.

Спасибо, прекрасные примеры и пояснения. Только условную компиляцию мы здесь ещё не проходили, -- об этом в 60-й главе.
Oleg_D
постоялец
 
Сообщения: 390
Зарегистрирован: 09.05.2011 11:28:36

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

Сообщение Paster Fob » 26.01.2013 13:05:09

Глава 49 разбор программы "Крестики-нолики".
чтение из файла
Код: Выделить всё
procedure ReadDesk(var F: Text);

Изначально вы заполняете матрицу значением false.
Код: Выделить всё
FillChar(Desk, SizeOf(Desk), false);

Далее
Код: Выделить всё
Desk[y,x]:= S[x]='+';

Получается что если символ в строке '+' ,то элементу матрицы присваиваем true ,иначе false.Зачем тогда нужна процедура fillchar,если оба значения всё равно присваиваются матрице.
Аватара пользователя
Paster Fob
постоялец
 
Сообщения: 188
Зарегистрирован: 22.02.2011 21:53:36
Откуда: Новосибирск.

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

Сообщение Oleg_D » 26.01.2013 14:37:26

Вот цикл ввода матрицы:
Код: Выделить всё
while not Eof(F) and (y<=Cy) do begin
  Readln(F, S);
  x:=1;
  while (x<=Length(S)) and (x<=Cx) do begin
    Desk[y,x]:= S[x]='+';
    Inc(x);
  end;
  Inc(y);
end

Если файл (или строка) окажется короче, чем требуется, то матрица всё равно будет заполнена определённым образом.
А иначе, без предварительной очистки, там остался бы "мусор".
Oleg_D
постоялец
 
Сообщения: 390
Зарегистрирован: 09.05.2011 11:28:36

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

Сообщение Paster Fob » 26.01.2013 15:16:14

Ясно,к данной программе это не относится.Но на будущее пригодится.

Добавлено спустя 1 час 26 минут 10 секунд:
У меня ещё вопрос по предыдущей программе "Купеческая задачо о пересечении границ" (империя).

Когда нам нужно создать новое множество,мы объявляем пустое множество и добавляем новый элемент к предыдущему значению.
Как быть при работе с массивом множеств?Нужно ли множества массива объявлять пустыми?
В вашей программе этого нет,но я на всякий случай делал так:

Код: Выделить всё
procedure ReadSet(var aFile: text; var aSet : TBoundSet);
var k : integer;
begin
  aSet:=[];
  while not seekEoln(aFile) do begin
    Read(aFile, k);
    aSet:= aSet+[k];
  end;
  Readln (aFile);
end;
Аватара пользователя
Paster Fob
постоялец
 
Сообщения: 188
Зарегистрирован: 22.02.2011 21:53:36
Откуда: Новосибирск.

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

Сообщение Oleg_D » 26.01.2013 20:44:15

Paster Fob писал(а): aSet:=[];

Да, это правильно. А я упустил очистку множества, надо поправить.
Современные компиляторы автоматически очищают секцию глобальных переменных, и оттого следствия таких пропусков не всегда проявляются.
Но привычка явной инициализации -- это хорошая привычка.
Oleg_D
постоялец
 
Сообщения: 390
Зарегистрирован: 09.05.2011 11:28:36

Пред.След.

Вернуться в Книга "Песни о Паскале"

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2

Рейтинг@Mail.ru