Функції в GAP

Останнє оновлення 2026-05-16 | Редагувати цю сторінку

Приблизний час: 55 хвилин

Огляд

Питання

  • Функції як спосіб повторного використання коду

Цілі

  • Використання командного рядка для прототипування
  • Створення функцій
  • Читання GAP-коду з файлу

Нагадаємо наше завдання: для скінченної групи G ми хотіли б обчислити середній порядок її елементів (тобто суму порядків її елементів, поділену на порядок групи).

Ми почнемо з дуже прямого підходу, перебираючи всі елементи групи, з якою ми працюємо:

GAP

S:=SymmetricGroup(10);

ВИХІД

Sym( [ 1 .. 10 ] )

GAP

sum:=0;

ВИХІД

0

GAP

for g in S do
  sum := sum + Order(g);
od;
sum/Size(S);

ВИХІД

39020911/3628800

Тепер припустімо, що ми хочемо зберегти цей фрагмент коду GAP і пізніше повторити цей розрахунок для деяких інших груп. Ми навіть можемо переформатувати його, щоб помістити в один рядок, і використати подвійну крапку з комою, щоб не виводити значення змінної sum:

GAP

sum:=0;; for g in S do sum := sum + Order(g); od; sum/Size(S);

ВИХІД

39020911/3628800

Тепер ми можемо легко скопіювати та вставити його в командний рядок GAP наступного разу, коли він нам знадобиться. Але тут ми бачимо першу незручність: код очікує, що група, про яку йде мова, має бути збережена в змінній з іменем S, тому або ми повинні визначати S знов кожного разу, або редагувати код, змінюючи імʼя групи:

GAP

S:=AlternatingGroup(10);

ВИХІД

Alt( [ 1 .. 10 ] )

GAP

sum:=0;; for g in S do sum := sum + Order(g); od; sum/Size(S);

ВИХІД

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

Тепер ми можемо застосувати цю функцію до іншої групи, передавши цю групу як аргумент:

GAP

A:=AlternatingGroup(10); AvgOrdOfGroup(A); time;

ВИХІД

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):

GAP

M11:=MathieuGroup(11);

ВИХІД

Group([ (1,2,3,4,5,6,7,8,9,10,11), (3,7,11,8)(4,10,5,6) ])

Очевидно, що функція AvgOrdOfGroup не визначена в цьому сеансі, тому спроба викликати її призводить до помилки:

GAP

AvgOrdOfGroup(M11);

ПОМИЛКА

Error, Variable: 'AvgOrdOfGroup' must have a value
not in any function at line 2 of *stdin*

Щоб ця функція була доступною, її потрібно спочатку завантажити за допомогою функції Read. Нижче ми припускаємо, що файл avgord.g знаходиться в поточному каталозі, тому шлях до нього не потрібен.

GAP

Read("avgord.g");

Це завантажує у GAP код з файлу, і тепер функція AvgOrdOfGroup вже доступна:

GAP

AvgOrdOfGroup(M11);

ВИХІД

53131/7920

У цьому приклад використання Read було розпочато новий сеанс GAP для того, щоб було зрозуміло, що функція AvgOrdOfGroup не існувала до виклику Read і була завантажена з файлу. Однак файл із такою функцією можна прочитати кілька разів під час одного сеансу GAP (пізніше ви побачите випадки, коли повторне читання файлу є складнішим). Повторний виклик Read виконує весь код у файлі, що зчитується. Це означає, що якщо код функції було змінено і в ньому немає помилок (але, можливо, є попередження), функція буде перезаписана. Ніколи не ігноруйте попередження!

Наприклад, давайте відредагуємо файл і замінимо рядок

GAP

return sum/Size(G);

рядком із навмисною синтаксичною помилкою:

GAP

return Float(sum/Size(G);

Тепер прочитайте цей файл

GAP

Read("avgord.g");

та зверніть увагу на повідомлення про помилку:

ПОМИЛКА

Syntax error: ) expected in avgord.g line 7
return Float(sum/Size(G);
                        ^

Оскільки сталася помилка, функція AvgOrdOfGroup у нашому сеансі не була перевизначена та залишається такою ж, як і минулого разу, коли вона була успішно прочитана:

GAP

Print(AvgOrdOfGroup);

ВИХІД

function ( G )
    for g  in G  do
        sum := sum + Order( g );
    od;
    return sum / Size( G );
end

Тепер виправте помилку, додавши відсутню закриваючу дужку, прочитайте файл ще раз і обчисліть знов середній порядок елемента для M11:

GAP

Read("avgord.g");
AvgOrdOfGroup(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;

Тепер, коли ви прочитаєте файл, ви побачите попередження:

GAP

Read("avgord.g");

ПОМИЛКА

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), що також відображатиме будь-які коментарі, якщо вони присутні у коді функції у файлі):

GAP

Print(AvgOrdOfGroup);

ВИХІД

function ( G )
    for g in G  do
        sum := sum + Order( g );
    od;
    return sum / Size( G );
end

але спроба викликати цю функції призводить до помилки:

GAP

AvgOrdOfGroup(M11);

ПОМИЛКА

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;.

Те, що відбувається далі, демонструє, як все може піти не так:

GAP

sum:=2^64; g:=[1];

ВИХІД

18446744073709551616
[ 1 ]

GAP

AvgOrdOfGroup(M11);

ВИХІД

18446744073709604747/7920

GAP

sum; g;

ВИХІД

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 два або чотири рази.

Ключові моменти
  • Командний рядок добре підходить для прототипування; функції підходять для повторюваних дій.
  • Інформативні назви функцій і коментарі зроблять код більш читабельним для вас та інших.
  • Остерігайтеся неоголошених локальних змінних!