ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.10.2023
Просмотров: 408
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Думайте как программист 253
Этот код было довольно просто написать, основываясь на пре- дыдущих функциях. Я просто обхожу list в поисках number. Если я нахожу его, то возвращаю значение true, а если добираюсь до конца списка, то возвращаю false. Теперь я могу реализовать общий тест на соответствие шаблону: bool matchesPattern(string word, char letter, list
for (int i = 0; i < word.length(); i++) {
if (word[i] == letter) {
if (!numberInPattern(pattern, i)) {
return false;
}
} else {
if (numberInPattern(pattern, i)) {
return false;
}
}
}
return true;
}
Как видите, эта функция соответствует описанному ранее плану.
Для каждого символа в строке, если он соответствует значению letter, код проверяет, находится ли текущая позиция в шаблоне. Если символ не соответствует значению letter, код производит проверку, чтобы удостовериться в том, что позиция не находится в шаблоне. Если хотя бы одна позиция не соответствует шаблону, слово отклоняется; в про- тивном случае будет достигнут конец слова, и оно будет принято.
Сейчас мне кажется, что найти наиболее распространенный шаб лон будет проще, если каждое слово в списке содержит указан- ную букву. Поэтому я пишу быструю функцию для отбрасывания слов, не содержащих эту букву: void removeWordsWithoutLetter(list
list
iter = wordList.begin();
while (iter != wordList.end()) {
if (iter-> nd(requiredLetter) == string::npos) {
iter = wordList.erase(iter);
} else {
iter++;
}
}
}
Этот код представляет собой просто комбинацию идей, исполь- зованных в предыдущих функциях. Теперь, когда я думаю об этом, мне понадобится и обратная функция, которая отбрасывает все сло- ва, которые содержат указанную букву. Я буду использовать ее для уменьшения списка слов-кандидатов, когда программа признает по- следнюю догадку промахом: void removeWordsWithLetter(list
list
iter = wordList.begin();
1 ... 26 27 28 29 30 31 32 33 34
254 Глава 8
while (iter != wordList.end()) {
if (iter-> nd(forbiddenLetter) != string::npos) {
iter = wordList.erase(iter);
} else {
iter++;
}
}
}
Теперь я готов найти наиболее распространенный шаблон в спи- ске слов для данной буквы. Я рассмотрел ряд подходов и выбрал тот, который, как мне кажется, я мог бы легче всего реализовать. Во- первых, я воспользуюсь вызовом вышеприведенной функции, что- бы удалить все слова, не содержащие указанную букву. Затем я возьму первое слово в списке, определю его шаблон и подсчитаю количе- ство других слов в списке, соответствующих этому же шаблону. Все эти слова будут стираться из списка по мере их подсчета. Затем про- цесс повторится снова с любым словом, находящимся в начале спи- ска, и так будет продолжаться, пока список не опустеет. Результат вы- глядит следующим образом: void mostFreqPatternByLetter(
X list
Y list
Z int & maxPatternCount) {
[ removeWordsWithoutLetter(wordList, letter);
list
maxPatternCount = 0;
\ while (wordList.size() > 0) {
iter = wordList.begin();
list
] for (int i = 0; i < iter->length(); i++) {
if ((*iter)[i] == letter) {
currentPattern.push_back(i);
}
}
int currentPatternCount = 1;
iter = wordList.erase(iter);
^ while (iter != wordList.end()) {
if (matchesPattern(*iter, letter, currentPattern)) {
currentPatternCount++;
iter = wordList.erase(iter);
} else {
iter++;
}
}
_ if (currentPatternCount > maxPatternCount) {
maxPatternCount = currentPatternCount;
maxPattern = currentPattern;
}
currentPattern.clear();
}
}
Список list представляет собой параметр типа значения
X
, по- скольку в процессе обработки данная функция будет уменьшать спи- сок, пока в нем ничего не останется, и я не хочу влиять на параметр, передающийся вызывающим кодом. Обратите внимание на то, что maxPattern
Y
и maxPatternCount
Z
являются только исходящими па-
Думайте как программист 255
раметрами; они будут использоваться для отправки наиболее часто встречающегося шаблона и количества случаев его появления обрат- но в вызывающий код. Я удаляю все слова, не содержащие значение letter
[
. Затем я вхожу в основной цикл функции, который продол- жается до тех пор, пока список не опустеет
\
. Код внутри цикла име- ет три основных раздела. Во-первых, цикл for создает шаблон для первого слова в списке
]
. Затем цикл while подсчитывает, сколько слов в списке соответствует этому шаблону
^
. Наконец, мы прове- ряем, превышает ли это значение наибольшее из определенных до сих пор значений, используя стратегию «Царь горы», описанную в главе 3
_
Последняя нужная мне служебная функция будет отображать все отгаданные до сих пор буквы. Помните, что я храню их как массив из 26 значений bool: void displayGuessedLetters(bool letters[26]) {
cout << "Letters guessed: ";
for (int i = 0; i < 26; i++) {
if (letters[i]) cout <<
X(char)('a' + i) << " ";
}
cout << "\n";
}
Обратите внимание на то, что я добавляю базовое значение од- ного диапазона, в данном случае символ a, к значению из другого диапа зона
X
, этот метод мы впервые использовали в главе 2.
Теперь, когда все ключевые подзадачи сформулированы, я готов попытаться решить задачу целиком, однако у меня есть много функ- ций, которые не были полностью протестированы, и я хотел бы протестировать их как можно скорее. Поэтому, вместо того, чтобы пытаться решить оставшуюся часть задачи за один шаг, я собираюсь уменьшить эту задачу. Я сделаю это, превратив в константы некото- рые переменные, например размер загаданного слова.
Поскольку я собираюсь отбросить эту версию, я спокойно могу включить всю игровую логику в функцию main. Тем не менее из-за внушительного объема кода я буду представлять его поэтапно.
int main () {
X list
const int wordLength = 8;
const int maxMisses = 9;
Y int misses = 0;
Z int discoveredLetterCount = 0;
[ removeWordsOfWrongLength(wordList, wordLength);
\ char revealedWord[wordLength + 1] = "********";
] bool guessedLetters[26];
for (int i = 0; i < 26; i++) guessedLetters[i] = false;
^ char nextLetter;
cout << "Word so far: " << revealedWord << "\n";
Этот первый раздел кода устанавливает константы и перемен- ные, которые нам понадобятся для игры. Большая часть этого кода не
256 Глава 8
требует пояснений. Список слов создается из файла
X
, а затем уреза- ется до указанной длины слова, которая в данном случае имеет посто- янное значение 8
[
. В переменной misses
Y
хранится количество не- правильных догадок Игрока 2, в то время как discoveredLetterCount
Z
отслеживает количество позиций, занятых в слове угаданной буквой,
(поэтому если буква d присутствует в слове дважды, то угадывание бук- вы d увеличивает это значение на две единицы). В переменной re- vealedWord хранится загаданное слово в том виде, в котором оно в настоящее время известно Игроку 2, со звездочками вместо букв, ко- торые еще не были угаданы
\
. Массив логических значений guessed-
Letters
]
отслеживает конкретные угаданные до сих пор буквы; цикл устанавливает все значения на false. Наконец, nextLetter
^
сохраня- ет текущую догадку Игрока 2. После вывода начального значения пе- ременной revealedWord я готов к запуску основного игрового цикла.
X while (discoveredLetterCount < wordLength && misses < maxMisses) {
cout << "Letter to guess: ";
cin >> nextLetter;
Y guessedLetters[nextLetter — 'a'] = true;
Z int missingCount = countWordsWithoutLetter(wordList, nextLetter);
list
int nextPatternCount;
[ mostFreqPatternByLetter(wordList, nextLetter, nextPattern,
nextPatternCount);
if (missingCount > nextPatternCount) {
\ removeWordsWithLetter(wordList, nextLetter);
misses++;
} else {
] list
while (iter != nextPattern.end()) {
discoveredLetterCount++;
revealedWord[*iter] = nextLetter;
iter++;
}
wordList = reduceByPattern(wordList, nextLetter, nextPattern);
}
cout << "Word so far: " << revealedWord << "\n";
displayGuessedLetters(guessedLetters);
}
Существует два условия, которые могут привести к окончанию игры. Либо Игрок 2 угадает все буквы в слове, так что значение dis- coveredLetterCount достигнет значения wordLength, либо промахи
Игрока 2 позволят завершить рисунок повешенного, и в этом случае значение misses будет равно значению maxMisses. Таким образом, цикл продолжает выполняться до тех пор, пока одно из этих условий не окажется истинным
X
. Внутри цикла после получения от пользо- вателя очередной догадки обновляется соответствующая позиция в массиве guessedLetters
Y
. Затем начинается жульничество. С помо- щью countWordsWithoutLetter программа определяет, сколько слов- кандидатов останется в списке слов, если догадка будет объявлена промахом
Z
, а с помощью mostFreqPatternByLetter она определяет максимальное количество оставшихся слов, если догадка будет объ- явлена попаданием
[
. Если первое значение будет больше второго,
Думайте как программист 257
то слова с угаданной буквой отбрасываются, а значение misses ин- крементируется
\
. Если второе значение больше первого, мы возь- мем шаблон, заданный mostFreqPatternByLetter, и обновим значе- ние revealedWord, удалив при этом все слова из списка, которые не соответствуют этому шаблону
]
if (misses == maxMisses) {
cout << "Sorry. You lost. The word I was thinking of was '";
cout <<
X(wordList.cbegin())->c_str() << "'.\n";
} else {
cout << "Great job. You win. Word was '" << revealedWord << "'.\n";
}
return 0;
}
Остальная часть кода представляет собой то, что я называю
вскрытием цикла, в котором действие, предпринимаемое после за- вершения цикла, определяется условием, которое «убило» этот цикл. Здесь либо нашей программе удастся обмануть пользователя и выиграть, либо Игрок 2, несмотря ни на что, заставит программу раскрыть слово целиком. Обратите внимание на то, что в случае по- беды программы в списке должно остаться хотя бы одно слово, по- этому я просто отображаю первое слово
X
и утверждаю, что именно оно и было загадано с самого начала. Более хитрая программа мог- ла бы случайным образом выбрать одно из оставшихся слов, чтобы уменьшить вероятность того, что противник обнаружит обман.
Анализ первоначальных результатов
Я собрал весь код воедино и протестировал. Он работает, однако, очевидно, существует множество возможностей для его улучшения.
Помимо дизайна, программе недостает большого количестве функ- ций. Она не позволяет пользователю указать длину загаданного сло- ва или количество допустимых неправильных догадок. Она не про- веряет, называлась ли угаданная буква раньше. В этом отношении она даже не проверяет, является ли входной символ строчной бук- вой. В этой программе также отсутствует множество полезных функ- ций интерфейса, например сообщающих пользователю количество оставшихся допустимых промахов. Я думаю, было бы неплохо, если бы программа предлагала пользователю сыграть снова, чтобы ему не приходилось повторно ее запускать.
Что касается дизайна, то, когда я начну обдумывать готовую вер- сию программы, я серьезно рассмотрю методы объектно-ориенти- рованного дизайна. Класс wordlist сейчас представляется естествен- ным выбором. Основная функция кажется мне слишком большой.
Мне нравится модульный, простой в обслуживании дизайн, поэтому основная функция должна быть короткой и просто направлять тра- фик между подпрограммами, которые выполняют настоящую рабо- ту. Таким образом, моя основная функция должна быть разбита на не- сколько функций. Вероятно, мне следует переосмыслить некоторые
258 Глава 8
из моих первоначальных вариантов дизайна. Например, оглядываясь назад, сохранение шаблонов в виде list
Может быть, мне стоит попробовать использовать массив значений типа bool аналогично тому, как я использовал массив guessedLetters?
Или, может быть, мне следует поискать совершенно другую структуру. Теперь мне пришло время отступить, чтобы посмотреть, существуют ли какие-либо возможности для изучения новых мето- дов решения этой задачи. Мне интересно, есть ли еще не рассмо- тренные мной специализированные структуры данных, которые могут оказаться полезными. Даже если я в конечном итоге останусь при своем первоначальном выборе, в процессе исследования я мог бы многому научиться.
Несмотря на то, что все эти решения по-прежнему не оконча- тельны, я ощущаю прогресс в работе над этим проектом. Хорошо иметь рабочую программу, которая отвечает основным требовани- ям задачи. Я могу легко поэкспериментировать с различными дизай- нерскими идеями на этой черновой версии, будучи уверенным в том, что у меня уже есть решение и я просто ищу лучший его вариант.
- 7 B. 7- %/
Операционная система Microsoft Windows создает то, что называется точкой вос- становления, перед установкой или модификацией компонентов системы. Точка восстановления содержит резервные копии ключевых файлов, например реестра.
Если установка или обновление приведет к серьезной проблеме, его можно факти- чески «откатить» или отменить, скопировав файлы из точки восстановления.
Я настоятельно рекомендую использовать тот же подход при работе с вашим собственным исходным кодом. Когда у вас есть рабочая программа, которую вы предполагаете позднее изменить, сделайте копию всего проекта и измените толь- ко копию. Это быстро, и в будущем может сэкономить вам время, если с вашими изменениями возникнут проблемы. Программисты могут легко попасть в ловушку, думая, что если они сделали что-то один раз, то смогут сделать это снова. Как пра- вило, это так, но есть большая разница между пониманием того, что вы можете что- то сделать снова, и тем, чтобы иметь возможность восстановить старый исходный код, к которому вы можете мгновенно обратиться.
Вы также можете использовать программное обеспечение для управления версия- ми, которое автоматизирует копирование и хранение файлов проекта. Программ- ное обеспечение для управления версиями делает больше, чем создание «точки восстановления»; например, оно также может позволить нескольким программи- стам работать независимо друг от друга над одними и теми же файлами. Хотя опи- сание таких инструментов выходит за рамки этой книги, вам следует исследовать их в процессе своего профессионального развития.
Искусство решения задач
Узнали ли вы все методы решения задач, которые я использовал в своей программе до сих пор? У меня был план решения задачи. Как всегда, это самый важный метод. Я начал с того, что знал, при созда-
Думайте как программист 259
нии первой версии своего решения и использовал пару структур дан- ных, с которыми был очень хорошо знаком, — массивы и класс list.
Я уменьшил функциональность, чтобы упростить процесс написа- ния черновой версии и иметь возможность протестировать свой код раньше, чем я мог бы это сделать в противном случае. Я разделил задачу на операции и сделал каждую операцию отдельной функци- ей, чтобы иметь возможность работать над частями программы по отдельности. Когда я не был уверен в способе обмана, я эксперимен- тировал, что позволило мне переформулировать «обман» как «мак- симизацию размера списка слов-кандидатов», что представляло со- бой конкретную концепцию, подлежащую кодированию. В процессе кодирования операций я применил методы, аналогичные тем, кото- рые использовались в этой книге.
Мне также удавалось не расстраиваться, хотя, полагаю, тут вам придется поверить мне на слово.
Прежде чем двигаться дальше, позвольте мне пояснить, что я продемонстрировал шаги, которые предпринял я, чтобы добрать- ся до этого этапа в процессе решения данной задачи. Вы не обяза- тельно пойдете тем же путем. Приведенный выше код не является лучшим решением задачи, и он не обязательно превосходит то, что могли бы придумать вы. Надеюсь, что это продемонстрирует вам то, что любая задача, независимо от размера, может быть решена с ис- пользованием вариаций одних и тех же базовых методов, которые использовались в этой книге. Если вы столкнетесь с задачей вдвое большей, чем эта, или в 10 раз большей, то это может испытать ваше терпение, но не помешает вам ее решить.
Изучение новых навыков программирования
Есть еще одна тема, которую следует обсудить. Осваивая описан- ные в этой книге методы решения задач, вы делаете ключевой шаг на пути становления программистом. Однако, как и в случае с боль- шинством профессий, это бесконечная дорога, потому что вы всегда должны стремиться к тому, чтобы профессионально расти. Как и во всем остальном, в программировании у вас должен быть план для из- учения новых навыков и методов, не стоит рассчитывать на хаотич- ное приобретение новых знаний.
В этом разделе мы обсудим несколько областей, в которых вы, возможно, захотите приобрести новые навыки, а также некоторые систематические подходы для каждой из них. Все эти области объ- единяет необходимость применять полученные знания на практике.
Именно поэтому каждая глава этой книги заканчивается упражнени- ями — и вы их выполняете, не так ли? Чтение ресурсов, посвящен- ных новым идеям в области программирования — это важный пер- вый шаг в их изучении, но это только первый шаг. Чтобы достичь точки, в которой вы можете уверенно использовать новую технику
260 Глава 8
для решения реальной задачи, сначала вы должны опробовать эту технику на меньшей, искусственной задаче. Помните, что один из наших основных методов решения задач состоит в разбиении слож- ной задачи, либо путем ее разделения на подзадачи, либо путем ее временного уменьшения, так что каждое состояние, с которым мы имеем дело, имеет только один нетривиальный элемент. Вам не сле- дует пытаться решить нетривиальную задачу одновременно с освое- нием нового навыка, который будет центральным в вашем решении, потому что тогда ваше внимание будет разделено между двумя труд- ными задачами.
Новые языки
Я считаю, что C++ — это отличный язык программирования для соз- дания производственного кода, и в первой главе я объяснил, поче- му я думаю, что это отличный язык для изучения. Тем не менее ни один язык программирования не является лучшим для всех ситуа- ций, поэтому хорошим программистам следует изучать несколько языков.
Выделите время на учебу
По возможности вам следует выделять время на изучение нового языка, прежде чем пытаться создавать производственный код на од- ном из них. Если вы попытаетесь решить нетривиальную задачу на языке, который вы никогда раньше не использовали, вы быстро на- рушите важное правило решения задач, которое заключается в том, чтобы избегать разочарования. Поставьте себе цель изучить язык и достигните ее, прежде чем писать «реальные» программы на этом языке.
Конечно, в реальном мире мы иногда не полностью контроли- руем процесс назначения проектов. В любой момент кто-то может попросить нас написать программу на определенном языке, и этот запрос может сопровождаться крайним сроком, который помешает нам не спеша изучить язык перед решением настоящей задачи. Луч- шая защита от возникновения такой ситуации заключается в том, чтобы начать изучение других языков программирования до того, как от вас потребуется владение ими. Исследуйте языки, которые вас интересуют или которые используются в тех областях, где вы планируете работать на протяжении своей карьеры. Это еще одна ситуация, когда деятельность, которая кажется тратой времени в краткосрочной перспективе, может принести большие дивиденды в долгосрочной. Даже если окажется, что в ближайшем будущем вам не понадобится язык, который вы изучили, освоение другого язы- ка может улучшить ваши навыки работы с языками, которые вы уже знаете, поскольку это заставляет вас думать по-новому, избавляя от старых привычек и позволяя свежим взглядом посмотреть на свои навыки и методы работы. Считайте это эквивалентом перекрестно- го обучения в области программирования.
Думайте как программист
1 ... 26 27 28 29 30 31 32 33 34
261
Начните с того, что вы знаете
Когда вы начинаете изучать новый язык программирования, вы по определению ничего о нем не знаете. Тем не менее, если это не пер- вый ваш язык программирования, вы уже многое знаете о самом программировании. Поэтому хорошим первым шагом при изучении нового языка является понимание того, как код, который вы уже уме- ете писать на другом языке, может быть написан на новом языке.
Как говорилось ранее, вам следует учиться на практике, а не ограничиваться чтением. Возьмите программы, написанные вами на других языках, и перепишите их на новом языке. Систематиче- ски исследуйте отдельные элементы языка, такие как управляющие инструкции, классы, другие структуры данных и т.д. Цель состоит в том, чтобы перенести в новый язык как можно больше знаний, полу- ченных вами ранее.
Изучите отличия
Следующий шаг заключается в изучении того, что отличает новый язык. Хотя два высокоуровневых языка программирования могут иметь большое сходство, какие-то отличия должны быть, иначе не было бы причин выбирать этот язык вместо любого другого. Опять же, учитесь на практике. Например, простое чтение о том, что ин- струкция множественного выбора языка допускает диапазоны (вме- сто отдельных значений инструкции switch языка C++) не так полезно для вашего профессионального становления, как фактическое напи- сание кода, в котором осмысленно используется данное свойство.
Этот шаг, очевидно, важен для языков, которые заметно отлича- ются друг от друга, но в равной степени важен и для языков, име- ющих общего предка, таких как C++, C# и Java, которые являются объектно ориентированными потомками языка C. Синтаксические сходства могут заставить вас ошибочно полагать, что вы знаете о но- вом языке больше, чем на самом деле. Рассмотрим следующий код. integerListClass numberList;
numberList.addInteger(15);
Если бы эти строки были представлены вам как код, созданный на C++, вы бы поняли, что первая строка создает объект numberList класса integerListClass, а вторая строка вызывает метод addInteger на этом объекте. Если этот класс действительно существует и имеет метод под таким названием, который принимает параметр int, то этот код имеет смысл. Теперь предположим, я сказал вам, что этот код написан на языке Java, а не на C++. С точки зрения синтаксиса в этих двух строках нет ничего недопустимого. Однако в языке Java простое объявление переменной объекта класса фактически не создает объ- ект, поскольку объектные переменные, по сути, являются ссылками, то есть они ведут себя аналогично указателям. Для выполнения экви- валентных действий на языке Java следует использовать такой код:
262 Глава 8
integerListClass numberList = new integerListClass;
numberList.addInteger(15);
Вероятно, вы быстро осознаете эту конкретную разницу между
Java и C++, однако многие другие различия могут оказаться доволь- но тонкими. Если вы не выделите времени на их обнаружение, то они могут усложнить отладку при работе с новым языком. В процес- се сканирования своего кода ваш внутренний интерпретатор языка программирования будет предоставлять вам некорректную инфор- мацию о том, что вы читаете.
Изучайте хорошо написанный код
В этой книге я несколько раз говорил о том, что вы не должны пы- таться учиться программированию путем модификации чужого кода. Однако бывают моменты, когда изучение чужого кода жизнен- но важно. Хотя вы можете развить навыки работы с новым языком, написав серию оригинальных программ, чтобы стать мастером, вам нужно будет найти код, написанный программистом, хорошо владе- ющим этим языком.
Вы не будете «списывать» этот код; вы не будете использовать этот код для решения конкретной задачи. Вместо этого вы будете исследовать существующий код, чтобы обнаружить «лучшие прак- тики» в этом языке. Посмотрите на код, написанный экспертом, и спросите себя не только о том, что делает программист, но и почему он это делает. Если этот код сопровождается пояснениями програм- миста, еще лучше. Делайте различия между решениями, принятыми с учетом стиля, и преимуществами с точки зрения производитель- ности. Этот шаг позволит вам избежать распространенной ловушки.
Слишком часто программисты изучают лишь жизненно необходи- мые основы нового языка, в результате чего получается слабый код, который не использует всех функций этого языка. Например, если бы вам как программисту C++ было необходимо написать код на язы- ке Java, вы не довольствовались бы написанием кода на упрощенном
C++; вместо этого вы решили бы научиться писать настоящий код
Java, как это делают программисты, работающие с этим языком.
Как и в случае со всем остальным, вам необходимо применять по- лученные навыки на практике. Возьмите исходный код и измените его так, чтобы он мог сделать что-то новое. Уберите код с глаз и по- пытайтесь воспроизвести его. Цель состоит в том, чтобы познако- миться с кодом достаточно хорошо для того, чтобы суметь ответить на вопросы другого программиста об этом коде.
Важно подчеркнуть, что этот шаг происходит после других. Пре- жде чем мы доберемся до стадии изучения чужого кода, написанно- го на новом языке, мы уже изучим синтаксис и грамматику нового языка и применим навыки решения задач, освоенные при работе с другим языком, к новому языку. Если мы попытаемся ускорить про- цесс, начав изучение нового языка с изучения длинных образцов
Думайте как программист 263
программ и модификации этих образцов, существует реальный риск того, что этим наши навыки и ограничатся.
Новые навыки для языка, который вы уже знаете
Достижение этапа, на котором вы можете сказать, что «знаете» язык, не означает, что вы знаете о нем все. Даже если вы освоили синтаксис языка, всегда будут существовать новые способы комби- нирования существующих языковых особенностей для решения за- дач. Большая часть этих новых способов будет относиться к одному из компонентов, описанных в предыдущей главе, в которой мы обсу- дили процесс их освоения. Важным фактором является усилие. На- учившись решать задачи определенным образом, легко успокоить- ся на том, что вы уже знаете, и перестать расти как программист.
В этот момент вы становитесь похожим на бейсбольного питчера, который умеет совершать только прямую подачу. Некоторые питче- ры построили успешную профессиональную карьеру на одной пода- че, однако игрок, который хочет превратиться из заменяющего в на- падающего, нуждается в большем.
Чтобы максимально раскрыть свой потенциал как программи- ста, вам нужно искать новые знания и новые методы и применять их на практике. Ищите препятствия и преодолевайте их. Изучайте работу экспертов-программистов, работающих с выбранными вами языками.
Помните, что необходимость — это мать изобретения. Ищите за- дачи, которые не могут быть удовлетворительно решены с помощью вашего нынешнего набора навыков. Иногда вы можете изменить уже решенные вами задачи для постановки новых. Например, вы на- писали программу, которая отлично работает с небольшим набором данных, но что произойдет, если этот набор разрастется до гигант- ских размеров? Или вы написали программу, которая хранит свои данные на локальном жестком диске, но вы хотите, чтобы данные хранились удаленно? Что, если вам требуется несколько исполнений программы, которые могут одновременно получать и обновлять дан- ные, хранящиеся удаленно? Начиная с рабочей программы и добав- ляя новые функции, вы можете сосредоточиться только на новых аспектах программирования.
Новые библиотеки
Современные языки программирования неотделимы от своих ос- новных библиотек. Например, в процессе изучения языка C++ вы не- избежно что-то узнаете о стандартных библиотеках шаблонов, а при изучении Java — о стандартных классах этого языка. Однако поми- мо библиотек, поставляемых вместе с языком, вам необходимо изу- чать сторонние библиотеки. Иногда они представляют собой общие каркасы приложений вроде .NET Framework компании Microsoft, которые могут использоваться с несколькими высокоуровневыми