Как научиться решать задачи по программированию
Перейти к содержимому

Как научиться решать задачи по программированию

  • автор:

Как решить любую задачу: пособие для разработчика

Каждый разработчик рано или поздно получает задачу, которую просто не понимает. Разбираемся, как преодолеть пропасть между требованиями и знаниями.

Как решить любую задачу: пособие для разработчика

Неважно, как долго вы работаете и как много у вас опыта. Даже разработчик с десятилетним стажем имеет все шансы встретиться с непонятной задачей. Как же быть? Просто начать и верить, что по ходу дела все станет яснее? Или немедленно сообщить начальнику, что вы не понимаете, что от вас хотят?

Оба ответа неправильные.

В разработке ПО (и любой другой профессии) подобные проблемы возникают постоянно. И это неплохо! Поверьте, вы способны не только решить их, но и извлечь из этого пользу, расширив свои знания и навыки.

О «требованиях»

В разных компаниях и проектах требования могут быть разными. Крупные компании обычно тщательно их прописывают, а маленькие могут иногда вообще не составлять.

В конечном счете наша цель как разработчиков – решать проблемы. Каким бы способом вы ни получили задачу, чтобы решить ее, вы должны ее понять.

О шагах

Ниже приведена последовательность шагов для решения сложных задач. Не всегда нужно выполнять каждый из них. Многое может пойти не по плану и вам придется искать ответы у коллег, аналитиков, руководителей, а возможно, и родственников. Этот процесс сбора и обобщения знаний из разных источников абсолютно нормален и поможет вам достигнуть лучшего результата.

И прежде чем мы перейдем к самим шагам, последнее предупреждение: не формализируйте их. Основная цель статьи – научить вас быстрее понимать проблему, а не создавать лишние барьеры и волокиту.

Первый шаг: анализ задачи

На этом шаге нужно попытаться осознать, что вам нужно сделать. Не как, а именно что.

Это важное уточнение. Иногда сразу переходить к воплощению, не продумав все последствия и не осознав конечную цель, может быть опасно.

Классифицируйте задачу

Определите, к какому типу относится работа, которую вам нужно выполнить. Это может быть:

  • правка ошибок;
  • внедрение новой функциональности;
  • создание нового приложения;
  • исследование проблемы;
  • улучшение производительности.

Это не все возможные типы задач, но теперь вы понимаете, о чем идет речь.

Основная цель на этом этапе – определить вид работы. Это очень важно, поскольку будет напрямую влиять на ваши дальнейшие действия.

Особую важность имеет первый шаг в том случае, если требования составлены нечетко, вроде «нам нужен способ очистить кэш клиентов после обновления веб-сайта». Это можно интерпретировать по-разному:

  1. Необходимо немедленно реализовать некий механизм очистки кэша, чтобы пользователи всегда видели последние обновления.
  2. Нужно изучить все возможные способы хранения кэша и определить лучшие методы для его очистки после внедрения обновлений.
  3. Кэш пользователей уже должен был быть очищен, а вам требуется найти и исправить ошибку, которая этому препятствует.

Если вы не совсем уверены, что скрывается за этими требованиями, следует попросить разъяснений, прежде чем продолжать.

Сформулируйте задачу в паре простых фраз

Обобщите требования в одном или двух предложениях. Возможно, это будет не очень просто, но у вас обязательно получится.

Если вы не можете это сделать, значит, нужно разделить задачу. Таким образом, этот шаг является лакмусовой бумажкой для определения правильной организации рабочего процесса. Если задача слишком велика, нужно решать ее по частям.

Хороший пример обобщения: «при обновлении мы добавляем файлам уникальный идентификатор, чтобы браузер знал, что нужно использовать самый свежий код». Тест на простоту пройден, задача не нуждается в делении.

А пример плохой постановки задачи. «При обновлении мы добавляем файлам уникальный идентификатор, чтобы браузер знал, что нужно использовать самый свежий код. Также мы должны отправить сообщение об обновлении в CDN. Также следует обновить приложение в app store. Также…».

Это точно провал. Для решения этой задачи требуется слишком много действий, поэтому ее, вероятно, следует разделить и отслеживать каждую подзадачу отдельно.

Выделите основные части

Составьте список основных дел в любой удобной форме. Ограничьтесь очень простыми общими описаниями для каждого крупного этапа, а не составляйте подробное руководство. Лучше всего записать все ваши идеи.

Помните, что сейчас вы все еще анализируете задачу, а не решаете ее.

Пример с кэшированием слишком прост и не нуждается в схеме, поэтому мы введем более сложную задачу по созданию новой фичи: «Необходимо показывать целевое рекламное объявление, адаптированное для каждого пользователя на основании собранных данных».

