Перейти к основному содержимому

Функции

Разработчику Аналитику Тестировщику
Архитектору Инженеру

Функции

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

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

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

Поначалу код был алгоритмическим и линейным:

действие1;
действие2;
действие3;
действие4;
действие5

Но позже, код разрастался:

действие1;
действие2;
действие3;
действие4;
действие5;
действие1;
действие2;
действие3;
действие4;
действие5;
действие1;
действие2;
действие3;
действие4;
действие5

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

функция Делать() {
действие1;
действие2;
действие3;
действие4;
действие5
}
Делать();
Делать();

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

Загрузка...

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

И вместо того, чтобы каждый раз приравнивать a=10, a=11, a=15, и писать процедуру, можно вызывать функцию sum(10, 20). Поскольку функция представляет собой шаблон sum(a, b), в самом блоке мы просто пишем a+b, и просто вызываем с передачей аргументов:

sum(a, b)
{
return a+b
}
sum(10, 20)

Здесь a и b - параметры, а sum - функция. Функция может не иметь аргументов, и именно поэтому все функции пишутся как имяФункции() - скобки являются характерной чертой функций.


Параметры и аргументы

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

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

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

Это правило работает даже в тех случаях, когда имя функции совпадает с именем переменной, параметра или другого объекта в той же области видимости.

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

  • x — обращение к значению переменной или параметра с именем x;
  • x() — вызов функции с именем x.

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


Рассмотрим следующую конструкцию:

x = 1
def x(x):
x = x
x(x)

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

Как вы думаете, в конструкции x = x:

  1. Что произойдет:
  • а) изменится глобальная переменная x;
  • б) объявится локальная переменная x;
  • в) изменится значение параметра x;
  1. Какое значение присвоится x:
  • а) параметр функции x(x);
  • б) глобальная x;
  • в) переменная будет равна сама себе?

Разберём каждую строку:

  • x = 1 — создаётся глобальная переменная x, которой присваивается значение 1.
  • def x(x): — объявляется функция с именем x. Внутри этой функции вводится параметр, также названный x. На момент входа в тело функции этот параметр скрывает глобальную переменную x.
  • x = x — внутри тела функции выполняется присваивание: локальной переменной x (которая в данном случае совпадает с параметром) присваивается значение этого же параметра. Эта операция не изменяет ни глобальную переменную, ни сам параметр как таковой — она лишь подтверждает текущее значение локальной области видимости.
  • x(x) — вызывается функция x, в которую передаётся в качестве аргумента значение глобальной переменной x (то есть 1).

Итого, ответы будут такими:

1. Что произойдёт в строке x = x внутри функции?

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

Следовательно, в выражении x = x:

  • Левая часть (x =) — это объявление или присваивание локальной переменной.
  • Правая часть (= x) — это чтение значения локальной переменной, то есть параметра функции.

Таким образом, ничто не изменяется глобально, новая переменная не создаётся (параметр уже существует как локальная переменная), и значение параметра просто копируется в самого себя.

Ответ на вопрос 1:
в) изменится значение параметра x — технически это не «изменение», а присваивание параметру его же собственного значения. Глобальная переменная не затрагивается, новая локальная переменная не создаётся.


2. Какое значение присвоится x в строке x = x?

Поскольку в момент вызова функции x(x) аргументом передаётся глобальная переменная x, равная 1, то:

  • Параметр функции x инициализируется значением 1.
  • Внутри функции имя x ссылается только на этот параметр.
  • Выражение x = x присваивает переменной x её текущее значение — то есть 1.

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

То есть, глобальная переменная x останется неизменной, внутри функции параметр x получит значение 1 и присвоится самому себе.

# Объявляем глобальную переменную x и присваиваем ей значение 1
x = 1

# Определяем функцию с именем x, принимающую один параметр (также названный x)
def x(x):
# Внутри функции имя x ссылается на параметр (локальную переменную)
# Присваиваем локальной переменной x её же текущее значение — операция без эффекта
x = x

# Вызываем функцию x, передавая в неё значение глобальной переменной x (то есть 1)
x(x)

