Разработка

Каждая из наших программ первым делом получает числовой параметр из командной строки:

my $n=shift // die "Нужно неотрицательное число!\n";

Здесь требуется пояснение. Выражение A // B возвращает значение A, если оно определено (напоминаем, что значения выражений в Perl могут быть неопределёнными; в частности, неопределённое значение имеет переменная после своего объявления, и до инициализации — первоначальное присвоения значений этой переменной). Если же значение A неопределённое, оператор // возвращает значение B.

В нашем случае A — выражение my $n=shift. Его значение — это то, что присваивается переменной $n, а присваивается то, что выдаёт команда shift. А выдаёт эта команда первый элемент массива @ARGV. Если массив пуст (отсутствуют параметры в командной строке), левая часть оператора // окажется неопределённой, и потребуется вычислить правую часть B (у нас это die), чтобы возвратить полученное значение. Вычисление правой части приведёт к завершению программы и выдаче сообщения об ошибке.

Вывод очередной строки сплошного квадрата будем выполнять в цикле. Всё, что требуется от такого цикла, это то, что он должен прокрутиться $n раз. В цикле можно устроить изменение счётчика $j (это номер строки, если нумерация начинается с нуля):

for(my $j=0; $j<$n; $j++)
{
	вывести $n звёздочек и перейти на новую строку
}

Чтобы вывести $n звёздочек, тоже организуем цикл (теперь меняется счётчик $i — номер столбца):

for(my $i=0; $i<$n; $i++)
{
	print '*';
}

Осталось поместить второй цикл внутрь первого, и не забыть про вывод символа конца строки:

for(my $j=0; $j<$n; $j++)
{
	for(my $i=0; $i<$n; $i++)
	{
		print '*';
	}
	print "\n";
}

Можно было бы вместо обычных циклов for использовать переборные:

for my $j(0..$n-1)
{
	for my $i(0..$n-1)
	{
		print '*';
	}
	print "\n";
}

или

for my $j(1..$n)
{
	for my $i(1..$n)
	{
		print '*';
	}
	print "\n";
}

Так короче. Наконец, поскольку счётчики $j и $i никак не упоминаются в телах обоих циклов, уберём их из заголовков циклов. Тогда вместо этих переменных в циклах будет меняться переменная по умолчанию $_, которая, впрочем, всё равно никак не используется:

for(1..$n)
{
	for(1..$n)
	{
		print '*';
	}
	print "\n";
}

Как и в первой версии программы, рисующей сплошной квадрат, организуем двойной цикл: во внешнем печатается строка, а во внутреннем — клетки шахматной доски (звёздочки или пробелы). В обоих циклах перебираются значения счётчиков $j (номер строки) и $i (номер столбца). Но как по номерам строки и столбца определить, какой из значков выводить?

Заметим, что для клетки чётность суммы номеров строки и столбца отличается от той же величины, вычисленной для клеток, соседних с ней (справа, сверху, слева и снизу). ведь у таких соседей меняется чётность одного из слагаемых, а значит, и всей суммы. Если мы договорились, что в левом верхнем углу доски будет чёрная клетка, тогда чёрными должны быть все клетки с чётной суммой, а белыми — с нечётными.

Это наблюдение приводит нас к такому коду:

for my $j(0..$n-1)
{
	for my $i(0..$n-1)
	{
		if(($i+$j)%2==1)
		{
			print ' ';
		}
		else
		{
			print '*';
		}
	}
	print "\n";
}

(кстати, заголовок условной конструкции if(($i+$j)%2==1) можно заменить на if(($i+$j)%2).

Поскольку и в блоке if, и в блоке else вызывается print (с различными параметрами), можно вообще обойтись без условной конструкции. Вместо неё напишем условный оператор, возложив на него задачу выбора или звёздочки, или пробела:

for my $j(0..$n-1)
{
	for my $i(0..$n-1)
	{
		print(($i+$j)%2? ' ': '*');
	}
}

Программу, рисующую пустой квадрат, оставим без подробных комментариев. В ней будут отдельно выводиться первая, последняя строки, и все промежуточные. Следует особо позаботиться о случаях, когда $n меньше двух.

Эта задача будет посложней предыдущей. Постараемся сформулировать правило, определяющее, будет ли в j-й строке и i-м столбце звёздочка или пробел.

Звёздочки будут располагаться на границе квадрата (в строках или столбцах с номерами от 0 до n 1 ). Затем, по мере удаления от границы квадрата, будут чередоваться слои, заполненные пробелами или звёздочками. Теперь ясно, что наличие звёздочки или пробела зависит от расстояния до границы: если оно чётно, ставим звёздочку, в противном случае — пробел.

Осталось лишь вычислить расстояние d до границы квадрата. Это наименьшее из расстояний до всех четырёх сторон. Расстояние до верхней стороны равно j, до левой — i, до нижней — n 1 j , до правой — n 1 i . Таким образом, d = min j i n 1 j n 1 i .

В приведённом ниже фрагменте программы четыре присваивания переменной $d посвящены как раз вычислению этого минимума:

my $d;
for my $j(0..$n-1)
{
	for my $i(0..$n-1)
	{
		$d=$j;
		$d=$i if $i<$d;
		$d=$n-$j-1 if $n-$j-1<$d;
		$d=$n-$i-1 if $n-$i-1<$d;
		print($d%2? ' ': '*');
	}
	print "\n";
}

Информатика-54© А. Н. Швец