Наметим основные части:

  • Текущие объявления следует разделить так, чтобы они могли соотноситься с какой-то конкретной метрикой пользователя.
  • Для маркетинговой команды нужен удобный способ сопоставить новые рекламные объявления с пользовательскими данными.
  • Система должна агрегировать все показатели, которые имеют отношение к рекламе.
  • Наконец, нужно каким-то образом получать идентификатор пользователя и выводить объявления.

Теперь этот список можно использовать для быстрого обсуждения с командой или руководителем, после которого могут быть внесены новые пункты, например:

  • Нужно добавить для пользователя возможность сообщить о том, что он больше не хочет видеть конкретные объявления.Вы же не хотите раздражать ваших любимых клиентов!

Вот так, немного подумав над задачей и составив план, вы сэкономили часы или даже дни разработки и избежали множества проблем.

Возможно, вы считаете, что вся эта работа должна быть выполнена еще до того, как разработчик получит требования – и это на 100% верно!

Однако мы, к сожалению, не живем в идеальном мире, и требования не всегда полностью конкретизируются до того, как попадут к разработчику. Поэтому нужно сделать все возможное, чтобы правильно оценить задачу.

Определите проблему, которую вы пытаетесь решить

Определитесь, какую существующую или предполагаемую проблему реального мира вы пытаетесь решить.

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

Если вы не можете ответить на вопрос, вероятно, следует спросить кого-то, зачем вы решаете эту задачу. Либо вы получите более четкое представление, либо еще раз подумаете о том, что необходимо сделать.

Более глубокое понимание проблемы и цели позволит вам принять лучшие решения при реализации и избежать бесполезных усилий.

Второй шаг: разбор и оценка требований

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

Уточните все термины, связанные с задачей

Вероятность встретить незнакомые понятия гораздо выше, если вы новичок или являетесь сотрудником большой компании.

Речь идет и о бизнес-терминах, например, продуктах, клиентах или процессах, и о понятиях разработки, таких как названия инструментов, приложений, служб или библиотек.

Возможно, вы знаете, что значит создать доступ к агрегированным данным о пользователе, а как насчет ее добавления в DAO? А имеете ли вы представление о том, что такое MADF в форматировании рекламных данных?

Если вы не понимаете важные термины, то общий смысл задачи может от вас ускользнуть и пострадает реализация.

Определите, как должна быть выполнена задача

Теперь можно начинать думать, как выполнить задачу. В зависимости от проекта этот этап может сильно различаться. Вам могут просто рассказать, какую функциональность нужно получить в итоге, или подробно описать каждый шаг, который следует сделать. Чаще всего встречается нечто среднее.

Если вы получили инструкции, то обязательно ознакомитесь с ними и оцените их. Это кажется очевидным, но обратите внимание, в какой именно момент мы переходим к этому шагу.

Вы могли бы погрузиться в технические детали реализации прежде, чем поняли суть и цель задачи. Но вы сделали все правильно и теперь можете объективно оценить каждую инструкцию.

Определите, решены ли проблемы

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

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

Если ответ положительный, начинайте работать. В ином случае для разрешения конфликтов необходимо перейти к третьему шагу.

Третий шаг: критическое мышление

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

Чтобы создать самый лучший продукт, мы обязаны отсекать недостатки и бессмысленные вещи, но прежде чем сказать коллегам о том, что конкретный подход неправильный, нужно объяснить почему это так.

Сформулируем некоторые основные правила о разногласиях.

Когда не соглашаться

  • Не возражайте, пока не поймете полностью.

Вы хорошо понимаете, с чем именно несогласны? Если у вас нет всех необходимых данных, самое время вернуться к предыдущим шагам.

  • Не спорьте по субъективным вопросам. Сосредоточьтесь на реальных возможных проблемах.

«Мне не нравится» – это субъективно. “Большое количество операций плохо повлияет на производительность» – это объективно.

  • Подготовьте аргументированное объяснение вашего несогласия.

Вы не способны объяснить, почему это неправильно? А вы уверены, что это действительно неправильно?

Прежде чем вы не согласитесь, внимательно выслушайте коллег и постарайтесь понять их аргументы. Может быть, вы что-то упускаете.

Как не соглашаться

Чтобы ваше несогласие было объективным, оно должно ясно демонстрировать, что принятое решение:

  • основано на неполной информации, не учитывает существующих условий и данных;
  • основано на ложной или ошибочной информации;
  • нелогично или ломает существующую функциональность. Также нелогичной может быть сама исходная проблема;
  • не решает проблему полностью. Иногда это именно то, что требуется. В разработке ПО мы часто начинаем с создания минимально жизнеспособного продукта, лишенного некоторой второстепенной функциональности. Но порой решение не дотягивает даже до этого уровня.

Подведем итоги

