Что такое функциональное программирование

Какая разница (запись в коде)

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

Итак ещё раз, предположим, что в вашем языке программирования есть:

оператор сложения чисел $+$
функция $sum()$ вычисляющая сумму двух чисел (не важно стандартная ли она или вы написали её сами, используя возможности языка программирования, который вы используете).

и пусть у вас есть задача записать в переменную $z$ сумму двух числовых литералов (ну или просто «сумму двух чисел») $2$ и $5$.

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

z := 5 + 2;  // (присваивание с синтаксисе Паскаль)

А с использованием функции так:

z := sum(5, 2);

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

Что могло бы быть — если бы операторов вообще не было

Таким образом, мы видим, что если бы была возможность определить (создать) функцию с именем «+» (из одного символа), то мы могли бы делать такой вызов, для сложения:

+(5, 2)

ну или с записью в переменную:

z := +(5, 2);

— но обычно так не пишут (да и многие языки не дают создавать функции, имена которых совпадают с записью операторов).

То есть разные формы записи связаны скорее с традицией и удобством восприятия.
Перейдём к выводам.

Назначение

Появление в 1960-х годах в языках (например, ALGOL и FORTRAN) функций позволило писать программы, более ясные в структурном отношении и компактные по объему кода. В идеале основная программа могла состоять из одних только вызовов функций, перемежаемых, возможно, операторами управления (условными переходами и/или циклами). Разрабатывать и отлаживать такие программы стало и быстрее и легче. Программист при желании мог писать программы, широко используя вызовы функций, тела которых еще не написаны.

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

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

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

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

Приведение типов

Возможно, вы заметили одно странное обстоятельство: функции в качестве параметра передается выражение в виде суммы разнотипных данных (символьного и числового значения):

Здесь «Выплата =» — символьная строка, а Выплата — переменная числового типа.
Ведь ранее упоминалось, что типы данных для того и придуманы, чтобы выполнять операции над однотипными данными. Тем не менее, сообщений об ошибках не было, а результат оказался правильным!

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

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

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

Недостатки функционального программирования

  • Одна из проблем функционального программирования заключается в том, что оно отличается от того, что вы уже знаете. Вы должны заново выучить многое из того, что вы уже знаете в императивной обстановке! Многие опытные разработчики, которым уже знакомо то, что они уже знают, не собираются решать проблемы по-другому. Требуется время и усилия, чтобы думать по-другому.
  • Легко написать чистые функции, но объединить их в законченное приложение — вот где все становится сложнее.
  • Большая проблема с предсказуемой производительностью. Использование только неизменяемых значений и рекурсии может потенциально привести к проблемам с производительностью, включая использование ОЗУ и скорость, поэтому большинство функциональных языков не являются особенно хорошим выбором для мягких или жестких систем реального времени или встроенных вычислений.
  • Функциональное программирование имеет тенденцию писать код в слишком абстрактной форме, когда сам программист больше не понимает, что он написал через некоторое время.
  • Чистые функции и ввод / вывод на самом деле не смешиваются.

Как работают программы

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

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

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

Вот пример логики такой подпрограммы:

Что такое функциональное программирование

Можно вернуть данные пользователю, записать их в файл или передать другой подпрограмме. Вот ещё один пример использования программы:

Эта команда запускает программу в ОС Linux, которая конвертирует изображение img.jpg в другой формат, а потом сохраняет в файл img.png.

Побочные эффекты

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

При­мер­чи­ки!

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

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

getTasksByPriority(‘high’) — вер­нёт новый мас­сив с при­о­ри­тет­ны­ми зада­ча­ми, не изме­нив дру­гие мас­си­вы. В памя­ти был один мас­сив, а теперь появит­ся ещё и вто­рой.

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

  • pullTasksByPriority(‘high’) — физи­че­ски выта­щит зада­чи из исход­но­го мас­си­ва и пере­ме­стит их в какой-то новый. В ста­ром мас­си­ве умень­шит­ся чис­ло задач.
  • Такие изме­не­ния назы­ва­ют мута­ци­я­ми: я вызвал функ­цию в одном месте, а мути­ро­ва­ло что-то в дру­гом.

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