В этом примере система различает три сущности с одинаковым именем x, но разным смыслом:

  1. Глобальная переменная — существует вне функции.
  2. Параметр функции — существует только внутри функции и инициализируется значением аргумента при вызове.
  3. Имя функции — ссылка на исполняемый блок кода.

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


Важный момент - порой люди называют аргументы параметрами, а параметры - аргументами. Это не то же самое.

Параметр - это переменная, которую мы передаём в функцию.

Аргумент - это значение переменной которое передаём.

То есть, в функции sum(a, b), a и b это параметры, а 10 и 20 (значения a и b соответственно) являются аргументами.

ФУНКЦИЯ ИМЯ(ПАРАМЕТР) {
ТЕЛО ФУНКЦИИ
}

ИМЯ(АРГУМЕНТ)

То есть, пусть вас не смущает ИМЯ(ПАРАМЕТР) и ИМЯ(АРГУМЕНТ) - одинаковые конструкции, но они служат простому результату:

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

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

def greet(name):
print(f"Привет, {name}!")

Это позволит многократно вызывать функцию, и отправлять разные значения при каждом вызове, что позволит подставлять значение параметра name в блок кода внутри функции:

greet("Тимур") # Результат - Привет, Тимур!
greet("Андрей") # Результат - Привет, Андрей!
greet("Василий") # Результат - Привет, Василий!

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


Стадии работы функции

Стадии работы функции:

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

Переменные и функции можно комбинировать, к примеру, сделав так, что c = sum(a, b), обозначив, что результат выполнения функции будет являться значением переменной.

Посмотрим пример на Python.

То есть, если у функции есть return, то можно записать её в переменную. Например, в Python это выглядит так:

def double(x):
result=x*2
return result

num=double(5)
print(num)

# Результат - 10

Поясняю - здесь:

  • def - ключевое слово для объявления функции,
  • double - имя функции,
  • (x) - параметр,
  • return - ключевое слово для возвращения значения,
  • result - переменная, выступающая в качестве возвращаемого значения,
  • double(5) - вызов функции с аргументом 5,
  • num - переменная, равная значению, возвращаемого функцией double(5),
  • print(num) - вызов функции print с передачей аргумента num.

Функция double(x) выполняет умножение параметра x на 2, и когда мы вызвали через double(5), мы передали аргумент 5, который умножился на 2 и записался в переменную result. И именно значение этой переменной мы записали в num и вывели через встроенную функцию языка Python - print(), которая выводит указанное в аргументе значение в консоль.

А теперь посмотрим пример на JavaScript.

function double(x) {
let result = x * 2;
return result;
}

const num = double(5);
console.log(num);

Здесь:

  • function — ключевое слово для определения функции;
  • double — имя функции;
  • (x) — параметр;
  • let result — объявление локальной переменной;
  • return — возврат значения из функции;
  • double(5) — вызов функции с аргументом 5;
  • const num — переменная, хранящая результат вызова;
  • console.log(num) — вывод значения в консоль (аналог print в Python).

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

Пример на Java:

public class Main {
public static int doubleValue(int x) {
int result = x * 2;
return result;
}

public static void main(String[] args) {
int num = doubleValue(5);
System.out.println(num);
}
}

Здесь:

  • public static int doubleValue(int x) — сигнатура метода (в Java функции внутри классов называются методами);
  • int перед именем — тип возвращаемого значения;
  • (int x) — параметр с явным указанием типа;
  • int result — локальная переменная целочисленного типа;
  • return result — возврат значения;
  • doubleValue(5) — вызов метода с аргументом 5;
  • int num — переменная для хранения результата;
  • System.out.println(num) — вывод значения в консоль.

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

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

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

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

Определение обычно использует указание сигнатуры - набор ключевых слов (видимость, тип возвращаемого значения, слово def или function).

<набор выражений> <имя функции>(<параметры>)

Вызов всегда одинаковый - <имя функции>(<аргументы>).

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

return <значение>

После возврата значений, функция уже не выполняется.

То есть, если будет такая конструкция:

def test(x):
result = x + 2
return result
# дальнейший код до конца функции уже не выполнится:
x = 3
print(x)

...то в таком случае x = 3 и print(x) не исполнятся, потому что возврат значения подразумевает завершение работы функции и выход из неё во внешний блок кода.