Запомните самое важное: не формализируйте этот процесс. На практике он гораздо быстрее и проще, чем в теории. Просто записывайте ваши мысли в заметки и при необходимости поговорите пару раз с коллегами, чтобы уточнить все тонкости задачи и реализации. Вот и все!

Вот упрощенный список шагов:

Шаг 1. Анализ

  • Классификация задачи;
  • Резюме в паре предложений;
  • Выделение основных частей;
  • Определение проблемы.

Шаг 2. Разбор и оценка

  • Уточнение терминов;
  • Составление общего плана реализации;
  • Проверка, решает ли этот план проблему.

Шаг 3. Критическое мышление

  • Знайте, когда не соглашаться;
  • Знайте, как не соглашаться.

Используете ли вы эти приемы? А может быть, у вас есть своя система для понимания сложных задач? Расскажите о ней в комментариях.

10 шагов по решению задач в программировании

Это сборник советов для разработчиков-новичков, которые смотрят на пустой экран и не знают, с чего начать. Нередко можно услышать от молодых разработчиков, работающих над решением каких-то задач в программировании, что они не уверены, за что нужно хвататься. Ты понимаешь саму задачу, логику, основы синтаксиса и так далее. Если ты видишь чей-то код, или тебе кто-то помогает, то можно всё сделать самому. Но бывает, что ты не уверен в своих силах, или поначалу тебе трудно реализовать свои мысли в коде, несмотря на то, что ты знаешь синтаксис и логику. Под катом — несколько советов по решению этой проблемы, которые помогут вам в повседневной работе.

1. Прочитайте условия задачи как минимум трижды (или хотя бы столько раз, сколько вам будет удобно)

Вы не сможете решить задачу, если не поймёте её. Есть разница между задачей и задачей, которую, как вам кажется, вы решаете. Можно прочитать первые несколько строк, а насчёт остального выстроить предположения, поскольку всё выглядит похожим на то, с чем вы сталкивались раньше. Даже если вы делаете популярную игру наподобие Виселицы, удостоверьтесь, что прочитали все её правила, пусть даже вы играли в неё раньше.

Иногда можно попробовать объяснить задачу другу и посмотреть, поймёт ли он ваше объяснение. Вы же не хотите пройти половину пути и обнаружить, что неправильно поняли требования. Так что лучше потратить в начале больше времени, чтобы всё прояснить. Чем лучше поймёте задачу, тем легче будет её решить.

Допустим, мы создаём простую функцию selectEvenNumbers , которая берёт массив чисел и возвращает массив evenNumbers с одними лишь чётными числами. Если чётных чисел в исходном массиве нет, то массив evenNumbers возвращается пустым.

Какие вопросы можно себе задать:

  • Как компьютер может сказать, что число является чётным? Разделить на 2 и проверить, чтобы результат получился целым.
  • Что я передаю этой функции? Массив.
  • Что содержит этот массив? Одно или несколько чисел.
  • Какие типы данных у элементов массива? Числа.
  • Какова цель этой функции? Что я возвращаю в конце её выполнения? Цель — получить все чётные числа и вернуть их в массиве. Если нет чётных чисел, то массив возвращается пустым.

2. Пройдите по задаче вручную как минимум с тремя наборами данных

Возьмите лист бумаги и пройдите по задаче вручную. Выберите не менее трёх наборов данных для проверки. Выберите предельно допустимые и крайние случаи.

Предельно допустимые случаи: проблема или ситуация, возникающая за пределами нормальных параметров функционирования. Например, когда одновременно несколько переменных или состояний среды имеют экстремальные значения, даже если каждый из параметров находится в своём специфическом диапазоне.

Крайние случаи: проблемы или ситуации, возникающие только при экстремальных (минимальных или максимальных) значениях параметров функционирования.

Вот, к примеру, несколько наборов данных для использования:

[1]
[1, 2]
[1, 2, 3, 4, 5, 6]
[-200.25]
[-800.1, 2000, 3.1, -1000.25, 42, 600]

Когда только начинаешь, то зачастую пренебрегаешь какими-то шагами. Поскольку наш мозг уже знаком с чётными числами, то можно просто посмотреть на набор чисел и сразу передать в массив 2, 4, 6 и так далее, не думая о том, как наш мозг выбирает конкретные числа. Если вы замечаете это за собой, то лучше взять большой набор данных, чтобы помешать мозгу решать задачу, просто глядя на числа. Это поможет придерживаться настоящего алгоритма.

Давайте пройдём по массиву [1]

  1. Смотрим на единственный элемент массива [1] .
  2. Определяем, является ли он чётным. Не является.
  3. Замечаем, что других элементов в массиве нет.
  4. Определяем, что здесь нет чётных чисел.
  5. Возвращаем пустой массив.
  1. Смотрим на первый элемент массива [1, 2]
  2. Это 1 .
  3. Определяем, является ли он чётным. Не является.
  4. Смотрим на следующий элемент.
  5. Это 2 .
  6. Определяем, является ли он чётным. Является.
  7. Создаём массив evenNumbers и добавляем в него 2 .
  8. Замечаем, что других элементов в массиве нет.
  9. Возвращаем массив evenNumbers — [2] .

