Функции
Аналогия: рецепт
Представь что ты повар. Каждый раз когда тебе нужно приготовить пасту — ты достаёшь рецепт, следуешь ему шаг за шагом, и получаешь готовое блюдо. Рецепт написан один раз, а готовить по нему можно сколько угодно раз.
Функция — это рецепт для компьютера. Ты один раз описываешь что нужно сделать, даёшь этому описанию имя, а потом вызываешь это имя когда угодно.
Без функций: проблема
Посмотри на эту программу. Черепашке нужно трижды развернуться на 180 градусов:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- Первый разворот
turtle.turnLeft()
turtle.turnLeft()
-- Что-то делаем...
turtle.forward()
turtle.forward()
turtle.forward()
-- Второй разворот
turtle.turnLeft()
turtle.turnLeft()
-- Что-то делаем...
turtle.forward()
-- Третий разворот
turtle.turnLeft()
turtle.turnLeft()
Три проблемы:
- Повторение кода — если нужно исправить разворот (вдруг через
turnRight), придётся исправить в трёх местах
- Неочевидно — непонятно что делают два
turnLeft рядом, нужно думать
- Легко ошибиться — можно случайно написать три
turnLeft в одном месте
С функцией: решение
local function turnAround()
turtle.turnLeft()
turtle.turnLeft()
end
-- Теперь код читается как рассказ:
turnAround()
turtle.forward()
turtle.forward()
turtle.forward()
turnAround()
turtle.forward()
turnAround()
Теперь:
- Слово
turnAround объясняет себя само
- Если нужно исправить логику разворота — меняешь только в одном месте
- Невозможно случайно сделать три
turnLeft — всё внутри функции
Функция vs Цикл — в чём разница
Это частая точка путаницы. Давай разберём на примерах.
Цикл — делает одно и то же много раз подряд прямо сейчас:
-- Сделать 10 шагов вперёд прямо сейчас
for i = 1, 10 do
turtle.forward()
end
Функция — описывает что нужно сделать, но не делает это сразу. Выполняется только когда ты её вызываешь:
-- Описание: что значит "сделать N шагов"
local function walk(n)
for i = 1, n do
turtle.forward()
end
end
-- Пока ничего не произошло!
-- Функция определена, но не вызвана
walk(10) -- вот теперь черепашка пошла
walk(5) -- и снова, но уже 5 шагов
walk(10) -- и снова 10
Видишь? Внутри функции может быть цикл. Это не противоречие — это два разных инструмента, которые отлично работают вместе.
| Цикл | Функция |
|---|
| Когда выполняется | Сразу, на месте | Только когда вызвана |
| Сколько раз | Заданное количество | Столько раз сколько вызовешь |
| Зачем | Повторить действие N раз | Дать имя блоку кода |
| Может содержать | Функции внутри | Циклы внутри |
Как объявить функцию
local function имяФункции()
-- код
end
local — функция видна только в этом файле (хорошая привычка)
function — ключевое слово
имяФункции — придумываешь сам, как переменную
() — скобки обязательны (здесь будут параметры, об этом позже)
end — закрывает функцию
Вызов функции:
имяФункции() -- просто имя со скобками
Функция выполняется только при вызове
Это важно понять. Вот что происходит пошагово:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 1. Компьютер читает это определение и запоминает его
local function sayHello()
print("Привет!")
end
-- 2. Здесь ничего не происходит — компьютер просто
-- запомнил что "sayHello" делает print("Привет!")
print("Начало программы") -- выполняется
sayHello() -- вот здесь вызов — выполняется функция
print("После первого вызова") -- выполняется
sayHello() -- ещё один вызов — снова выполняется
print("Конец программы") -- выполняется
Вывод:
Начало программы
Привет!
После первого вызова
Привет!
Конец программы
Зачем давать коду имя
Хорошее имя функции — это документация. Сравни два варианта:
-- Вариант 1: непонятно что происходит
turtle.select(1)
for i = 1, 16 do
if turtle.getItemCount(i) > 0 then
local d = turtle.getItemDetail(i)
if d.name == "minecraft:cobblestone" or
d.name == "minecraft:dirt" or
d.name == "minecraft:gravel" then
turtle.select(i)
turtle.drop()
end
end
end
turtle.select(1)
-- Вариант 2: читается как предложение
dropJunk()
Для понимания второго варианта не нужно анализировать код. Имя говорит всё. Детали скрыты внутри функции — ты можешь прочитать их когда нужно, но не обязан держать в голове постоянно.
Локальные переменные в функции
Переменные объявленные через local внутри функции живут только внутри неё. Снаружи их не существует:
local function calculate()
local result = 100 + 200
print("Внутри: " .. result)
end
calculate()
-- print(result) -- ошибка! result не существует здесь
Это защищает программу от конфликтов имён. Разные функции могут иметь переменные с одинаковыми именами, и они не будут мешать друг другу:
local function funcA()
local temp = 100
print(temp) -- 100
end
local function funcB()
local temp = 999 -- другая переменная, хоть и называется так же
print(temp) -- 999
end
funcA() -- 100
funcB() -- 999
Строим программу из функций
Хорошая программа читается как список шагов:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
local function checkFuel()
if turtle.getFuelLevel() < 100 then
print("Мало топлива, заправляемся")
turtle.select(16)
turtle.refuel()
end
end
local function digTunnel(length)
for i = 1, length do
while turtle.detect() do
turtle.dig()
sleep(0.4)
end
turtle.forward()
turtle.digUp()
end
end
local function returnHome(steps)
turtle.turnLeft()
turtle.turnLeft()
for i = 1, steps do
turtle.forward()
end
turtle.turnLeft()
turtle.turnLeft()
end
local function dropItems()
for slot = 1, 15 do
turtle.select(slot)
turtle.drop()
end
turtle.select(1)
end
-- Главная программа — читается как план действий:
checkFuel()
digTunnel(20)
returnHome(20)
dropItems()
print("Готово!")
Главный блок программы — пять строк. Понятно что делает программа не читая детали функций.
Функции вызывают другие функции
Функции могут вызывать другие функции. Это позволяет строить сложное из простого:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- Маленькие кирпичики:
local function digStep()
while turtle.detect() do turtle.dig() end
turtle.forward()
end
local function digColumn()
digStep()
turtle.digUp()
turtle.digUp()
end
-- Из кирпичиков — большая конструкция:
local function digRoom(width, length)
for row = 1, length do
for col = 1, width do
digColumn()
if col < width then turtle.forward() end
end
-- разворот между рядами
if row < length then
if row % 2 == 1 then
turtle.turnRight()
turtle.forward()
turtle.turnRight()
else
turtle.turnLeft()
turtle.forward()
turtle.turnLeft()
end
end
end
end
Частые ошибки
Забыть вызвать функцию:
local function doWork()
print("Работаю...")
end
-- Упс: функция определена, но никогда не вызвана
-- Программа ничего не выводит
Вызвать до объявления:
sayHi() -- ошибка! функция ещё не определена
local function sayHi()
print("Привет!")
end
Объявляй функции до их использования. Или используй паттерн — все функции вверху файла, основной код внизу.
🚀 Задания
Задание 1
Напиши функцию greet() которая выводит "Привет от черепашки!". Вызови её три раза. Убедись что вывод появился три раза — и что код без функции был бы длиннее.
Задание 2
Есть повторяющийся блок кода — вынеси его в функцию и замени вызовами:
print("---")
print("Шаг 1: старт")
print("---")
turtle.forward()
turtle.forward()
print("---")
print("Шаг 2: копаем")
print("---")
turtle.dig()
print("---")
print("Шаг 3: финиш")
print("---")
Задание 3
Напиши функцию square(size) которая заставляет черепашку пройти квадрат со стороной size. Вызови её с разными значениями: 3, 5, 10.
Задание 4
Объясни (в комментариях) что выведет эта программа и в каком порядке:
local function b()
print("B")
end
local function a()
print("A start")
b()
print("A end")
end
print("main start")
a()
print("main end")
a()
Задание 5
Перепиши вот этот "спагетти-код" используя функции. Каждая функция — один логический шаг. Программа должна читаться как 4–5 строк в главном блоке:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- Заправка
turtle.select(16)
turtle.refuel()
print("Заправлен: " .. turtle.getFuelLevel())
-- Копаем 10 блоков
for i = 1, 10 do
while turtle.detect() do turtle.dig() sleep(0.4) end
turtle.forward()
turtle.digUp()
end
-- Возврат
turtle.turnLeft()
turtle.turnLeft()
for i = 1, 10 do turtle.forward() end
turtle.turnLeft()
turtle.turnLeft()
-- Выгрузка
for s = 1, 15 do turtle.select(s) turtle.drop() end
turtle.select(1)
print("Готово")