Вот типич­ная ошиб­ка, свя­зан­ная с мута­ци­ей. Мы пишем игру, нуж­но поме­нять сум­му игро­вых очков. За это отве­ча­ет функ­ция changeScore(), кото­рая запи­сы­ва­ет резуль­тат в гло­баль­ную пере­мен­ную playerScore — то есть мути­ру­ет эту пере­мен­ную. Мы слу­чай­но, по невни­ма­тель­но­сти, вызва­ли эту функ­цию в двух местах вме­сто одно­го, и бал­лы уве­ли­чи­ва­ют­ся вдвое. Это баг.

Дру­гая типич­ная ошиб­ка. Про­грам­мист напи­сал функ­цию, кото­рая уда­ля­ет из таб­ли­цы послед­нюю стро­ку, пото­му что был почему-то уве­рен: стро­ка будет пустой и нико­му не нуж­ной. Слу­чай­но эта функ­ция вызы­ва­ет­ся в бес­ко­неч­ном цик­ле и сти­ра­ет все стро­ки, от послед­ней к пер­вой. Дан­ные уни­что­жа­ют­ся. А вот если бы функ­ция не уда­ля­ла стро­ку из таб­ли­цы, а дела­ла новую таб­ли­цу без послед­ней стро­ки, дан­ные бы не постра­да­ли.

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

Вызов функции

Рассмотрим полную версию программы с функцией:

def countFood():
    a = int(input())
    b = int(input())
    print("Всего", a+b, "шт.")

print("Сколько бананов и ананасов для обезьян?")
countFood()

print("Сколько жуков и червей для ежей?")
countFood()

print("Сколько рыб и моллюсков для выдр?")
countFood()

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

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

В языке Python определение функции должно предшествовать ее вызовам. Это связано с тем, что интерпретатор читает код строка за строкой и о том, что находится ниже по течению, ему еще неизвестно. Поэтому если вызов функции предшествует ее определению, то возникает ошибка (выбрасывается исключение NameError):

print("Сколько бананов и ананасов для обезьян?")
countFood()

print("Сколько жуков и червей для ежей?")
countFood()

print("Сколько рыб и моллюсков для выдр?")
countFood()

def countFood():
    a = int(input())
    b = int(input())
    print("Всего", a+b, "шт.")

Результат:

Сколько бананов и ананасов для обезьян?
Traceback (most recent call last):
  File "test.py", line 2, in <module>
    countFood()
NameError: name 'countFood' is not defined

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

Функции в программировании

  • Информатика

  • Duisek_Akbota
  • 29.05.2019
  • 972

г. Алматы НИШ ХБН учитель информатики
Дүйсек Ақбота Тлеубердіқызы   

Раздел долгосрочного плана:

Раздел 8.4А: Программирование (С++)

Дата: 09/04

Класс: 8 А

Школа: 

ФИО учителя: 

Количество присутствующих:

отсутствующих:

Цель профессионального развития

Развитие логического мышления учащихся на уроке информатики через диалоговое обучение.

Тема урока:

Функции в программировании

Цели обучения, которые достигаются на данном уроке

8.4.3.2 объяснять назначение подпрограмм, процедур и функций в программировании

Цели урока

Применение функций в программировании.

Критерии оценивания

Описывает назначение функции в программировании
Умеет использовать функции в программном коде

Языковые цели

Учащиеся могут:

называть типы данных и описывать их свойства

определять структуру алгоритма

описывать различия между функциями и процедурами

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

Словарь специфических терминов и терминология:

язык программирования, процедурное программирование, идентификатор, выражение, структура программы, команда, оператор, постоянная, переменная, входные и выходные данные, типы данных: числовые, целые, вещественные, литерные, строковые, условные операторы, цикл, строка, функции, процедуры

Полезная серия фраз для диалога/письма

Примерами… типов данных являются…

Разветвляющаяся структура

Циклический алгоритм

Функция это… в то время как процедура это…

Привитие ценностей

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

Межпредметные связи

Русский язык, Английский язык, математика

Навыки использования ИКТ

Google chrome, Google drive

Предварительные

знания

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

Ход урока

Запланированные этапы урока

Запланированная деятельность на уроке

Ресурсы

Начало

1-3 мин

Цель: организация начала урока.