3. Упрощайте и оптимизируйте свой алгоритм

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

  1. Создадим функцию selectEvenNumbers .
  2. Создадим новый пустой массив evenNumbers для хранения чётных чисел.
  3. Проходим по каждому элементу массива [1, 2] .
  4. Находим первый элемент.
  5. Делим его на 2 и определяем, чётный ли он. Если да, то прибавляем к evenNumbers .
  6. Находим следующий элемент.
  7. Повторяем шаг №4.
  8. Повторяем шаги №5 и №4, пока не кончатся элементы в массиве.
  9. Возвращаем массив evenNumbers вне зависимости от того, есть ли в нём что-то.
  1. Доказываем истинность для n = 1 , n = 2 , .
  2. Предполагаем, что будет истинно для n = k .
  3. Доказываем истинность для n = k + 1 .

4. Пишите псевдокод

После проработки основных шагов напишите псевдокод, который можно перевести в настоящий код. Это поможет определить структуру кода, да и вообще облегчит его написание. Пишите псевдокод построчно. Это можно делать на бумаге или в виде комментариев в редакторе. Если вы только начинаете и считаете, что пустой экран выглядит жутковато или отвлекает, то лучше писать на бумаге.

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

Применительно к нашему случаю есть много разных вариантов. Например, можно использовать filter , но ради простоты примера воспользуемся простым циклом for (однако при последующем рефакторинге мы ещё столкнёмся с filter ).

Вот пример псевдокода, в основном состоящего из слов:

А вот псевдокод, в котором слов гораздо меньше:

Главное, писать код построчно и понимать логику каждой строки.

Вернитесь к задаче, чтобы удостовериться, что вы на правильном пути.

5. Преобразуйте псевдокод в нормальный код и отладьте его

Когда ваш псевдокод будет готов, преобразуйте каждую строку в реальный код на вашем языке. Здесь мы воспользуемся JavaScript.

Если вы писали на бумаге, то перенесите всё в редактор в виде комментариев, а затем замените каждую строку.

Теперь вызовите функцию и дайте ей некоторые из ранее использованных наборов данных. Так можно проверить, возвращает ли код нужный результат. Также можете написать тесты для проверки соответствия выходных данных ожидаемому результату.

После каждой переменной или строки можно использовать console.log() . Это поможет проверить, ведут ли себя значения и код так, как ожидается, прежде чем двигаться дальше. Таким образом вы выловите любые проблемы, не зайдя слишком далеко. Вот пример того, какие значения можно проверить при начале работы.

Ниже приведён код, полученный после обработки каждой строки псевдокода. Символы // обозначают строки из псевдокода. Жирным выделен реальный код на JavaScript.

Уберём псевдокод, чтобы не путаться.

Иногда разработчики-новички настолько увлекаются синтаксисом, что им трудно идти дальше. Помните, что со временем вам будет проще соблюдать синтаксис, и нет ничего стыдного в том, чтобы потом при написании кода обращаться к справочным материалам для корректного соблюдения синтаксиса.

6. Упрощайте и оптимизируйте код

Возможно, вы заметили, что упрощение и оптимизация — это повторяющиеся темы.

Эдсгер Дейкстра, нидерландский учёный и один из первопроходцев в ряде областей информатики

В нашем примере одним из путей оптимизации будет фильтрация элементов в массиве посредством возвращения нового массива с помощью filter . В этом случае нам не нужно определять переменную evenNumbers , потому что filter вернёт новый массив с копиями элементов, которые соответствуют фильтру. При этом исходный массив не изменится. Также нам не нужно использовать цикл for . filter пройдёт по каждому элементу, и если вернёт true, то элемент попадёт в массив, а если false , то будет пропущен.

Может потребоваться несколько итераций упрощения и оптимизации кода, по мере того как вы будете находить новые способы.

Задавайте себе такие вопросы:

  • Каковы цели упрощения и оптимизации? Цели зависят от принятого в вашей команде стиля или ваших личных предпочтений. Вы пытаетесь максимально уплотнить код? Или вы хотите сделать его более читабельным? В таком случае вы можете добавить отдельные строки с определением переменной или вычислением чего-либо, а не пытаться всё сделать в одной строке.
  • Как ещё можно сделать код более читабельным?
  • Можно ли ещё уменьшить количество шагов?
  • Есть ли переменные или функции, которые не нужны или не используются?
  • Какие-то шаги повторяются? Посмотрите, можно ли определить в другой функции.
  • Существуют ли более эффективные способы обработки крайних случаев?

