+100 XP
instructions14 / 63
taught in: functions
Mediumfunctionsclosuresslices~30m+100 XP
Closure Counter
In Go a function is a value. It has a type, it can be stored in a variable, passed as an argument, returned from another function and kept in a slice:
var op func(int) int = func(n int) int { return n * 2 }
op(21) // 42
A function literal written inside another function can refer to that function's variables, and those variables outlive the call. That is a closure:
func Counter() func() int {
n := 0
return func() int {
n++
return n
}
}
next := Counter()
next() // 1
next() // 2
n is not copied into the returned function; the function holds a reference to
it. Two things follow, and both matter. Each call to Counter() creates a
new n, so two counters count independently. And any closure that captures
the same variable sees the same value, so two functions returned together share
their state on purpose.
Task
Fill in the seven functions in main.go.
Counter() func() intcounts 1, 2, 3 on successive calls.Accumulator() func(int) intkeeps a running total and returns it.Multiplier(n int) func(int) intmultiplies by the capturedn.Apply(nums []int, f func(int) int) []intmapsfover a slice into a new one, leaving the input alone.Compose(f, g func(int) int) func(int) intappliesgfirst, thenf.Countdown(from int) func() (int, bool)yieldsfromdown to1, then0, falseforever.Multipliers(ns []int) []func(int) intreturns one multiplier per element.
Hints
- The state goes outside the returned literal.
n := 0inside the inner function resets on every call and the counter is stuck at 1. Accumulatorneeds noif: the total starts at its zero value, and the tests include a step that brings it back to0.Applybuilds a new slice withappendonto avar out []int. Writing back intonums[i]would change the caller's data, and one test checks for that.- Read
Compose(f, g)right to left, like the maths:f(g(x)). Swapping the two givesCompose(double, increment)(3) = 7instead of8. Countdownhas to stay exhausted. Return0, falsewithout touching the counter once it has gone below 1, or the fifth call starts counting into the negatives.Multiplierslooks like the classic loop-variable trap. Since Go 1.22 the range variable is a new variable each iteration, so capturing it directly is correct now; in older Go every closure shared one variable and they all multiplied by the last element. CallingMultiplier(n)is unambiguous under either rule, which is why it is worth preferring.