Виды функций

Встроенные и пользовательские

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

Как раз функция print() - встроенная функция языка Python, а double(x) - пользовательская.

Они могут принадлежать более крупным элементам - встроенным объектам. Допустим, в Javascript есть объект console, и у него есть функция log():

console.log("Привет, мир!")

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

echo "Привет, мир";
sleep(5);
exit("Ошибка");

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

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

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

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


Анонимные функции

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

функция(x, y) -> x * y

Как видим, у такой функции имени нет - и в разных языках используется разный подход.

Эта анонимная функция принимает два аргумента x и y и возвращает их произведение. Её можно использовать сразу, например, передав в качестве аргумента в другую функцию. Анонимные функции полезны, когда нужно быстро выполнить операцию без создания полноценной функции.

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

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

// Функция принимает массив и функцию-предикат
function filterArray(arr, predicate) {
const result = [];
for (const item of arr) {
if (predicate(item)) {
result.push(item);
}
}
return result;
}

// Используем с анонимной функцией
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

//Обратите внимание - здесь функция filterArray вызывается
const evens = filterArray(numbers, function(x) {
return x % 2 === 0;
// Эта функция не имеет имени и передаётся как аргумент в параметр predicate
});

console.log(evens); // [2, 4, 6, 8, 10]

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

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


Лямбда-функции

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

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

лямбда(x, y) -> x + y

Здесь мы создали анонимную функцию, которая принимает два аргумента x и y и возвращает их сумму. Такую функцию можно использовать сразу, например, передав её в качестве аргумента в другую функцию. А слово лямбда указывает, что эта функция именно такого типа. Лямбда-функции удобны, когда нужно выполнить небольшую операцию, которую нет смысла выносить в отдельную функцию.

Все лямбда-функции являются анонимными функциями, но не все анонимные функции обязательно являются лямбда-функциями.

Лямбда — это математическая концепция, которая описывает анонимную функцию. Она была впервые введена в лямбда-исчислении (λ-calculus), которое является основой функционального программирования.

Лямбда-выражение — это реализация лямбда-функции в программировании. Оно представляет собой компактный способ записи анонимной функции.

Вот пример обычной анонимной функции на JavaScript:

const multiply = function(x, y) {
return x * y;
};

Это обычная функция без имени, её можно присвоить переменной или передать как аргумент. Но в JavaScript "лямбдой" можно назвать любую функцию, которую можно передать как значение. В повседневном JS "лямбда" и "стрелочная функция" часто используются как синонимы.

Стрелочная функция:

const multiply = (x, y) => x * y;

Если вы присмотритесь, то это просто немного другой синтаксис для создания анонимных функций. Концептуально, лямбда представляет собой термин из λ-исчисления (Алонзо Чёрч, 1936), означающий, что функция - это значение, которое можно передавать, возвращать и присваивать.

В Python реализация только лямбды и ограничена одним выражением:

# Лямбда (и она же анонимная)
square = lambda x: x ** 2

# Обычная функция — не лямбда
def square(x):
return x ** 2

В Java (до Java 8) используются анонимные классы, что более громоздко:

button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Clicked!");
}
});

Java 8 представили уже как раз лямбды без стрелочного синтаксиса:

button.addActionListener(e -> System.out.println("Clicked!"));

В C# есть и анонимные методы, и лямбды:

// Лямбда-выражение
Func<int, int> square = x => x * x;

// Анонимный метод
Func<int, int> square = delegate(int x) { return x * x; };

В Ruby - необычный синтаксис, там блоки и лямбды:

# Блок (часто используется)
[1,2,3].map { |x| x * 2 }

# Лямбда — более строгий вариант
square = ->(x) { x * 2 }
square.call(5) # 10

В Go только анонимные функции, а понятия "стрелочная" нет:

square := func(x int) int {
return x * x
}

Swift и Kotlin используют термин "замыкания" (closure), но по сути это лямбда:

let square = { (x: Int) in x * x }

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


Стрелочные функции

Стрелочные функции — это упрощённый синтаксис для записи функций.

sum(a, b) => a + b

