ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.10.2023
Просмотров: 430
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
40 Глава 1
ка сильного сигнала, на который реагируют глубинные структуры мозга. Если вы не знаете, что вот-вот наступите на гвоздь, то для вас будет невозможно своевременно отреагировать и оказать противо- действие автоматическому отклику мозга. Поэтому мы разрешим ре- бенку прокричаться.
Программист не находится с этим мальчиком в одной лодке. Ри- скуя быть похожим на гуру саморазвития, разочарованный програм- мист не реагирует на внешние раздражители. Расстроенный про- граммист не зол на исходный код, отображенный на мониторе, хотя программист в этом смысле тоже может выражать свое расстрой- ство. Скорее всего, расстроенный программист зол на самого себя.
Источник раздражения также является и конечным пунктом, и это сознание программиста.
Когда вы позволяете себе расстроиться — и я использую глагол
«позволять» умышленно, — вы, по сути, даете себе оправдание в про- должающихся неудачах. Представьте, что вы работаете над сложной задачей и чувствуете как усиливается ваше разочарование. Спустя часы, взглянув на послеобеденное время, стиснутые зубы и каранда- ши, брошенные со злости в стену, вы говорите себе, что добились бы гораздо большего, если бы у вас получилось успокоиться. На самом же деле вы, возможно, решили, что поддаться злости гораздо проще, чем посмотреть в глаза сложной задаче.
В конечном итоге избегать расстройств — это то решение, кото- рое вы должны принять. Однако есть некоторые мысли, которые вы можете использовать и которые вам помогут. Прежде всего, никогда не забывайте первое правило, которое гласит, что у вас всегда дол- жен быть план и что несмотря на то, написание кода, который ре- шает исходную задачу, — это цель этого плана, это не единственный шаг этого плана. Таким образом, если у вас есть план и вы следуете ему, то вы продвигаетесь вперед, и вы должны этому верить. Если вы выполнили все шаги по своему первоначальному плану, и вы все еще не готовы начать писать программный код, значит, пришло время сделать другой план.
Кроме того, когда дело доходит до ультиматума «разочаровать- ся» или сделать «паузу», делайте паузу. Одна из хитростей — одновре- менно решать несколько задач. Таким образом, если решение одной задачи зашло в тупик, то вы можете перенаправить свои усилия на решение другой. Обратите внимание, что если у вас получится раз- делить задачу, то вы сможете воспользоваться этой техникой для ре- шения одной и той же задачи: просто заблокируйте ту часть задачи, что завела вас в тупик, и начните работать над каким-то другим аспек- том. Если у вас нет другой задачи, которую вы могли бы порешать, встаньте со стула и займитесь чем-то другим, что стимулирует кро- вообращение мозга, но не напрягает его: прогуляйтесь, постирайте белье, выполните зарядку с растяжками (если вы подписываетесь на работу программистом, который должен сидеть за компьютером це-
лый день, я очень рекомендую вам выполнять такую гимнастику!).
Не терзайтесь над решением задачи до победного конца.
Упражнения
Помните: чтобы по-настоящему изучить что-то, что вы должны при- менить на практике, так что выполните как можно больше упражне- ний. В этой первой главе мы, конечно, еще не обсуждаем програм- мирование, но даже несмотря на это я рекомендую вам попробовать некоторые упражнения. Воспринимайте эти вопросы как разминку для пальцев, прежде чем мы начнем играть настоящую музыку.
1.1.
Попробуйте решить головоломку «Судоку» средней сложности
(вы можете найти их по всему Интернету и в газетных киосках), экспериментируя с различными стратегиями и принимая во внимание результаты. Можете ли вы написать общий план ре- шения «Судоку»?
1.2.
Рассмотрим вариант головоломки со скользящими плитками, где на плитки нанесены изображения, а не цифры. Насколько это увеличивает сложность и почему?
1.3.
Найдите стратегию для решения головоломки со скользящими плитками, отличную от моей.
1.4.
Найдите старомодные головоломки наподобие задачи про лиси- цу, гуся и кукурузу и попытайтесь их решить. Многие из великих головоломок были созданы или популяризированы Сэмом Лой- дом, поэтому вы можете поискать его имя. Кроме того, как толь- ко вы откроете (или сдадитесь и прочитаете) решение, подумай- те, как вы могли бы сделать более легкую версию головоломки.
Что бы вы изменили? Ограничения или просто формулировку?
1.5.
Попробуйте написать несколько явных стратегий для других традиционных игр с карандашом и бумагой, например для крос- свордов. С чего начать? Что вы должны делать, когда зашли в ту- пик? Даже простые газетные игры, такие как составление слов из набора букв, полезны для обдумывания стратегии.
Не терзайтесь над решением задачи до победного конца.
Упражнения
Помните: чтобы по-настоящему изучить что-то, что вы должны при- менить на практике, так что выполните как можно больше упражне- ний. В этой первой главе мы, конечно, еще не обсуждаем програм- мирование, но даже несмотря на это я рекомендую вам попробовать некоторые упражнения. Воспринимайте эти вопросы как разминку для пальцев, прежде чем мы начнем играть настоящую музыку.
1.1.
Попробуйте решить головоломку «Судоку» средней сложности
(вы можете найти их по всему Интернету и в газетных киосках), экспериментируя с различными стратегиями и принимая во внимание результаты. Можете ли вы написать общий план ре- шения «Судоку»?
1.2.
Рассмотрим вариант головоломки со скользящими плитками, где на плитки нанесены изображения, а не цифры. Насколько это увеличивает сложность и почему?
1.3.
Найдите стратегию для решения головоломки со скользящими плитками, отличную от моей.
1.4.
Найдите старомодные головоломки наподобие задачи про лиси- цу, гуся и кукурузу и попытайтесь их решить. Многие из великих головоломок были созданы или популяризированы Сэмом Лой- дом, поэтому вы можете поискать его имя. Кроме того, как толь- ко вы откроете (или сдадитесь и прочитаете) решение, подумай- те, как вы могли бы сделать более легкую версию головоломки.
Что бы вы изменили? Ограничения или просто формулировку?
1.5.
Попробуйте написать несколько явных стратегий для других традиционных игр с карандашом и бумагой, например для крос- свордов. С чего начать? Что вы должны делать, когда зашли в ту- пик? Даже простые газетные игры, такие как составление слов из набора букв, полезны для обдумывания стратегии.
1 2 3 4 5 6 7 8 9 ... 34
42 Глава 2
42
В этой главе мы начнем работать с программным кодом. Хотя для по- следующих глав потребуются знания программирования на среднем уровне, навыки программирования, требуемые в этой главе, настолько просты, насколько это возмож- но. Это не означает, что все эти головоломки бу- дут легкими.
Но это позволит вам сосредоточиться на решении задач, а не на синтаксисе языка. Это решение проблемы в самом чи- стом виде. Когда вы выясните, что вам нужно сделать, перевод ва- ших мыслей в код на языке С++ будет прост. Помните, что простое прочтение этой книги приносит лишь долю возможного результата.
По ходу обсуждения вы должны прорабатывать каждую задачу, ко- торая кажется вам нетривиальной, и пытаться решить ее самосто-
# "
2
Истинные головоломки 43
ятельно, прежде чем читать о моем подходе. В конце главы попро- буйте выполнить некоторые упражнения, многие из которых будут продолжением обсуждаемых нами проблем.
Обзор языка С++, используемого в этой главе
В этой главе используется базовый язык С++, с которым вы должны быть знакомы, в том числе управляющие инструкции if, for, while и do-while
, а также switch. Возможно, вы еще не очень уверенно решае- те исходные задачи с этими инструкциями, но, в конце концов, книга- то как раз об этом. Однако вы должны понимать синтаксис написания этих инструкций или иметь под рукой хороший справочник по С++.
Вы также должны знать, как писать и вызывать функции. Для упрощения задачи мы будем использовать стандартные потоки cin и cout для ввода и вывода. Чтобы использовать эти потоки, включите в свой код необходимый заголовочный файл iostream и добавьте ин- струкции using для стандартных объектов потока:
#include
using std::cin;
using std::cout;
Для краткости эти инструкции не будут приведены в листингах кода. Включение этих инструкций предполагается по умолчанию во всех программах по необходимости.
Шаблоны вывода
В этой главе мы разберем три задачи. Так как я буду обширно исполь- зовать приемы разделения и редукции задач, каждая из этих задач станет источником нескольких подзадач. В первом разделе давайте опробуем несколько программ, производящих шаблонный вывод не- кой геометрической фигуры. Такие программы развивают навыки написания циклов.
:
Напишите программу, в которой используется только две инструкции вывода: cout <<
"#" и cout << "\n" для создания узора из символов # в виде половины квадрата 5
х
5
(или прямоугольного треугольника):
#####
####
###
##
#
Это еще один отличный пример важности ограничений. Если мы проигнорируем требование, что можем использовать только две
44 Глава 2
инструкции вывода, одна из которых выводит только один символ #, а вторая — символ переноса строки, мы можем написать «Кобаяси
Мару» и тривиально решить эту задачу. Однако с этими ограничени- ями для решения задачи нам придется воспользоваться циклами.
Возможно, у вас в голове уже есть решение, но давайте предпо- ложим, что его нет. Первый хороший прием — это упрощение. Как мы можем упростить задачу до такой степени, где ее будет легко ре- шить? Что если бы нам надо было вывести целый квадрат, а не его половину?
:
( ! $ )
Напишите программу, в которой используется только две инструкции вывода: cout <<
"#" и cout << "\n" для создания узора из символов # в виде квадрата 5
x
5:
#####
#####
#####
#####
#####
Этого может быть достаточно для начала, но давайте предполо- жим, что мы не знаем, как решить и эту задачу. Мы могли бы еще сильнее упростить задачу, поставив условие вывести одну линию, а не целый квадрат.
: ( ! !
$ )
Напишите программу, в которой используется только две инструкции вывода: cout <<
"#" и cout << "\n" — для вывода линии из пяти символов #:
#####
Теперь перед нами возникает тривиальная задача, которую мы мо- жем решить с помощью цикла for: for (int hashNum = 1; hashNum <= 5; hashNum++) {
cout << "#";
}
cout << "\n";
Отсюда вернемся к предыдущему упрощению: фигуре полного ква- драта. Полный квадрат — это всего лишь повторяющаяся пять раз линия из символов #. Мы знаем, как создать повторяющийся код: нужно просто лишь написать цикл. Таким образом, мы можем пре- вратить цикл во вложенный:
for (int row = 1; row <= 5; row++) {
for (int hashNum = 1; hashNum <= 5; hashNum++) {
Истинные головоломки 45
cout << "#";
}
cout << "\n";
}
Мы поместили весь код из предыдущего листинга в цикл, таким образом, чтобы он повторялся пять раз, выводя пять строк, каждая из которых — линия из символов #. Мы уже приближаемся к оконча- тельному решению. Как же изменить код, чтобы он выводил узор в виде половины квадрата? Если мы взглянем на последний листинг и сравним с нужным выводом в виде половины квадрата, то уви- дим, что вся проблема в условном выражении hashNum <= 5. Это условное выражение выводит одинаковую линию из символов # на каждой строке. Что нам требуется — это механизм, который настра- ивал бы количество символов на каждой строке так, чтобы на пер- вой строке было пять символов, на второй четыре и так далее.
Чтобы понять, как это сделать, давайте поставим еще один экс- перимент по упрощению программы. Повторимся, всегда проще ра- ботать над проблемным участком задачи, изолировав его. На минуту давайте забудем о символах # и поговорим только о числах.
: " ,
Напишите строку кода в указанном участке цикла нижеприведенного листинга. Про- грамма распечатывает цифры от 5 до 1 в порядке убывания, при этом каждая цифра находится на отдельной строке.
for (int row = 1; row <= 5; row++) {
cout <<
X выражение << "\n";
}
Мы должны найти выражение
X
, оно равняется 5, когда row = 1 и 4, когда row = 2. Если мы хотим получить выражение, которое бу- дет уменьшаться по мере возрастания значения переменной row, то первая мысль, которая может нас посетить — это приписать знак ми- нус к значениям row, умножив их на –1. Это приведет к появлению уменьшающихся чисел, но это будут не те числа, что нам нужны. Тем не менее мы будем ближе, чем думаем. Какова тогда разница между нужным значением и значением, полученным в результате умноже- ния row на –1? В табл. 2.1 приводится анализ.
Табл. 2.1. Вычисление требуемого значения из значения переменной row
row
Š!K3% ƒ…=…,
row*-1
n2%……, %2 Š!K3%% ƒ…=…,
1 5
-1 6
2 4
-2 6
3 3
-3 6
4 2
-4 6
5 1
-5 6
46 Глава 2
Отклонение представляет собой константу, равную 6. Это зна- чит, что требуемое выражение — это row * -1 + 6. Вспомнив уроки алгебры, мы можем упростить это выражение до 6 — row. Давайте проверим:
for (int row = 1; row <= 5; row++) {
cout << 6 — row << "\n";
}
Отлично — все работает! Если бы код не заработал, то наша ошибка, скорее всего, была бы незначительной, благодаря тем тща- тельным шагам, которые мы предприняли. Повторимся, с такими маленькими и простыми блоками кода гораздо проще эксперимен- тировать. Теперь давайте возьмем это выражение и воспользуемся им для ограничения вложенного цикла:
for (int row = 1; row <= 5; row++) {
for (int hashNum = 1; hashNum <= 6 — row; hashNum++) {
cout << "#";
}
cout << "\n";
}
Использование техники упрощения требует больше шагов, что- бы добраться от описания до завершенной программы, но сделать отдельный шаг гораздо проще. Представьте использование несколь- ких ремней и подвижных блоков для подъема тяжелого объекта: что- бы приложить столько же подъемной силы, вам потребуется тянуть веревку на большее расстояние, но каждое усилие будет иметь мень- шую нагрузку на ваши мышцы.
Прежде чем мы пойдем дальше, давайте решим еще одну задачу с геометрической фигурой.
: # $
Напишите программу, в которой используется только две инструкции вывода: cout
<< "#" и cout << "\n" для создания узора из символов # в виде равнобедренного треугольника:
#
##
###
####
###
##
#
Мы не будем проходить все шаги, которые прошли при решении предыдущей задачи, потому как нет в этом необходимости. Задача
«Равнобедренный треугольник» аналогична задаче «Половина ква- драта», а это значит, что при ее решении мы можем воспользоваться
Истинные головоломки 47
теми сведениями, которые узнали при решении предыдущего упраж- нения. Помните максиму «начните с того, что вы уже знаете»? Давай- те начнем с перечисления приемов и техник задачи «Половина ква- драта», которые мы можем использовать при решении этой задачи.
Мы знаем, как:
y
вывести на экран строку символов конкретной величины с помощью цикла;
y
вывести на экран последовательность строк с помощью вло- женных циклов;
y
отображать различное, а не одно фиксированное количе- ство символов в каждой строке с помощью математической форму лы; y
найти корректное алгебраическое выражение путем экспери- ментирования и анализа.
Рис. 2.1 резюмирует наше текущее положение. В первом ряду по- казана предыдущая задача «Половина квадрата». Мы также можем видеть требуемый узор из символов (а), узор «линию» (б), узор «ква- драт» (в), а также числовую последовательность (г), которая транс- формирует узор «квадрат» в узор «половина квадрата». Во втором ряду показана текущая задача «Равнобедренный треугольник». Мы опять видим требуемый узор из символов (д), узор «линию» (е), узор
«квадрат» (ж), а также числовую последовательность (з).
Рис. 2.1. Различные компоненты, необходимые для решения задач с фигурами.
На данном этапе у нас не будет проблем с воспроизведением (е), так как этот узор почти идентичен (б). Мы также должны быть в со- стоянии вывести узор (ж), так как это просто узор (в) с большим ко- личеством строк и меньшим количеством символов в строке. На- конец, если бы кто-то предложил нам алгебраическое выражение,