Джеральд Сассман и Гарольд Абельсон, авторы “Structure and Interpretation of Computer Programs”

7. Отлаживайте

Этот шаг необходимо выполнять в ходе всего процесса. Сквозная отладка поможет раньше выловить любые ошибки синтаксиса или недостатки логики. Воспользуйтесь преимуществами своего IDE (Integrated Development Environment) и отладчика. При обнаружении бага рекомендуется просматривать код построчно, стараясь найти неожиданные вещи. Несколько советов:

  • Читайте, что пишется в сообщения об ошибках в консоли. Иногда в них указываются номера строк для проверки. Это даст вам исходную точку для поисков, хотя иногда проблемы могут скрываться в других строках.
  • Комментируйте блоки кода или строки, а также выходные данные, чтобы быстро подмечать поведение кода. При необходимости код можно всегда раскомментировать.
  • Используйте другие образцы данных, если возникают непредусмотренные сценарии.
  • Сохраняйте разные версии файла, если применяете другие подходы. Не стоит терять наработанное, если захочется откатиться к прежнему решению!

Брайан Керниган, профессор информатики в Принстонском университете

8. Пишите полезные комментарии

Через месяц вы можете и не вспомнить, что означает каждая строка кода. А тот, кто будет работать с вашим кодом, вообще этого не знает. Поэтому важно писать полезные комментарии, чтобы избежать проблем и сэкономить впоследствии время, когда придётся снова вернуться к этому коду.

Избегайте таких комментариев:

// Это массив. Итерируем его.
// Это переменная.

Старайтесь писать короткие, высокоуровневые комментарии, которые помогут понять, что тут происходит, если это не очевидно. Это пригодится при решении сложных задач, вы сможете быстро понять, что делает конкретная функция, и почему. Благодаря использованию понятных комментариев и имён переменных и функций вы (и другие люди) сможете понять:

  • Для чего этот код?
  • Что он делает?

9. Получайте отзывы посредством ревизии кода

Получайте отзывы от коллег, руководителей и других разработчиков. Читайте Stack Overflow. Смотрите, как другие решали аналогичные задачи и учитесь у них. Нередко бывает несколько способов решения задачи. Узнайте, что они собой представляют, и вам будет быстрее и проще приходить к ним самостоятельно.

Дядя Боб Мартин, программный инженер и соавтор манифеста Agile

10. Практикуйтесь, практикуйтесь, практикуйтесь

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

Как решать задачи на программирование

Перевод статьи «How to Solve Coding Problems with a Simple Four Step Method».

Photo by Cécile Brasseur on Unsplash

Я потратила два месяца на подготовку к своему первому техническому собеседованию и думала, что уже готова. Но когда подошла к нему уже вплотную, внезапно меня озарило: я ж понятия не имею, как решать задачи на программирование.

Ни в одном туториале из тех, по которым я училась программированию, не рассказывалось о решении задач. А мне определенно нужно было овладеть этим искусством: от этого зависела вся моя будущая карьера разработчика. И я немедленно взялась за поиски.

То, что я нашла, было не просто подходом, а бесценной стратегией. Это был проверенный временем алгоритм действий в четыре шага, который по какой-то причине остался незамеченным в сфере разработки.

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

Решение задач — это не только часть процедуры собеседований. Это то, чем разработчики занимаются ежедневно. В конечном итоге, само написание кода — это и есть решение задач.

Универсальный подход к решению задач

Этот метод изложен в книге «Как решать задачу» Дьёрдя Пойа. Первое издание вышло еще в 1945 году, было продано больше миллиона экземпляров. (На русском языке книга публиковалась как пособие для учителей еще в 1959 году. — Прим. перев.).

Метод Пойа используют многие программисты, от профессоров информатики (см. курс «Intro to CS» на Udacity, который ведет профессор Дэвид Эванс) до преподавателей современной веб-разработки вроде Кольта Стила.

Давайте пройдемся по решению простой задачи на программирование с применением метода Пойа. Это позволит увидеть работу метода на практике и в результате лучше разобраться в нем. Для примера будем использовать язык JavaScript.

Создайте функцию, которая будет складывать два числа и возвращать их сумму.

Вот четыре шага по решению любой задачи:

  1. Понять задачу.
  2. Разработать план.
  3. Осуществить план.
  4. Оглянуться назад.

«Во-первых, мы должны понять задачу; мы должны ясно видеть, что в ней является искомым. Во-вторых, мы должны усмотреть, как связаны друг с другом различные элементы задачи, как неизвестное связано с данными. Это необходимо, чтобы получить представление о решении, чтобы составить план. В-третьих, мы осуществляем наш план. В-четвертых, оглядываясь назад на полученное решение, мы вновь изучаем и анализируем его», — «Как решать задачу», 1959 г.