Здесь мы определили функцию sum, которая принимает два аргумента a и b и возвращает их сумму. Символ => указывает, что это стрелочная функция. Стрелочные функции делают код короче и читаемее, особенно если функция выполняет простую операцию — это позволяет не создавать блок кода, а писать «тело» функции сразу после =>, в одну строку.

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

Ключевой элемент - оператор => (стрелка).

ЯзыкСинтаксисПример
JavaScript=>(x) => x * 2
TypeScript=>(x: number) => x * 2
C#=>x => x * 2
Java-> (с Java 8)x -> x * 2
Kotlin->{ x -> x * 2 }
Scala=>(x: Int) => x * 2
PythonНет
RubyНет
GoНет

Почему именно стрелка?

Исторически:

  • Lisp (1958) использовал lambda;
  • ML (1973) использовал fun;
  • C++ (2011) лямбды без стрелки: [] (int x) { return x*x; }.

И благодаря C# и его технологии LINQ в 2007 году, стрелка => стала популярной, и эту идею подхватили уже в JavaScript в 2015 году, также Java и Kotlin.

Стрелку выбрали, потому что это:

  • короче, чем function;
  • визуально похоже на математическую стрелку f: A → B
  • ассоциируется с указанием на результат.

К примеру, в JavaScript есть варианты синтаксиса:

  1. Только выражение (без return):
const square = x => x * x;
  1. Блок (с return):
const square = x => {
return x * x;
};
  1. Несколько аргументов:
const add = (a, b) => a + b;
  1. Без аргументов:
const getRandom = () => Math.random();

Причина существования та же:

  • короче;
  • удобнее для колбеков и даёт возможность передать функцию на лету;
  • читаемо, сразу видно, что это трансформация.

Пример:

// Было
numbers.map(function(x) {
return x * 2;
});

// Стало
numbers.map(x => x * 2);

В результате, мы формируем всё в одну строку.


Функции высшего порядка

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

applyOperation(operation, x, y) -> operation(x, y)

Здесь функция applyOperation принимает три аргумента: функцию operation и два числа x и y. Она вызывает переданную функцию operation с аргументами x и y.

Теперь мы можем использовать эту функцию с разными операциями:

sum(a, b) -> a + b
multiply(a, b) -> a * b
result1 = applyOperation(sum, 5, 3) // Результат: 8
result2 = applyOperation(multiply, 5, 3) // Результат: 15

Здесь мы создали ещё две функции - sum, multiply и обе являются аргументами в applyOperation.

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


Рекурсивные функции

Рекурсивные функции — это функции, которые вызывают сами себя.

ОБЪЯВИТЬ ФУНКЦИЯ(X):
ЕСЛИ X БОЛЬШЕ НУЛЯ:
ФУНКЦИЯ(X - 1)

ФУНКЦИЯ(10)

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

  • при первом вызове функции, параметр имеет значение 10;
  • проверка даёт истину, ведь 10 больше нуля;
  • функция вызывает саму себя, и отправляет аргумент 10-1, то есть 9;
  • функция снова и снова будет повторять вызов, пока не дойдёт до нуля;
  • проверка даст ложь, ведь X теперь имеет значение 0;
  • только тогда функция перестанет себя вызывать.

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

factorial(n) ->
если n == 0
вернуть 1
иначе
вернуть n * factorial(n - 1)

Здесь функция factorial вычисляет факториал числа n. Она вызывает саму себя с уменьшенным значением n, пока не достигнет базового случая (n == 0).

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

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

function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}

factorial(5); // 120

Стек растёт вглубь, потом сворачивается обратно:


factorial(5)
→ factorial(4)
→ factorial(3)
→ factorial(2)
→ factorial(1) → возвращает 1
→ 2 * 1 = 2
→ 3 * 2 = 6
→ 4 * 6 = 24
→ 5 * 24 = 120

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

Пример базового случая с условием выхода:

if (n <= 1) return 1;

Рекурсивный случай же подразумевает как раз изменение аргумента:

return n * factorial(n - 1);

Хвостовая рекурсия подразумевает рекурсивный вызов как последнюю операцию:

function factorial(n, acc = 1) {
if (n <= 1) return acc;
return factorial(n - 1, n * acc); // хвост
}

