Эндрю Хант - Программист-прагматик. Путь от подмастерья к мастеру
etest.c etest.h: etest.inc enumerated.pl
perl enumerated.pl etest.inc
Упражнение 12 из раздела "Обработка текста"Ответ: Вот ответ, написанный на языке Perl.
my $dir = shift or die "Missing directory" ,
for my Sfile (glob(u$dir/*.pr)) {
open(IP, "$file") or die "Opening $file: $!";
undef $/; # Turn off input record separator -
my Scontent = <IP>; # read whole file as one string.
close(IP);
if (Scontent Гrusestrict/m) {
rename Sfile, "$file.bak" or die "Renaming $file: $!"; open(OP, ">$file") or die "Creating $file: $!";
# Put 'use strict' on first line that
# doesn't start #
Scontent =" sr(V.#)/nuse strict;nn/m',
print OP Scontent; close(OP);
print "Updated $filen";
)
else {
print "Sfile already strictn":
)
}
Упражнение 13 из раздела "Генераторы исходных текстов"Ответ: Решение реализовано на языке Perl. В программе происходит динамическая загрузка модуля для генерации требуемого языка, так что добавление новых языков не представляет труда. Главная программа загружает внутреннюю часть (основанную на параметре командной строки), затем считывает ее входные данные и вызывает подпрограммы генерации текста, основанные на содержимом каждой из строк. Мы особенно не суетимся, если речь идет об обработке ошибок: если что-то не так, узнаем об этом довольно быстро.
my lang = shift or die "Missing language";
$lang .= "_cg.pm";
require <$lang> or die "Couldn't load $lang";
# Read and parse the file
my $name;
while (<>) {
chomp;
if (/^ s*$/) {CG::blankLine();)
elsif ((/^ #(.*)/) {CG::comment($1);}
elsif ((/^Ms*(.+)/) {CG::startMsg($1); $name = $1;}
elsif ((/^E/) {CG::endMsg($name);}
elsif (/^Fs*(w+)s+(w+)$/) {CG::simpleType($1,$2);}
elsif (/^Fs*(w+)s+(w+)[(d+)]$/) {CG::arrayType $1,$2,$3);}
else {
die "Invalid line: $ ";
}
}
Написание языковой серверной части не составит труда: создайте модуль, который реализует шесть точек входа. Вот генератор текста на языке С:
#!/usr/bin/perl – w
package CG;
use strict;
# Code generator for 'C' (see cg_base.pl)
sub blankLine() {print "n"; }
sub comment() {print "/*$_[0] */n"; }
sub startMsg() {print "typedef struct {n"; }
sub endMsg() {print "} $_[0];nn"; }
sub arrayType() {
my ($name, $type, $size) = @_;
print " $type $name[$size];n":
}
sub simpleType{) {
my ($name, $type) = @_;
print " $type $пате;п";
}
1;
А вот генератор текста на языке Pascal:
#!/usr/bin/perl – w
package CG;
use strict;
# Code generator for 'Pascal' (see cg_base.pl)
sub blankLine() {print "V;)
sub comment() {print "{$_[0] }n";)
sub startMsg() {print "$_[0] = packed recordn"; }
sub endMsg() {print "end;nn"; }
sub arrayType() {
my ($name, $type, $size) = @_;
$size--;
print" $name: array[0…$size] of $type;n";
)
sub simpleType() {
my ($name, $type) = @_;
print" $name: $type;n7 ',
}
1;
Упражнение 14 из раздела "Проектирование по контракту"Ответ: Этот пример на языке Eiffel удачен. Мы требуем передачи непустых данных и гарантируем, что семантика циклического двунаправленного списка будет соблюдена. Это также способствует нахождению сохраненной строки. Поскольку это некий отложенный класс, то действительный класс, его реализующий, допускает использование любого основного механизма по своему усмотрению. Он может использовать указатели или массив, или что-то еще; пока он соблюдает контракт, мы не беспокоимся.
Упражнение 15 из раздела "Проектирование по контракту"Ответ: Это неудачно. Математическое действие в индексном выражении (index-1) не будет работать с граничными условиями, подобными первой точке входа. Постусловие предполагает определенную реализацию; контракты должны быть более абстрактными по сравнению с указанным выше.
Упражнение 16 из раздела "Проектирование по контракту"Ответ 16: Это удачный контракт, но неудачная реализация. Здесь высовывает свою уродливую голову ошибка типа "Heisenbug" [URL52]. Вероятно, программист допустил опечатку – набрал pop вместо top. Хотя это простой и надуманный пример, весьма трудно диагностировать побочные эффекты в утверждениях (или в любом, самом неожиданном месте в программе).
Упражнение 17 из раздела "Проектирование по контракту"Ответ: Мы продемонстрируем функциональные сигнатуры на языке Java, обозначая предусловия и постусловия в соответствии с iContract. Сначала инвариант для класса:
/**
* @invariant getSpeed() >0
* implies isFull() // Не запускать пустое
* @invariant getSpeed()>=0 &&
* getSpeed() <10 // Проверка границ
*/
Затем предусловия и постусловия:
/**
* @pre Math.abs(getSpeed() – х) <= 1 // Единственный выбор
* @рге х>= 0 && х > 10 // Проверка границ
* @post getSpeed() == х // Проверка скорости
*/
public void setSpeed (final int x)
/**
* @pre !isFull() // Heзаполнять дважды
* @post isFull() // Убедитесь, что было выполнено
*/
void fill()
/**
* @pre isFull() // He очищатьдважды
* @post !isFull() // Убедиться, что выполнено
*/
void empty()
Упражнение 18 из раздела "Проектирование по контракту"Ответ: В этом ряду содержится 21 число. Если вы ответили «20», то допустили так называемую ошибку "поста охраны".
Упражнение 19 из раздела "Программирование утверждений"Ответ:
1. В сентябре 1752 г. было всего лишь 19 дней. Это было сделано с целью синхронизации при переходе с юлианского на григорианский календарь.
2. Каталог мог быть удален другим процессом, у вас нет прав доступа на его чтение, выражение &sb может быть недопустимым – вы все уловили.
3. Мы проявили малодушие, не указав типов а и b. Во время перегрузки операторов могло случиться так, что поведение знаков +, =, или != стало непредсказуемым. Кроме того, а и b могут быть псевдонимами одной и той же переменной, так что при втором присвоении произойдет перезапись значения, сохраненного во время первого.
4. В неевклидовой геометрии сумма углов треугольника не будет составлять 180°. Подумайте о треугольнике, отображаемом на поверхности сферы.
5. Минуты, приходящиеся на високосный год, могут состоять из 61 или 62 секунд.
6. Переполнение может оставить результат операции а+1 отрицательным (это также может произойти в языках С и С++).
Упражнение 20 из раздела "Программирование утверждений"Ответ: Мы решили реализовать очень простой класс с единственным статическим методом TEST, который выводит на печать сообщение и след стека, если переданный параметр condition является ложным.
package com.pragprog.util;
import java.lang.System; //для exit()
import java.lang.Thread; //для dumpStack()
public class Assert {
/** Write a message, print a stack trace and exit if
* our parameter is false.
*/
public static void TEST(boolean condition) {
if (Icondition) {
System.out.println("==Assertion Failed==");
Thread.dumpStack();
System.exit(1);
}
}
// Testbed. If our argument is 'okay', try an assertion that
// succeeds, if 'fail' try one that fails
public static final void main(String args[]) {
if (args[0].compareTo("okay") == 0) {
TEST(1 == 1);
}
else if (args[0].compareTo("fail") == 0) {
TEST(1 == 2);
}
else {
throw new RuntimeException("Bad argument") ,
}
}
}
Упражнение 21 из раздела "Случаи, когда используются исключения"Ответ: Нехватка памяти является исключительным состоянием, поэтому мы полагаем, что в случае (1) должно возбуждаться исключение.
Невозможность отыскания точки входа – вполне нормальная ситуация. Приложение, которое вызывает наш класс-набор, может написать программу, которая проверяет наличие точки входа, перед тем как добавить потенциальный дубликат. Мы полагаем, что в случае (2) нужно просто осуществить возврат ошибки.
Случай (3) более проблематичен – если указатель null играет существенную роль в приложении, его добавление к контейнеру может быть оправдано. Но если для хранения пустых значений нет веских оснований, то, по всей вероятности, необходимо возбудить исключительную ситуацию.
Упражнение 22 из раздела "Балансировка ресурсов"Ответ: В большинстве реализаций языков С и С++ отсутствуют способы проверки того, что указатель действительно указывает на допустимый блок памяти. Обычная ошибка состоит в освобождении блока памяти и организации ссылки на этот блок далее в тексте программы. К тому времени этот блок памяти уже может быть перераспределен для других целей. Обнуляя указатель, программисты надеются предотвращать эти инородные ссылки – в большинстве случаев разыменование указателя null генерирует ошибку в ходе выполнения программы.
Упражнение 23 из раздела "Балансировка ресурсов"Ответ: Обнуляя ссылку, вы уменьшаете число указателей на упомянутый объект на единицу. Как только этот счетчик становится равным нулю, объект получает право на сбор «мусора». Обнуление ссылок может играть существенную роль в продолжительных по времени программах, где программистам приходиться удостоверяться, что использование памяти со временем не возрастает.
Упражнение 24 из раздела "Несвязанность и закон Деметера"Ответ: Файл заголовка предназначен для определения интерфейса между соответствующей реализацией и внешним миром. Сам по себе файл заголовка не обязан обладать информацией о внутренней организации класса Date – от него лишь требуется сообщить компилятору о том, что конструктор принимает класс Date в качестве параметра. Поэтому, если файл заголовка не использует Dates в подставляемых функциях, второй фрагмент будет работать просто замечательно.