https://amdy.su/wp-admin/options-general.php?page=ad-inserter.php#tab-8

Давайте сделаем первый шаг.

Photo by Dimitri Houtteman on Unsplash

Шаг 1: понять задачу

Когда вы на собеседовании получаете задачу на программирование, возникает соблазн тут же приступить к написанию кода. Этому искушению трудно противостоять, особенно, если время ограничено.

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

Прочтите текст задачи. Можно даже читать вслух, если это поможет вам притормозить.

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

Этот первый шаг жизненно важен. Мы часто не уделяем достаточного количества времени на то, чтобы разобраться в постановке задачи. А если вы не до конца понимаете задачу, вам будет куда труднее ее решить.

Чтобы помочь себе разобраться, спросите себя:

Каковы здесь входящие данные?

Какого рода input-ы следует ожидать? В нашем примере это аргументы, принимаемые функцией.

Читая текст задачи, мы понимаем, что входящими данными будут числа. Если подходить к делу более скрупулезно, мы можем спросить:

  • всегда ли будет только два числа?
  • что будет, если функция получит в качестве входящих данных три числа?

Эти вопросы мы можем задать интервьюеру или поискать ответ в описании задачи. (К задаче может прилагаться примечание типа «Исходите из того, что в функцию всегда будут передаваться только два числа». Конечно, это сразу все прояснит).

Можно задаться и другими вопросами. Всегда ли входящими данными будут числа? Что должна делать функция, если получит в качестве аргументов «a» и «b»? Уточните, всегда ли input будет числовым.

В качестве варианта — можно записать вероятные входящие данные в комментарии к коду, чтобы иметь представление о том, как они должны выглядеть.

Далее следует спросить себя:

Каковы должны быть результаты?

Что функция будет возвращать? В нашем случае это должно быть оно число — сумма двух чисел, переданных в качестве аргументов. Убедитесь, что вы хорошо понимаете, каким должен быть output.

Придумайте простые примеры

Разобравшись в сути задачи и зная вероятные input-ы и output-ы, можно начать работать над конкретными примерами.

Примеры также могут использоваться для тестирования вашего решения. На техническом собеседовании или при подготовке к нему на сайтах вроде Codewars или HackerRank используются специальные редакторы. Большинство из них содержат уже готовые примеры или test cases. Несмотря на это, написание собственных примеров может помочь вам упрочить понимание задачи.

Начните с написания одного-двух простых примеров.

Давайте вернемся к нашей складывающей функции. Назовем ее «add».

Каким может быть input? Ну, допустим, таким:

Каким будет результат при таких входящих данных? Записать это можно так:

Это показывает, что наша функция принимает в качестве input 2 и 3, а как output возвращает 5.

Photo by Jason Leung on Unsplash

Придумайте сложные примеры

Продумывая более сложные примеры, вы можете уделить время поиску крайних случаев, которые стоит учесть.

Например, что будет, если наши входящие данные будут не числами, а строками? Что, если мы получим в качестве аргументов две строки, например, add(‘a’, ‘b’)?

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

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

Если вы не на собеседовании, а просто решаете задачу, в ней может быть обозначено, что должно происходить при вводе невалидных данных.

Например, в задаче может говориться «При отсутствии input-а верните undefined». В таком случае можно написать комментарий:

В нашем случае мы будем исходить из того, что input всегда будет числовым. Но в общем всегда лучше подумать о крайних случаях.

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

Прежде чем перейти ко второму шагу, давайте кратко повторим, что нужно сделать на первом шаге:

  • прочитать задачу
  • выяснить, какими будут входящие данные
  • выяснить, каким должен быть результат
  • создать простые примеры, затем — более сложные.

Шаг 2: разработать план решения задачи

Далее нужно составить план того, как вы, собственно, собираетесь решать задачу. Придумав план, запишите его в виде псевдокода.

Псевдокод — это изложенные простым языком шаги алгоритма. Иными словами, это пошаговый план решения задачи.

Опишите каждый этап решения. Если задача сложная, этапов может быть много. Если говорить о нашей задаче, мы можем написать:

Теперь у вас есть четырехэтапный план решения задачи.

Для более сложных задач профессор Эванс советует: «Полагайтесь на то, как задачи решаются людьми». То есть, забудьте на мгновение о том, как задачу может решить код, и подумайте о том, как ее решали бы вы — человек. Это может помочь вам более четко увидеть нужные шаги.

Photo by Chewy on Unsplash

3. Шаг 3: осуществить план (решить задачу!)

Следующий шаг — собственно решение задачи. Руководствуясь псевдокодом, напишите настоящий код.

