Функції в GAP
Last updated on 2026-04-27 | Edit this page
Overview
Questions
- Функції як спосіб повторного використання коду
Objectives
- Використання командного рядка для прототипування
- Створення функцій
- Читання GAP-коду з файлу
Просто нагадаємо наше завдання: для скінченної групи G ми хотіли б обчислити середній порядок її елементів (тобто суму порядків її елементів, поділену на порядок групи).
Ми починаємо з дуже прямого підходу, повторюючи всі елементи групи, про яку йдеться:
OUTPUT
Sym( [ 1 .. 10 ] )
OUTPUT
0
OUTPUT
39020911/3628800
Тепер припустімо, що ми хочемо зберегти цей фрагмент коду GAP і
пізніше повторити цей розрахунок для деяких інших груп. Ми навіть можемо
переформатувати його, щоб помістити його в один рядок, і використати
подвійну крапку з комою, щоб закоментувати вивід sum:
OUTPUT
39020911/3628800
Тепер ми можемо легко скопіювати та вставити його в сеанс GAP
наступного разу, коли він нам знадобиться. Але тут ми бачимо першу
незручність: код очікує, що група, про яку йде мова, має бути збережена
в змінній з іменем S, тому або ми повинні скидати
S кожного разу, або нам потрібно редагувати код:
OUTPUT
Alt( [ 1 .. 10 ] )
OUTPUT
2587393/259200
Це працює лише для швидкого прототипування
- можна випадково скопіювати та вставити лише частину коду, а неповне введення може викликати цикл розриву;
- ще небезпечніше: можна забути скинути
sumна нуль перед новим обчисленням і отримати неправильні результати; - група, про яку йде мова, може мати іншу назву змінної, тому код доведеться змінити;
- останнє, але не менш важливе: коли код GAP вставляється в інтерпретатор, він оцінюється рядок за рядком. Якщо у вас є довгий файл із багатьма командами, а синтаксична помилка міститься в рядку N, про цю помилку буде повідомлено лише тоді, коли GAP завершить оцінку всіх попередніх рядків, а це може зайняти досить багато часу.
Ось чому нам потрібно надати нашому коду GAP більше структури, організувавши його у функції:
- функції аналізуються спочатку і можуть бути викликані пізніше;
- будь-які синтаксичні помилки будуть виявлені на етапі аналізу, а не під час виклику;
- функції можуть мати локальні змінні, і це запобігає їх випадковому перезапису лише через повторне використання того самого імені змінної для зберігання чогось іншого.
Наступна функція приймає аргумент 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;
OUTPUT
function( G ) ... end
Тепер ми можемо застосувати це до іншої групи, передавши групу як аргумент:
OUTPUT
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):
OUTPUT
Group([ (1,2,3,4,5,6,7,8,9,10,11), (3,7,11,8)(4,10,5,6) ])
Очевидно, що AvgOrdOfGroup не визначено в цьому сеансі,
тому спроба викликати цю функцію призводить до помилки:
ERROR
Error, Variable: 'AvgOrdOfGroup' must have a value
not in any function at line 2 of *stdin*
Щоб бути доступною, її потрібно спочатку завантажити за допомогою
функції Read. Нижче ми припускаємо, що файл знаходиться в
поточному каталозі, тому шлях не потрібен.
Це завантажує файл у GAP, і функція AvgOrdOfGroup тепер
доступна:
OUTPUT
53131/7920
У цьому прикладі, використовуючи Read, було розпочато
новий сеанс GAP, щоб було зрозуміло, що AvgOrdOfGroup не
існувало до виклику Read і було завантажено з файлу. Однак
файл із такою функцією можна прочитати кілька разів під час одного
сеансу GAP (пізніше ви побачите випадки, коли повторне читання файлу є
складнішим). Повторний виклик Read виконує весь код у
файлі, що зчитується. Це означає, що якщо код функції було змінено і в
ньому немає помилок (але, можливо, є попередження), функція буде
перезаписана. Ніколи не ігноруйте попередження!
Наприклад, давайте відредагуємо файл і замінимо рядок
рядком із навмисною синтаксичною помилкою:
Тепер прочитайте цей файл
і Ви побачите повідомлення про помилку:
ERROR
Syntax error: ) expected in avgord.g line 7
return Float(sum/Size(G);
^
Оскільки сталася помилка, функція AvgOrdOfGroup у нашому
сеансі не була перевизначена та залишається такою ж, як і минулого разу,
коли її успішно прочитали:
OUTPUT
function ( G )
for g in G do
sum := sum + Order( g );
od;
return sum / Size( G );
end
Тепер виправте помилку, додавши відсутню закриваючу дужку, прочитайте
файл ще раз і перерахуйте середній порядок елемента для
M11:
OUTPUT
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;
Тепер, коли ви прочитаєте файл, ви побачите попередження:
ERROR
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 не оголошено як локальні змінні, GAP
очікує, що вони будуть глобальними змінними під час виклику функції.
Оскільки вони не існували під час виклику Read,
відображалося попередження. Однак, якби вони існували до того моменту,
попередження не було б, і будь-який виклик AvgOrdOfGroup
перезаписав би їх! Це показує, наскільки важливим є оголошення локальних
змінних. Давайте розберемося трохи детальніше, що сталося:
Функцію тепер перевизначено, як ми бачимо з її виводу (або
перевіряємо за допомогою PageSource(AvgOrdOfGroup), який
також відображатиме будь-які коментарі):
OUTPUT
function ( G )
for g in G do
sum := sum + Order( g );
od;
return sum / Size( G );
end
але спроба запустити його призводить до розриву циклу:
ERROR
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>
з якого можна вийти за допомогою quit;.
Те, що відбувається далі, демонструє, як все може піти не так:
OUTPUT
18446744073709551616
[ 1 ]
OUTPUT
18446744073709604747/7920
OUTPUT
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 два або чотири рази.
- Командний рядок добре підходить для прототипування; функції підходять для повторних обчислень.
- Інформативні назви функцій і коментарі зроблять код більш читабельним для вас і інших.
- Остерігайтеся неоголошених локальних змінних!