суббота, 12 февраля 2011 г.

Объявление и оформление функций

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

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

  1. Весь код программы должен быть разбит на подпрограммы (функции), каждая из которых выполняет только одну цель.
  2. Все входные и выходные параметры должны быть явно описаны.
  3. Давайте функциям и переменным осмысленные названия.
  4. Код нужно писать как можно проще и понятнее.
  5. Код функций должен быть компактным, так, чтобы можно было одним взглядом охватить весь код функции.
При обучении своих учеников программированию, я использую язык Python. При этом стараюсь много чего брать из отличного учебника How to Design Programs. Например, касательно стиля кодирования, мне нравится то, как они оформляют свои функции. Перед описанием функции (и желательно до того, как мы приступим непосредственно к реализации функции) написать в виде комментария контракт, цель и пример вызова функции. Контракт описывает входные и выходные параметры функции. Указываем кратко и четко цель. А также, пример вызова функции и что должна вернуть функция при заданных входных параметрах.

В качестве примеров, приведу пару функций написанных одним из моих учеников:


# Контракт: String ~> Boolean
# Цель: Является ли переданная строка цифрой
# Пример: is_digit("1") ~> True, is_digit("123") ~> False
# is_digit("abc") ~> False
def is_digit(a):
digits = ['0','1','2','3','4','5','6','7','8','9']
if a in digits:
s = True
else:
s = False
return s


# Контракт: String ~> Boolean
# Цель: Является ли строка годом
# Пример: is_valid_year("2010") ~> True
# is_valid_year("2as2") ~> False
def is_valid_year(b):
s = False
if len(b) == 4:
if is_digit(b[0]) and is_digit(b[1]) and is_digit(b[2]) and is_digit(b[3]):
s = True
return s


Данные примеры, удовлетворяют почти всем моим правилам хорошего стиля.

4 комментария:

  1. Не соглашусь по поводу комментариев. Если функция правильно названа и нормально написан код, то и так понятно, что она делает. Исключения, конечно, бывают.

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

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

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

    ОтветитьУдалить
  3. Тогда уже примеры вызова оформить как doctests и автоматически проверять корректность.

    ОтветитьУдалить
  4. В питоне для документации есть docstrings которые задают определенный стиль оформления. Также, как отметил Roman, для примеров есть doctest.

    Для кросс-языковой документации есть doxygen.

    Та же документация, которая приведена в посте, на практике бесполезна и сложна в поддержке.

    Также, ИМО, за s = True / else: / s = False по рукам надо бить.

    ОтветитьУдалить