Higher-order procedures
A procedure that takes another procedure as an argument is higher-order. It decides when the work happens; the caller decides what the work is. To accept plain lambdas and capturing closures alike, the parameter is a generic type:
#import "std/containers"
'' A procedure that takes another procedure is "higher-order". Taking it
'' as a generic F accepts plain lambdas and capturing closures alike.
sub Repeat( of F )( byval times as int32, byref action as F )
for i as int32 = 1 to times
action( i )
next
end sub
dim as int32 sumOfSquares = 0
Repeat( 4, sub[ byref sumOfSquares ]( byval i as int32 )
sumOfSquares += i * i
end sub )
print "1 + 4 + 9 + 16 ="; sumOfSquares
'' The standard containers take callables the same way.
dim scores as containers.Array( of int32 ) = { 72, 95, 41, 88, 60 }
'' SortBy asks the comparer one question: does a come before b?
containers.SortBy( scores, function( byref a as int32, _
byref b as int32 ) as boolean
return a > b
end function )
'' ForEach hands each element to an action; this one counts passes.
dim as int32 passMark = 60, passed = 0
containers.ForEach( scores, _
sub[ byval passMark, byref passed ]( byref s as int32 )
print s;
if s >= passMark then passed += 1
end sub )
print
print passed; " of"; scores.Count( ); " scores passed"
Output
1 + 4 + 9 + 16 = 30
95 88 72 60 41
4 of 5 scores passed
Repeat( of F ) takes its action as byref action as F. F is whatever type
the argument has: here, the type of a closure that captures sumOfSquares.
Inside, action( i ) calls it like any procedure.
Generics covers of in full.
The standard library's containers take callables the same way:
containers.SortBytakes a comparer that answers one question, "doesacome beforeb?". Answeringa > bsorts from highest to lowest.containers.ForEachhands each element to an action. This one capturespassMarkby value, because it only reads it, andpassedby reference, because it updates it.
A parameter typed as a procedure pointer, like Tabulate's on the
procedure pointers page, takes only lambdas that capture
nothing; a closure passed to one is
TKO0698: this argument does not convert to the parameter's type. The generic
form takes both. Containers
lists the other procedures that take a callable.
Activities
- Write
function CountIf( of F )( values( ) as int32, byref test as F ) as int32and use it with a lambda that tests for even numbers. - Sort
scoresfrom lowest to highest, then print only the three best.