Взаимная рекурсия более забавная. Это случай, когда две функции вызывают друг друга:

function isEven(n) {
if (n === 0) return true;
return isOdd(n - 1);
}

function isOdd(n) {
if (n === 0) return false;
return isEven(n - 1);
}

Рекурсию используют для задач, когда требуется обход дерева:

function traverse(node) {
if (!node) return;
console.log(node.value);
traverse(node.left);
traverse(node.right);
}

Также для глубокого копирования:

function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;

const copy = Array.isArray(obj) ? [] : {};
for (const key in obj) {
copy[key] = deepClone(obj[key]);
}
return copy;
}

Но! Всё, что можно сделать рекурсией, можно сделать циклом:

// Рекурсия
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}

// Цикл
function factorial(n) {
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
}

Цикл эффективнее по памяти. Но о них мы поговорим отдельно.


Встроенные функции

Встроенные функции — это функции, которые уже реализованы в языке программирования. Они предоставляют готовые решения для распространённых задач.


Чистые функции

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

add(a, b) -> a + b

Функция add является чистой, так как она всегда возвращает сумму a и b и не изменяет ничего вне своей области видимости.

Функции, которые ничего не возвращают, не могут быть чистыми (к примеру, функции типа void).


Практика

Практическое задание
Напишите любую функцию. Это будет функция 1.

Придумайте имя для функции.

Напишите другую функцию с другим именем.

Это будет функция 2.

Вызовите функцию 1 из функции 2.


Методы

Метод – функция, привязанная к объекту.

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

Методы позволяют объектам «делать что-то» с их данными. Например, если у нас есть объект строки (текст), метод length может вернуть длину этой строки, а метод toUpper преобразует все символы в верхний регистр.

функция Делать {
действие1;
}

некийОбъект {
метод Делать {
действие1;
}
}

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


Функция, метод и процедура

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

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

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

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

КритерийФункцияПроцедураМетод
Основное назначениеВычисление и возврат результатаВыполнение последовательности действийРеализация поведения объекта или класса
Возврат значенияВсегда возвращает значениеНе возвращает значение напрямуюМожет возвращать значение или не возвращать его
Контекст вызоваМожет вызываться независимоМожет вызываться независимоВызывается через экземпляр класса или сам класс
ПринадлежностьНезависимая подпрограммаНезависимая подпрограммаПринадлежит классу или объекту
Доступ к даннымРаботает только с переданными аргументамиРаботает с переданными аргументамиИмеет доступ к полям и свойствам своего объекта
Использование в ООППрименяется вне объектной моделиПрименяется вне объектной моделиЯвляется частью объектно-ориентированной парадигмы
Побочные эффектыПредпочтительно без побочных эффектовЧасто имеет побочные эффектыМожет иметь побочные эффекты
Пример использованияВычисление суммы, хэш-функция, преобразованиеВывод на экран, запись в файл, инициализацияОбработка события, изменение состояния объекта

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

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

К примеру, если определить функцию, вычисляющую сумму чисел a и b, и возвращающую результат, то можно такой результат записывать в переменные:

def sum(a,b):
return a+b

x = sum(1,2)

Здесь есть два подхода:

# Первый

print(x)

# Второй

print(sum(1,2))

Второй - это прямая передача результатов функции.

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

Семантическая целостность выражения

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

Пример на Python демонстрирует эту связность:

print(greet())

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

Аналогичный пример на JavaScript:

console.log(sum(1, 2));

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

Пример на C#:

Console.WriteLine(FormatMessage("Приветствие"));

Форматирование сообщения и его вывод объединены в атомарное действие.

Пример на Java:

System.out.println(calculateTotal(items));

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

Технические аспекты выполнения

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

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

Читаемость и контекст использования

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

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

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

Когда оправдано промежуточное присваивание

Промежуточное сохранение результата в переменную становится предпочтительным в определённых сценариях:

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

Пример оправданного присваивания на Python:

user_profile = fetch_profile(user_id)
print(user_profile)
save_to_cache(user_profile)

Здесь переменная user_profile используется дважды, её имя поясняет содержание, а присваивание устраняет повторный вызов функции.


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).