Функції в GAP
Останнє оновлення 2026-06-16 | Редагувати цю сторінку
Приблизний час: 55 хвилин
Огляд
Питання
- Функції як спосіб повторного використання коду
Цілі
- Використання командного рядка для прототипування
- Створення функцій
- Завантаження GAP-коду з файлу
Нагадаємо наше завдання: для скінченної групи G ми хотіли б обчислити середній порядок її елементів (тобто суму порядків її елементів, поділену на порядок групи).
Ми почнемо з прямого підходу, перебираючи всі елементи групи, з якою ми працюємо:
ВИВІД
Sym( [ 1 .. 10 ] )
ВИВІД
0
ВИВІД
39020911/3628800
Тепер припустимо, що ми хочемо зберегти цей фрагмент коду GAP і
пізніше повторити ці обчислення для деяких інших груп. Ми навіть можемо
переформатувати цей код, щоб помістити в один рядок, і використати
крапку з комою двічі, щоб не виводити на екран значення змінної
sum:
ВИВІД
39020911/3628800
Тепер ми можемо легко скопіювати та вставити його в командний рядок
GAP наступного разу, коли він нам знадобиться. Але тут ми бачимо першу
незручність: код очікує, що група, про яку йде мова, має бути збережена
в змінній S, тому або ми повинні визначати S
знов кожного разу, або редагувати код, використовуючи іншу змінну:
ВИВІД
Alt( [ 1 .. 10 ] )
ВИВІД
2587393/259200
Це працює лише для швидкого прототипування
- можна випадково скопіювати та вставити лише частину коду, і тоді неповне введення може призвести до повідомлення про помилку;
- ще небезпечніше: можна забути повторно встановити
sumна нуль перед новим обчисленням і отримати неправильні результати; - група, яка розглядається, може зберігатися в іншій змінній, і тому код доведеться змінити;
- останнє, але не менш важливе: коли код GAP вставляється в інтерпретатор командного рядка, він виконується рядок за рядком. Якщо у вас є довгий файл із багатьма командами, а синтаксична помилка міститься в рядку N, про цю помилку буде повідомлено лише тоді, коли GAP завершить виконання всіх попередніх рядків, а це може зайняти досить багато часу.
Ось чому нам потрібно надати нашому коду GAP більше структури, організувавши його у функції:
- функції спочатку читаються та аналізуються, а їх фактичне виконання відбувається пізніше під час їх виклику;
- будь-які синтаксичні помилки будуть виявлені на етапі аналізу, а не під час виклику;
- функції можуть мати локальні змінні, і це запобігає випадковому перезапису змінних у разі повторного використання тієї самої назви змінної для зберігання чогось іншого поза межами функції.
Наступна функція приймає аргумент G і обчислює середній
порядок елементів з групи G:
GAP
AvgOrdOfGroup := function(G)
local sum, g;
sum := 0;
for g in G do
sum := sum + Order(g);
od;
return sum/Size(G);
end;
ВИВІД
function( G ) ... end
Тепер ми можемо застосувати цю функцію до іншої групи, вказуючи групу як аргумент:
ВИВІД
Alt( [ 1 .. 10 ] )
2587393/259200
837
Наведений вище приклад також демонструє використання
time. Ця змінна зберігає в мілісекундах час, який процесор
витратив на виконання останньої команди.
Тепер ми можемо створювати нові групи та повторно використовувати
AvgOrdOfGroup для обчислення середнього порядку їхніх
елементів у тому самому сеансі роботи з GAP. Наша наступна мета —
зберегти цю функцію для її використання у майбутньому.
Використовуючи текстовий редактор (наприклад, той, з яким ви,
можливо, працювали на попередніх уроках Software Carpentry), створіть
текстовий файл під назвою avgord.g, що містить наступний
код функції та коментарі (хороший шанс звернути увагу на коментування
коду):
GAP
#####################################################################
#
# AvgOrdOfGroup(G)
#
# Calculating the average order of an element of G, where G meant to
# be a group but in fact may be any collection of objects having
# multiplicative order
#
AvgOrdOfGroup := function(G)
local sum, g;
sum := 0;
for g in G do
sum := sum + Order(g);
od;
return sum/Size(G);
end;
Далі, почніть новий сеанс роботи з GAP і створіть іншу групу,
наприклад, MathieuGroup(11):
ВИВІД
Group([ (1,2,3,4,5,6,7,8,9,10,11), (3,7,11,8)(4,10,5,6) ])
Очевидно, що функція AvgOrdOfGroup невизначена в цьому
сеансі, тому спроба викликати її призведе до помилки:
ПОМИЛКА
Error, Variable: 'AvgOrdOfGroup' must have a value
not in any function at line 2 of *stdin*
Щоб ця функція була доступною, її потрібно спочатку завантажити за
допомогою функції Read. Нижче ми припускаємо, що файл
avgord.g знаходиться в поточному каталозі, тому шлях до
нього не потрібно вказувати.
Це завантажує у GAP код з файлу, і тепер функцію
AvgOrdOfGroup можна використовувати:
ВИВІД
53131/7920
У цьому прикладі ми навмисно розпочали новий сеанс GAP для того, щоб
було зрозуміло, що функція AvgOrdOfGroup не існувала до
виклику Read і була завантажена з файлу. Однак файл із
такою функцією можна прочитати кілька разів під час одного сеансу GAP
(пізніше ви побачите випадки, коли повторне читання файлу є складнішим).
Повторний виклик Read виконує весь код у файлі, що
зчитується. Якщо код функції було змінено і в ньому немає помилок (але,
можливо, є попередження), функція буде перезаписана. Ніколи не
ігноруйте попередження!
Наприклад, давайте відредагуємо файл і замінимо рядок
рядком із навмисною синтаксичною помилкою:
Тепер прочитайте цей файл
та зверніть увагу на повідомлення про помилку:
ПОМИЛКА
Syntax error: ) expected in avgord.g line 7
return Float(sum/Size(G);
^
Оскільки сталася помилка, функція AvgOrdOfGroup у нашому
сеансі не була перевизначена та залишається без змін:
ВИВІД
function ( G )
for g in G do
sum := sum + Order( g );
od;
return sum / Size( G );
end
Тепер виправте помилку, додавши відсутню закриваючу дужку, прочитайте
файл ще раз і повторно обчисліть середній порядок елемента для групи
M11:
ВИВІД
6.70846
Далі, давайте розглянемо приклад попередження. Оскільки це лише попередження, читання коду призведе до перевизначення функції, і це може призвести до несподіваного результату. Щоб побачити, що може статися, спочатку відредагуйте файл, щоб відкотити зміни в типі результату (тобто, щоб повертати раціональне значення замість числа з плаваючою комою), а потім закоментуйте два рядки наступним чином:
GAP
AvgOrdOfGroup := function(G)
# local sum, g;
# sum := 0;
for g in G do
sum := sum + Order(g);
od;
return sum/Size(G);
end;
Коли ви прочитаєте файл, то побачите попередження:
ПОМИЛКА
Syntax error: warning: unbound global variable in avgord.g line 4
for g in G do
^
Syntax error: warning: unbound global variable in avgord.g line 5
sum := sum + Order(g);
^
Syntax error: warning: unbound global variable in avgord.g line 5
sum := sum + Order(g);
^
Syntax error: warning: unbound global variable in avgord.g line 7
return sum/Size(G);
^
Ці попередження означають, що g та sum не
оголошено як local (локальні) змінні. Тому GAP очікує, що
вони будуть глобальними змінними під час виклику функції. Оскільки вони
не існували під час виклику Read, то відображалося
попередження. Однак, якби ці змінні вже існували на цей момент, то
попередження не було б, і будь-який виклик AvgOrdOfGroup
перезаписав би їх! Це показує, наскільки важливим є оголошення локальних
змінних. Давайте розберемося трохи детальніше, що сталося.
Тепер функцію перевизначено (як ми бачимо з її виводу, або можемо
перевірити за допомогою PageSource(AvgOrdOfGroup), що також
відображатиме будь-які коментарі, якщо вони присутні у коді функції у
файлі):
ВИВІД
function ( G )
for g in G do
sum := sum + Order( g );
od;
return sum / Size( G );
end
але спроба викликати цю функції призводить до помилки:
ПОМИЛКА
Error, Variable: 'sum' must have an assigned value in
sum := sum + Order( g ); called from
<function "AvgOrdOfGroup">( <arguments> )
called from read-eval loop at line 24 of *stdin*
you can 'return;' after assigning a value
brk>
Це стан, у якому GAP виводить запрошення brk>. У
ньому ви можете, наприклад, перевірити поточні значення змінних, щоб
краще зрозуміти помилку. Щоб вийти з цього стану до головної сесії GAP
треба ввести команду quit;.
Те, що відбувається далі, демонструє, як все може піти не так:
ВИВІД
18446744073709551616
[ 1 ]
ВИВІД
18446744073709604747/7920
ВИВІД
18446744073709604747
(1,2)(3,10,5,6,8,9)(4,7,11)
Перш ніж перейти до наступного епізоду, будь ласка, скасуйте останню
зміну, розкоментувавши два рядки з коментарями, щоб у вас знову була
коректна версія AvgOrdOfGroup у файлі
avgord.g:
GAP
AvgOrdOfGroup := function(G)
local sum, g;
sum := 0;
for g in G do
sum := sum + Order(g);
od;
return sum/Size(G);
end;
Шляхи
Важливо знати, як вказувати шляхи до файлів у всіх операційних системах і де знайти свій домашній і поточний каталог.
Корисно знати, що завершення шляху та імені файлу активується натисканням клавіші
Escдва або чотири рази.
- Командний рядок добре підходить для прототипування; функції підходять для повторюваних дій.
- Інформативні назви функцій і коментарі зроблять код більш читабельним для вас та інших.
- Уникайте неоголошених локальних змінних!