Частью культуры B&G является наставничество людей, плохо знакомых с разработкой программного обеспечения, и это всегда возвращает меня к моим дням в колледже, когда ничто не имело смысла и не работало так, как должно было бы. Я мог видеть это тогда во мне и в моих друзьях, и я мог видеть это в студентах, которым я помогал как ТА. Мне потребовалось много практики, чтобы перестать слепо использовать вещи, упомянутые преподавателями в классе, и начать искать в своем наборе инструментов нужный инструмент, основанный на реальном понимании проблемы и решения.
Этот процесс не уникален ни для меня, ни для разработки программного обеспечения. Было много разговоров о правиле 10 000 часов Малкольма Гладуэлла и его целенаправленной практике. Есть также куча книг, посвященных пути от новичка до мастера. Путешествие требует времени и много борьбы с мелочами, пока в конце концов все не начнет щелкать.
Хотя я не могу ускорить этот процесс для вас, я могу дать совет, которому, я надеюсь, вы последуете на пути от новичка к мастеру. Сам совет прост: беритесь за клавиатуру в последнюю очередь. Другими словами, когда вы сталкиваетесь с проблемой кодирования, код — это последнее, о чем вы должны думать. Код — это побочный продукт решения, а не само решение. Это может показаться немного нелогичным, особенно если вашей задачей является написание программного обеспечения, и вам будет сложно следовать этому правилу, но при достаточной практике и внимательном отношении к этому шагу вам будет легче следовать по мере продвижения по карьерной лестнице.
Чтобы проиллюстрировать эту идею, давайте посмотрим на нее в действии. Мы попробуем решить один из кодовых ката, предоставленных http://codewars.com, который представляет собой следующую задачу:
Some numbers have funny properties. For example: 89 --> 8¹ + 9² = 89 * 1 695 --> 6² + 9³ + 5⁴= 1390 = 695 * 2 46288 --> 4³ + 6⁴+ 2⁵ + 8⁶ + 8⁷ = 2360688 = 46288 * 51 Given a positive integer n written as abcd... (a, b, c, d... being digits) and a positive integer p we want to find a positive integer k, if it exists, such as the sum of the digits of n taken to the successive powers of p is equal to k * n. In other words: Is there an integer k such as : (a ^ p + b ^ (p+1) + c ^(p+2) + d ^ (p+3) + ...) = n * k If it is the case we will return k, if not return -1. Note: n, p will always be given as strictly positive integers. dig_pow(89, 1) should return 1 since 8¹ + 9² = 89 = 89 * 1 dig_pow(92, 1) should return -1 since there is no k such as 9¹ + 2² equals 92 * k dig_pow(695, 2) should return 2 since 6² + 9³ + 5⁴= 1390 = 695 * 2 dig_pow(46288, 3) should return 51 since 4³ + 6⁴+ 2⁵ + 8⁶ + 8⁷ = 2360688 = 46288 * 51
Если я попрошу вас решить эту проблему, а вы начнете со вставки кода или ввода кода в своем любимом редакторе кода, вы делаете это неправильно. Это говорит мне о том, что вы сосредоточены на строках кода, а не на работающем программном обеспечении. И ваше внимание должно быть сосредоточено не только на любом работающем программном обеспечении, но и на программном обеспечении, которое действительно решает намеченную проблему.
Эта задача с кодом интересна, потому что мне кажется, что описание проблемы загадочно. Требуется несколько прочтений, прежде чем действительно понять, что от нас требуется и каковы ожидания. В таком случае лучшим подходом является анализ требований, поэтому давайте сделаем это:
- Даны положительное целое число
n
и положительное целое числоp
- Найдите
k
, напримерk * n = (a ^ p + b ^ (p + 1) + c ^ (p + 2) ...
, гдеa, b, and c
— цифры вn
. - Проверить, является ли
k
целым числом - Если
k
является целым числом, вернутьk
, иначе вернуть-1
.
Это должен быть более простой способ объяснить требования. Он берет все слова и сокращает их до минимума, оставаясь при этом верным первоначальному замыслу. Он также структурирует требования в хронологическом порядке, что упрощает их разбивку на этапы, необходимые для достижения результата. Итак, давайте сделаем это дальше, какие шаги необходимы для выполнения этой задачи?
step 1: take n and split it to the list of digits step 2: go through the list of digits, one by one, d(i) where i is the index of the digit in the list starting at 0 from the left step 3: raise d(i) to the power p + i and add it to the sum (sum += d(i) ^ (p + i)) step 4: determine the value of k such that k = sum / n step 5: check if k is an integer step 6: if k is an integer, return k. otherwise, return -1
Переход от исходного описания к приведенному выше описанию шагов очевиден постфактум, но требует усилий от того, кто к этому не привык. Следует отметить, что определение шагов все еще на простом английском языке с некоторыми математическими обозначениями. Но до этого момента мы еще не обсуждали никакого кода. Это главный вывод. Самое большое усилие, на котором должен сосредоточиться разработчик, — это способность взять некоторые требования, независимо от того, хорошо они структурированы или нет, и преобразовать их в набор понятных и воспроизводимых шагов. Последний шаг, перевод этих шагов в код, является легкой частью. Как только вы поймете, что строите, дальше будет просто преобразование английского языка в синтаксис любого используемого вами языка программирования. Гугл в этом хорош ;)
Если бы мы хотели преобразовать описанные выше шаги в код Ruby, мы бы просто выполняли каждый шаг и писали соответствующий код Ruby. Итак, давайте сделаем это вместе:
Шаг 1: digits = n.to_s.chars.map(&:to_i)
Шаг 2: digits.each_with_index { |d, i| }
Шаг 3: sum += d ** (p + i)
Шаг 4: k = sum / n
Шаг 5: k % 1 == 0
Шаг 6: k % 1 == 0 ? k : -1
Если вы думаете, что это слишком просто и слишком легко, так и есть. Это так, потому что мы приложили все усилия на начальном этапе перевода требований в краткие и понятные шаги. Перевод каждого шага в код очень прост. Осталось собрать все воедино:
def dig_pow n, p sum = 0n.to_s.chars.map(&:to_i).each_with_index { |d, i| sum += d ** (p + i) }
k =sum / n
k % 1 == 0 ? k : -1 end
На данный момент у вас может быть не самое оптимальное решение, но у вас есть правильное. Вы можете провести рефакторинг на этом этапе, при условии, что вы написали тесты, чтобы убедиться, что ваш рефакторинг не нарушил решение. Пока вы продемонстрировали, что понимаете проблему и знаете, как найти решение, остальное — детали реализации.
Если, с другой стороны, вы начали решать эту задачу, набрав что-то вроде:
def dig_pow n, p (1..p).each do ... end end
or
def dig_pow n, p n >= 0 && p >= 0 k = n**p + n**(p+1) + n**(p+2) end
Это указывает мне на то, что вы просто гадаете, вы взяли горсть спагетти, швырнули ее в стену и надеетесь, что что-то прилипнет, не до конца понимая, что на самом деле требуется и как этого добиться. Тем не менее, многие разработчики начинают именно так. Я тоже это сделал. У всех нас есть. Мы начинаем с предположения, что понимаем проблему, и начинаем вводить код, думая, что код приведет нас к решению. Хотя на самом деле печатание должно быть последним, что мы делаем в процессе. Думать быть первым. Анализ второго. Стратегия третья…
Это был пример, который может показаться вам бесполезным во всех смыслах и целях. На мой взгляд, этот пример подчеркивает ключевую проблему разработки программного обеспечения, которая заключается в умении понимать бизнес-требования и потребности пользователя, прежде чем пытаться написать какую-либо строку кода. Поэтому, если вы практикуете то, что мы обсуждали в этом посте, с каждой поставленной перед вами задачей или задачей кодирования, вы настраиваете себя на успех, когда дело доходит до получения требований от клиента или владельца продукта.
Я надеюсь, что это поможет и сделает ваш путь к мастерству менее болезненным :)