Lesson 4

Functional operations

Goal

Replace hand-written loops with List, Filtered, ForAll, ForAny, Number, Set, and Sum : the idioms that make GAP scripts short and that you'll reach for constantly.

Concept

GAP is heavily functional: instead of looping to build or test, you pass a function to a combinator. The function is usually an arrow expression x -> expr (anonymous, one argument, returns expr). List maps, Filtered selects, ForAll/ForAny test, Number counts, Set dedupes-and-sorts. Lines like Set(List(gens, Order)) and ForAny(divs, d -> d = 60) are exactly this pattern.

Commands to try

List([1..5], x -> x^2);

Expect [ 1, 4, 9, 16, 25 ]. List(domain, f) applies f to each element - this is "map". The x -> x^2 is a one-argument function written inline.

Filtered([1..20], n -> n mod 3 = 0);

Expect [ 3, 6, 9, 12, 15, 18 ]. Filtered(domain, test) keeps the elements where test returns true. (mod is the remainder operator; = tests equality.)

Number([1..20], n -> IsPrimeInt(n));

Expect 8 (the primes ≤ 20). Number counts how many satisfy the predicate like Length(Filtered(...)) but direct.

ForAll([2,4,6,8], n -> n mod 2 = 0);
ForAny([1,3,5,6], n -> n mod 2 = 0);

Expect true then true. ForAll is ∀, ForAny is ∃. They short-circuit.

Set([3,1,2,3,1]);

Expect [ 1, 2, 3 ]. Set returns a strictly sorted, duplicate-free list: a "set" in GAP is just a sorted list with no repeats. Set(List(...)) is the standard way to get the distinct results of a map (e.g. the distinct element orders in a group).

Sum(List([1..10], x -> x^2));

Expect 385. Combinators compose: map then sum.

Predict-then-check

Recreate the idiom on a toy. Predict each before evaluating:

divs := DivisorsInt(12);
Number(divs, d -> d > 3);
Set(List(divs, d -> d mod 3));
ForAll(divs, d -> 12 mod d = 0);

(DivisorsInt(12) is [1,2,3,4,6,12]. The last one asks: does every divisor divide 12? - a tautology, so predict its truth value confidently.)

Exercise

  1. In one expression, get the list of squares of 1..10 that are even. (Compose Filtered and List, or filter on the square directly.)
  2. Count how many integers in 1..100 are divisible by both 2 and 5.
  3. Using Set and List, get the distinct last digits of the cubes 1³…20³.
  4. Write a ForAll check that every element of [4,9,16,25] is a perfect square. (Hint: RootInt(n)^2 = n.)
  5. Re-do Exercise 2 from Lesson 03 (the cubes list) as a single List call.

Pitfalls

  • x -> expr takes exactly one argument and returns the expression. For more arguments or multiple statements you need full function … end (Lesson 06) - an arrow can't hold a ;-separated body.
  • Filtered/ForAll/Number want a predicate returning true/false; List wants a function returning a value. Swapping them gives type-confusing results, not always an error.
  • Set(L) returns a new sorted list; it does not sort L in place (that's Sort(L), which mutates and returns nothing).
  • = is equality even inside these functions; never reach for := in a predicate.
  • These read the whole domain. Over a giant group List(G, …) will try to enumerate every element. That's fine for A5, fatal for a group with billions of elements.
← Back to Learning GAP - Part 1: Foundations