Lesson 6

Functions & Read

Goal

Write a multi-statement function … end with locals and an if, understand what it returns, and Read a .g file you wrote in your own editor - so you can keep real work in scratch/ instead of retyping at the prompt.

Concept

The arrow x -> expr (Lesson 04) is syntactic sugar for a one-line function. For anything with several statements, local variables, or a conditional you write the full form:

f := function(args) 
  local v1, v2; 
  …statements… 

  return value; 
end;

local declares names private to the function (forget it and GAP warns about globals). A function returns value when it hits return; with no return it returns nothing. Read("file.g") executes a file as if you'd typed it - the way you'll load scratch/*.g. Our target is a small chk(name, got, want) verifier; building it earns the whole language vocabulary at once.

Commands to try

double := function(x) return 2*x; end;
double(21);

Expect 42. The body is one return; end closes it (no od/fi here, just end).

classify := function(n)
  if n < 0 then
    return "negative";
  elif n = 0 then
    return "zero";
  else
    return "positive";
  fi;
end;
List([-2, 0, 5], classify);

Expect [ "negative", "zero", "positive" ]. Note elif/fi, and that a named function composes with List exactly like an arrow does.

Locals and a real helper

sumsq := function(L)
  local r, s;
  r := 0;
  for x in L do
    r := r + s^2;
  od;
  return r;
end;
sumsq([1,2,3,4]);

Expect 30. r and s are local - they don't leak into your session. Drop the local line and re-enter it: GAP prints a warning about r/s being made global. That warning is worth provoking once, so you get familiar with it showing up.

The chk harness (a self-check verifier)

This is a common pattern for scripting checks. It prints OK/FAIL and records a failure flag.

ok := true;;
chk := function(name, got, want)
  if got = want then
    Print("  OK   ", name, " = ", got, "\n");
  else
    Print("  FAIL ", name, " = ", got, "  (expected ", want, ")\n");
    ok := false;
  fi;
end;;
chk("two plus two", 2+2, 4);
chk("a wrong one", 2+2, 5);
ok;

Predict ok before the last line: one check failed, so what is it? Notice chk reads and writes the global ok: that's deliberate here (it's not in the local list), a handy way to accumulate a pass/fail verdict across many checks.

Reading a file

In your editor, create scratch/warmup.g containing, say:

sq := function(x) return x^2; end;;
Print("warmup.g loaded; sq(9) = ", sq(9), "\n");

Then in GAP:

Read("scratch/warmup.g");
sq(9);

The file runs; sq is now defined in your session. This is how you'll load larger scripts from here on - write in your editor, Read it in, test at the prompt.

Predict-then-check

Before running, predict the return value and whether anything prints:

mystery := function(n) local i; for i in [1..n] do od; end;
mystery(3);

(Trick: the loop body is empty and there's no return. What does GAP show for the value of mystery(3)?)

Exercise

  1. Write isperfectsquare := function(n) … end returning true/false using RootInt. Test it with Filtered([1..30], isperfectsquare).
  2. Extend the chk harness: add a chkn(name, got, want) that on failure also prints the difference got - want. Reuse the same global ok.
  3. Put isperfectsquare and one chk(...) call into scratch/lesson06.g, Read it, and confirm both the function and the OK line appear.
  4. Report what mystery(3) evaluated to and why.

Pitfalls

  • function … end, not function … endfunction. Blocks inside still use fi/od; only the function itself closes with end.
  • Forgetting local makes loop/temp variables global and triggers a warning - and can clobber a session variable of the same name. Declare locals.
  • No return (or a bare return;) means the function returns nothing; calling it at the prompt prints nothing. That's a feature for side-effect functions like Print-only helpers.
  • Read("path") takes a string and runs relative to GAP's current directory (where you launched it). If Read "does nothing," you're likely in the wrong directory - check with DirectoryCurrent() or launch GAP from the repo root.
  • A function closing over a global (like chk over ok) is intentional here but is exactly the kind of shared state to keep deliberate, not accidental.
← Back to Learning GAP - Part 1: Foundations