Приветствие. Отметка отсутствующих. Проверка готовности учащихся к уроку. Концентрация внимания учащихся.

Позитивный настрой

4-9

Цель: связь с предыдущем уроком.

Ученикам даются вопросы на повторение.

Перечисли преимущества использования подпрограмм:

облегчает восприятие программы, она становится более понятной и структурированной;

сокращает код программы;

легче находить ошибки;

легче редактировать и т.д.

Работа в паре.

Задание на повторение: Найти ошибки в програмном коде

 Примерные ответы учеников:

Да, допущены 3 ошибки при объявлении параметров. Неправильно указан тип параметров и при перечислении вместо запятой стоит точка с запятой.

Слайд 1-3

Середина 10-20

Цель: Определения темы, ЦО, КО.

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

Изучают новые слова на английском.

Изложение нового материала (работа с классом):

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

Слайд 4-8

20-30

Цель: Усвоение новых знаний через практическую деятельность

Групповая работа

Задание 1: Найти правильную последовательность программного кода

Определи по заданному количеству секунд количество часов и минут.

#include

using namespace std;

int seconds, h, m;

void HoursMinutes(int s)

{ m= s / 60;  h= m / 60;  m= m % 60; }

int main()

{ cout>seconds;

HoursMinutes(seconds);

coutИспользование формативного оценивания результатов по КО и дескрипторам.

Соблюдение требований эргономики и охраны труда.

Рефлексия по уроку

Были ли цели урока/цели обучения реалистичными?

Все ли учащиеся достигли ЦО?

Если нет, то почему?

Правильно ли проведена дифференциация на уроке?

Выдержаны ли бы временные этапы урока?

Какие отступления были от плана урока и почему?

Общая оценка

Какие два аспекта урока прошли хорошо (подумайте, как о преподавании, так и об обучении)?

1:

2:

Что могло бы способствовать улучшению урока (подумайте как о преподавании, так и об обучении)?

1:

2:

Что я выявил (а) за время урока о классе или достижениях/трудностях отдельных учеников, на что необходимо обратить внимание на последующих уроках?

Набор инструментов для работы

Инструментарий типичного программиста чаще всего состоит из следующих вещей:

  • компьютер;
  • интернет (прежде всего он нужен для поиска всего неизученного и неизвестного в любом из известных поисковых сервисов);
  • редактор кода (или IDE — комплекс программных средств, используемый программистами для разработки программного обеспечения), который поможет упорядочить всё, что вы создаёте;
  • компилятор или интерпретатор. Это программа, которая читает ваш код и пытается найти в нём ошибки. Затем он собирает ваш код в единый пакет и передаёт компьютеру для выполнения;
  • наушники. Возможно, вас будут отвлекать внешние шумы, а наушники — один из простых способов оградить себя от шумов.

Вы можете использовать свободное программное обеспечение для начала работы с кодом. Таковым является Atom и Notepad++. Вы также можете попробовать SublimeText, однако этот редактор является платным программным обеспечением.

Функции в программировании

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

Замечание 1

Входные данные являются параметрами, а выходные данные называются возвращаемым значением.

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

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

имя_функции(параметры)

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

Что такое функция

Программа представляет собой последовательность выражений языка. Нередко случается, что какая-то часть программы (блок кода) неоднократно повторяется. Чтобы устранить подобного рода избыточность программного кода, используют понятие функции. Функция — это именованный блок кода, который вызывается в нужных местах программы по имени. Другими словами, функция представляет собой подпрограмму, которую можно вызвать из основной программы, причем неоднократно. Повторяющийся (да и не только) блок программного кода обычно обозначают некоторым уникальным именем, чтобы потом при необходимости обратиться к нему по этому имени. Как видно, это простая и естественная идея, направленная на облегчение реализации сложных проектов, состоящих из более простых программ.

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

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

Функция, создаваемая разработчиком, должна иметь определение, а ее применение в программе называют вызовом функции. Например, в JavaScript и РНР (а также в большинстве других языков) такое определение начинается с ключевого слова , за которым следуют имя функции, пара круглых скобок, внутри которых можно указать список параметров, а затем блок кода, заключенный в фигурные скобки:

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

Для вызова функции в том или ином месте программы указывают следующее выражение:

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

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

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