Профессор Эванс рекомендует сфокусироваться на простом, механическом решении. Чем проще и легче ваше решение, тем вероятнее, что вы напишете код правильно.

Если взять наш псевдокод, мы можем написать следующее:

Помните, что не следует преждевременно оптимизировать код. Написав решение, вы можете подумать, что ваш код неэффективный, и захотеть его улучшить. Но для начала создайте простое, механическое решение.

Что, если вы не можете решить задачу полностью? Например, если вы так и не нашли решения для какой-то ее части? Кольт Стил советует в таких ситуациях игнорировать эту сложную часть. Сконцентрируйтесь на всем остальном — том, что вы уже можете написать. Когда напишете, возвращайтесь к части, которая вызвала у вас затруднения.

Шаг 4: оглянуться назад

Когда у вас на руках будет готовое рабочее решение, подумайте, как его можно улучшить. На этом этапе можно провести рефакторинг и преобразовать свое решение в более эффективное.

Просматривая свою работу, вы можете задать себе несколько вопросов:

  • Можно ли получить этот результат как-то иначе? Какие еще подходы есть?
  • Понятно ли это решение с первого взгляда?
  • Можно ли использовать результат или метод для какой-то другой задачи?
  • Можно ли улучшить производительность решения?
  • Не приходят ли вам на ум какие-то способы рефакторинга для этого решения?
  • Как эту задачу решают другие люди?

Возвращаясь к нашему коду, мы можем сделать его более лаконичным, если удалим нашу переменную и используем имплицитный return:

Дойдя до четвертого шага, вы понимаете, что можете никогда не довести свое решение до совершенства. Даже великие разработчики пишут код, который впоследствии хотят изменить. Так что вопросы из приведенного списка — лишь направляющие, которые могут вам помочь.

Если вы решаете задачу на собеседовании, то для четвертого шага у вас может просто не остаться времени. Впрочем, если время есть, попробуйте улучшить свое решение. Ну а если вы просто решаете какую-нибудь задачу, обязательно проходите все описанные шаги.

Когда я практиковалась в решении задач, я практически всегда смотрела и чужие решения, которые часто были элегантнее и эффективнее моих.

Итоги

В этой статье мы рассмотрели четырехэтапную стратегию решения задач на программирование. Давайте повторим:

  • Шаг 1: понять задачу
  • Шаг2: создать пошаговый план решения
  • Шаг 3: реализовать план и написать код решения
  • Шаг 4: оглянуться назад и по возможности улучшить решение.

Применение этого подхода очень помогло мне при прохождении технических собеседований, да и вообще в моей работе.

Если вы не чувствуете себя уверенно, решая задачи, помните, что умение решать задачи — это навык. Чем больше практики, тем лучше вам это будет удаваться.

Как Решать Задачи По Программированию

Наверное, у многих новичков бывало такое, когда ты смотришь решение какой-то задачи в Интернете или на уроке, читаешь чужой код и кажется что всё максимально просто и понятно. Но вот приступаю к решению такой же задачи самостоятельно, впадаешь в ступор. Что делать, с чего начать и т. д. Хотя казалась бы было всё было понятно. Знакомое чувство? С ним сталкивается каждый, кто только начинает изучать программирование. Поэтому сегодня я расскажу как решать зачади по программированию на языке Python, да и любом другом языке. Меня зовут Макс. Я один из авторов YouTube-канала PyLounge. Поехали!

Во-первых, ты должен чётко понимать, что от тебя требуется.

Как нельзя лучше сделать это тебе поможет декомпозиция. Декомпозиция — это когда ты разбиваешь одну большую сложную задачу на несколько мелких простых задача. При этом в момент дробления задачи не пытайся думать о программной реализации и программировании вообще. Старайся думать как ТЫ бы решал эту задачу, ты сам, будучи человеком. Опиши свои человеческие действия на каждом этапе. Перед этим вдумчиво прочитай текст задачи. Можно даже читать вслух. Часто проговаривание вслух помогает восприятию. И начинай декомпозировать с человеческой точки зрения.

Допустим перед нами стоит задача:

Файл имеет следующий вид:

Начинаем смотреть на задачу глазами обычного человека. Вот у меня на Рабочем столе лежит текстовый файл. Чтобы мне прочитать содержимое файла его надо открыть. Соответственно это и есть первый пункт наше алгоритма — Открыть файл.

Далее я глазами вижу первую строчку файла. Здесь через пробел записаны два слова. Так как в файле записаны настройки графики то, очевидно, в каждой строчке хранится пара «название опции — её значение». Например, Разрешение 1920×1080. Соответственно я, будучи человеком, мысленно читаю первую строчку. Дальше мне нужно отделить «ключ настройки» от её значения. Как я будучи человеком понимаю, что в этой строчке два разных слова? Как я понимаю, что здесь не одно слово? Слова разделены между собой пробелами. Значит слово до пробела ключ, после значение. Это и есть следующий этап нашего алгоритма — читаем строку и делим её, за разделитель принимаем символ пробела. Затем первую половину строки заносим в ключ, вторую в значение.

Далее я будучи человеком, опускаю взгляд на следующую строку и повторяю описанное выше действие. Потом следующую и следующую. До тех пор, пока в файле не закончатся строки. Это и есть следующий шаг — по очереди обрабатывать каждую строчку из файла.

Теперь у меня в голове (или в переменной типа dict (словарь)) имеется вся нужная информация. По аналогии с функцией мы возвращаем словарь с данными. Потом я закрываю файл. Вот у нас и есть по сути готовый алгоритм:

1. Открыть файл.
2. Читаем строку из файла.
3. Делим её на две части через символ пробела.
4. Записываем первую часть строки в «ключ», а второю в «значение ключа» (например, ключ vsync — значение ключа off).
5. Аналогичным образом по очереди обрабатываем каждую следующую строку. Пока строчки в файл не кончатся.
6. Закрываем файл.
7. Сообщаем результат.

Можно для себя набросать псевдокод. То есть описание каждого шага алгоритма не на конкретно языке программирования, а абстрактно, на русском. Также неплохим вариантом может являться нарисовать блок-схему.

Псевдокод — это изложенные простым языком шаги алгоритма. Иными словами, это пошаговый план решения задачи. Если вы и так чётко понимаете процесс или задача относительно простая, то псевдокод и схему можно пропустить.

Теперь когда у нас имеется алгоритм, надо задать себе два вопроса:

1. Что мы имеем на вход (входящие данные).
2. Что мы имеем на выходе.

Снова смотри с позиции обычного человека. Чтобы я, будучи человеком, смог открыть какой-то файл и прочитать его, я должен знать где он лежит. Значит на вход мы и наша функция получает путь до файла.

Также в условии уже сказано, что мы должны сформировать словарь настроек. Значит словарь с ключами и значениями (куда сохраняются данные из файла) мы и должны вернуть из функции (вывести результат).

Теперь пытаемся переложить это на программный код. Также построчно всё проговаривая:

Очень важно давать осмысленные и понятные названия переменным и функция. Это помогает делать код более читаемым и ты сам не запутаешься, если откроешь его спустя неделю. Рекомендую посмотреть наше видео «4 Совета Которые Помогут Сделать Твой Код Лучше».

Также стоит подумать, какие ошибки у нас могут возникнуть. Опять таки, задаём себе вопросы, например:

1. Что делать если файл не будет существовать?

Надо сделать проверку, добавить обработку такой ситуации.

2. Что случится если структура файла будет другой?
3. Кодировка файла будет нестандартной?
Нужно придумать как можно больше таких вопросов. И ответить на каждый из них, реализовав программное решение с помощью исключений, сообщений об ошибках и т.д.

Теперь когда у нас есть рабочее решение на руках, надо подумать, как его можно улучшить. То есть надо провести рефакторинг кода и сделать решение более эффективным.

Рефакторинг — это процесс улучшения кода, когда мы переписываем какие-то участки кода, которые можно сделать лучше.

При рефакторинге можно задавать себе следующие вопросы:
1. Можно ли получить этот результат как-то иначе? Какие еще подходы есть?
2. Понятно ли это решение с первого взгляда?
3. Можно ли использовать результат или метод для какой-то другой задачи?
4. Можно ли улучшить производительность решения?
5. Как эту задачу решают другие люди?

Никогда не пренебрегай внутренним диалогом с собой. Составляй план, блок-схему и т. д. Многие люди не делают этого, им кажется, что это бесполезная трата времени. Но на самом деле именно в этом и кроется ключевой момент в решении любой задачи.

В нашем случае мы могли бы работать с файлом через менеджер контекста, чтобы явно не вызывать функцию close () и метод readlines (). И применить к значением настроек функцию strip (), что бы не оставлять в строке символы переноса, табуляции и т. д. В случае с Python можно ещё и аннотации типов добавить:

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

Я в своё время очень часто смотрел чужие решения, сравнивал их со своим и это сильно мне помогло. Главное не смотреть ответ сразу) А именно сравнивать своё решение с чужим, если сам ну никак не можешь сделать.

Я привел пример простой задачи. Но более сложная задача будет отличаться лишь количеством этапов и инструментами, которые будет необходимо применить.

Запомните, умение решать задачи — это навык. Чем больше практики, тем лучше вам это будет удаваться. Если вы хотите много раз подтягиваться, то надо подтягиваться. Если вы хотите научиться быстро бегать, то надо бегать. Если вы хотите научиться решать задачи по программированию, то надо решать задачи. Истина всегда на поверхности.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *