Version:0.9 StartHTML:0000000105 EndHTML:0000055000 StartFragment:0000001037 EndFragment:0000054984
{***************************************************************
/*
* System: maXbox :Product: Closures Report
* File: $RCS:271_closures_study.txt$:locs=251
* Purpose: Implements and calls some function patterns
*
* Release V:1.0.2
* ©:public domain; Company: kleiner kommunikation
* Author: Max
* Filepath: $E:\maxbox\maxbox3\examples\271_closures_study.txt$
*
* Created: 2012-02-09 by mX4
* Modified: $Date: 2012-05-11 2:40p $ @: Max
* Revision: $LastChanged: 0.2$
*/*}
program Closure_Studies_Snippets;
// this is in conjunction with the Report in Der Entwickler 2012, locs=236
// in V4.1 you find closure enabled in maXbox
// see also Script 043_pas_proceduretype.txt
function GetShortPathName(lname, sname: pchar; mpath: longint): bool;
external 'GetShortPathNameW@kernel32.dll stdcall';
function TFormMain_GetTime: TDateTime; //Function Enclosing
begin
result:= StrToTime(FormatDateTime('h:n',time)); // cut seconds
end;
function RoundSchweiz(value: Extended):Extended; //Function Casing
var i:integer;
j:double;
begin
// 0-3 Rappen --> auf 0 abrunden
// 4-5 Rappen --> auf 5 aufrunden
// 6-7 Rappen --> auf 5 abrunden
// 8-9 Rappen --> auf 0 aufrunden
i:= round(frac(abs(value)*10) * 10);
case i of
0: j:=0;
1: j:=-0.01;
2: j:=-0.02;
3: j:=-0.03;
4: j:= 0.01;
5: j:= 0;
6: j:=-0.01;
7: j:=-0.02;
8: j:= 0.02;
9: j:= 0.01;
end;
if j=0
then result:=value
else if value<0
then result:=value-j
else result:=value+j;
end;
Function TDaten3_BerechneMwStBetrag(aNettobetrag, aMwStSatz:Extended; //func delegate
aWaehrung:string):Extended;
BEGIN
result:=Round2Dec(aNettobetrag*aMwStSatz/100);
if AnsiUpperCase(aWaehrung)='SFR' then result:= RoundSchweiz(result);
END;
function GetQuery(SQLCommand: string; aQuery: TQuery): TFields; //function object enclosing
begin
aQuery.SQL.Text:= SQLCommand;
aQuery.Open;
aQuery.Active:= true;
result:= aQuery.Fields;
end;
Procedure TDaten_CopyDS(t: TDataSet); //Procedure parameter as an object
// Beim Kopieren immer AsVariant benutzen!!!
// Leere, nicht initialisierte Integer-, Float- und DateTime-Felder liefern mit AsInteger oder value
// den Wert 0 und nicht NULL!
// Wenn das Feld eine Nachschlagetabelle benutzt, und das Nachschlagefeld mit Min/Max begrenzt ist,
// gibt es bei Locate und Lookup eine Exception!!!
var a:array[0..60] of Variant;
i:integer;
begin
with T do begin
//if State in dsEditModes then Post;
for i:=0 to FieldCount-1 do a[i]:=Fields[i].AsVariant;
Insert;
if (Name='Kunde') or (Name='Liefer') or (Name='Personal') or
(Name='Angebot') or (Name='Auftrag') or (Name='Kasse') or (Name='Artikel')
then for i:=1 to FieldCount-1 do Fields[i].AsVariant:=a[i] // no new copy
else for i:=0 to FieldCount-1 do Fields[i].AsVariant:=a[i];
end;
end;
function BinToInt(Value: string): Integer;
var
i, iValueSize: Integer;
begin
Result := 0;
iValueSize := Length(Value);
for i := iValueSize downto 1 do
if Value[i] = '1' then Result := Result + (1 shl (iValueSize - i));
end;
//--------------------------closure tests--------------------------
type
TMath_func = PROCEDURE(VAR x: single);
var
fct1x, fct2x, fct3x: TMath_func;
PROCEDURE fct1(var x: single);
BEGIN
x:= Sin(x);
END;
PROCEDURE fct2(var x: single);
BEGIN
x:= Cos(x);
END;
PROCEDURE nth_power(var x: single);
BEGIN
x:= power(x,4);
END;
PROCEDURE fct_table(start, stop, step: single; fct: TMath_func; fct_name: string);
VAR x: single;
BEGIN
x:= start;
Writeln('x | ' + fct_name);
WHILE x <= stop DO BEGIN
fct(x);
Writeln(floatToStr(x) +' | '+ floatToStr((x)));
x := x + step;
END;
END;
Function fct_table2(start, stop, step: single; fct: TMath_func;
fct_name: string): TMath_func;
VAR x: single;
BEGIN
x:= start;
Writeln('x | ' + fct_name);
WHILE x <= stop DO BEGIN
fct(x);
Writeln(floatToStr(x) +' | '+ floatToStr((x)));
x := x + step;
END;
{@function a: boolean; begin
end;}
result:= fct;
END;
function generate_power_func(var n: single; fct: TMath_func): TMath_func;
var x: single;
begin
//print "id(n): %X" % id(n)
fct(x); //call
result:= fct;//_power(x); //nth_power(n);
//return x**n
//print "id(nth_power): %X" % id(nth_power)
end;
//----------------------------change to python syntax--------------------------
(*def generate_power_func(n):
print "id(n): %X" % id(n)
def nth_power(x):
return x**n
print "id(nth_power): %X" % id(nth_power)
return nth_power
>>> raised_to_4 = generate_power_func(4)
id(n): CCF7DC
id(nth_power): C46630
>>> repr(raised_to_4)
'<function nth_power at 0x00C46630>'
>>> del generate_power_func
>>> raised_to_4(2) ---> 16 *)
//Anonymous in Delphi
Procedure ClosureGag;
begin
with TForm.Create(self) do begin
BorderStyle := bsNone;
WindowState := wsMaximized;
Show;
end;
end;
var
mylst: TStringlist;
i: integer;
ms: string;
me: extended;
mes: single;
begin
//maxform1.ShellStyle1click(self)
maxform1.Console1Click(self)
//maxform1.Decompile1Click(self)
mylst:= TStringlist.create;
with TSession.Create(NIL) do try
SessionName:= 'Mars4'
getAliasNames(mylst);
Writeln('BDE / DB Alias List: ******************************');
for i:= 1 to mylst.count-1 do
write(mylst[i]+' ');
finally
Free;
mylst.Free;
end;
Writeln('BDE/DB Parameters: ******************************');
Writeln('BDE Directory '+GetBdeDirectory);
writeln('DB Alias Path '+GetAliasPath('DBDEMOS'));
writeln('Temp File Path'+getTempDir)
Writeln('*************************************************');
Writeln(floatToStr(TDaten3_BerechneMwStBetrag(260, 19, 'SFR')))
writeln(timetoStr(TFormMain_GetTime)) //cut seconds
writeln(timeToStr(time))
GetTickCount //no return
fct1x:= @fct1
fct2x:= @fct2
fct3x:= @nth_power;
fct_table(0.1, 0.7, 0.1, fct1x, 'Sin(x)');
fct_table(0.1, 0.9, 0.1, fct2x, 'Cos(x)');
fct1x:= fct_table2(0.1, 0.7, 0.1, fct1x, 'Sin(x)');
fct1x:= fct_table2(0.1, 0.7, 0.1, @fct1, 'Sin(x) Closure scheme');
mes:= 0.5;
fct1x(mes);
writeln('back from pointer without schema '+floatToStr(mes))
fct1(mes);
writeln('back from pointer closure schema '+floatToStr(mes))
ms:= roundfloattoStr(power(2,10),0);
writeln((ms))
assert2(ms='1024', ' must be 1024')
assert2(roundfloattoStr(power(2,10),0)='1024', ' must be 1024')
//ClosureGag;
End.
Interfaces for Testing
function GetShortPathName(lname, sname: pchar; mpath: longint): bool; //external
function TFormMain_GetTime: TDateTime; //Function Enclosing
function RoundSchweiz(value: Extended):Extended; //Function Casing
Function TDaten3_BerechneMwStBetrag(aNettobetrag, aMwStSatz:Extended; //func delegate
function GetQuery(SQLCommand: string; aQuery: TQuery): TFields; //function object enclosing
procedure TDaten_CopyDS(t: TDataSet); //Procedure parameter as an object
function BinToInt(Value: string): Integer;
PROCEDURE fct1(var x: single);
PROCEDURE fct2(var x: single);
PROCEDURE nth_power(var x: single);
PROCEDURE fct_table(start, stop, step: single; fct: TMath_func;
Function fct_table2(start, stop, step: single; fct: TMath_func;
function generate_power_func(var n: single; fct: TMath_func): TMath_func;
Procedure ClosureGag;
Closures Studies and Sources:
http://tronicek.blogspot.com/2007/12/closures-closure-is-form-of-anonymous_28.html
http://www.javac.info/
def generate_power_func(n):
print "id(n): %X" % id(n)
def nth_power(x):
return x**n
print "id(nth_power): %X" % id(nth_power)
return nth_power
>>> raised_to_4 = generate_power_func(4)
id(n): CCF7DC
id(nth_power): C46630
>>> repr(raised_to_4)
'<function nth_power at 0x00C46630>'
>>> del generate_power_func
>>> raised_to_4(2)
16
PHP:
[/php]
Just found a somewhat simpler example ...
[php]# define a function within a function and return it to the calling scope
# the inner function can access variables in the calling function, i.e. its closure
def times(n):
def _f(x):
return x * n
return _f
t3 = times(3)
print t3 #
print t3(7) # 21
[/php]
Here the inner function _f() is the block of code of the closure, similar to the lambda code in the first example.
Now, we can put those two facts together by having a function return a "customized" version of an inner function. For example:
>>> def outer(x):
... def inner(y):
... return x+y
... return inner
...
>>> customInner=outer(2)
>>> customInner(3)
5
The trick that you want to notice in what's going on there is what happens to the value of x. The argument x
is a local variable in outer() and the behavior of local variables isn't normally very exciting. But in this
case, x is global to the function inner(). And since inner() uses the name, it doesn't go away when outer()
exits. Instead inner() captures it or "closes over" it. You can call outer() as many times as you like and each value of x will be captured separately.
The function that's returned is called a closure. The idea is potentially useful because we can specify part
of the behavior of a function based on data at runtime.
At this stage you might say, "OK, I followed your tedious explanation, but what good is such a thing? Is it
anything more than a curiosity for ordinary programming?" The answer to that is that it is occasionally
useful when something, such as a library interface, requires a function and you want to specify more than a
couple of them that are very similar.
Imagine that you're designing a GUI interface and you need six buttons that do similar things. Tkinter
buttons take a function to call as an argument and it would be tedious to write six very similar functions.
Instead you might do something like this:
from Tkinter import *
def makeButtonFunc(buttonName):
def buttonFunc():
print buttonName
return buttonFunc
class mainWin:
def __init__(self,root):
self.root=root
self.createWidgets()
return None
def createWidgets(self):
for buttonName in ("A","B","C","D","E","F"):
b=Button(self.root,text=buttonName,
command=makeButtonFunc(buttonName))
b.pack()
return None
def main():
root=Tk()
mainWin(root)
root.mainloop()
return None
if __name__=="__main__":
main()
That's clearly better than writing six functions that are virtually identical.
There are lots of people who like using closures. I, personally, don't. To me, it feels like using a subtle
trick and I prefer my programs to be as obvious as possible. In a similar situation, I'd use a Python object
with a __call__() method. If a Python object has that method and it's called as though it were a function,
that method is run. In a program I wrote, I'd probably replace makeButtonFunc() with something like:
class makeButtonFunc:
def __init__(self,buttonName):
self.buttonName=buttonName
def __call__(self):
print self.buttonName
Which would do the same thing. Of course, I'd give the class a different name.
Posted: Mon - September 18, 2006 at 08:07 PM Main Category: Geek
6.1 – Closures
When a function is written enclosed in another function, it has full access to local variables from the
enclosing function; this feature is called lexical scoping. Although that may sound obvious, it is not.
Lexical scoping, plus first-class functions, is a powerful concept in a programming language, but few
languages support that concept.
Let us start with a simple example. Suppose you have a list of student names and a table that associates
names to grades; you want to sort the list of names, according to their grades (higher grades first). You
can do this task as follows:
names = {"Peter", "Paul", "Mary"}
grades = {Mary = 10, Paul = 7, Peter = 8}
table.sort(names, function (n1, n2)
return grades[n1] > grades[n2] -- compare the grades
end)
Now, suppose you want to create a function to do this task:
function sortbygrade (names, grades)
table.sort(names, function (n1, n2)
return grades[n1] > grades[n2] -- compare the grades
end)
end
The interesting point in the example is that the anonymous function given to sort accesses the parameter
grades, which is local to the enclosing function sortbygrade. Inside this anonymous function, grades is
neither a global variable nor a local variable. We call it an external local variable, or an upvalue. (The
term "upvalue" is a little misleading, because grades is a variable, not a value. However, this term has
historical roots in Lua and it is shorter than "external local variable".)
Why is that so interesting? Because functions are first-class values. Consider the following code:
function newCounter ()
local i = 0
return function () -- anonymous function
i = i + 1
return i
end
end
c1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2
Now, the anonymous function uses an upvalue, i, to keep its counter. However, by the time we call the
anonymous function, i is already out of scope, because the function that created that variable (newCounter)
has returned. Nevertheless, Lua handles that situation correctly, using the concept of closure. Simply put,
a closure is a function plus all it needs to access its upvalues correctly. If we call newCounter again,
it will create a new local variable i, so we will get a new closure, acting over that new variable:
c2 = newCounter()
print(c2()) --> 1
print(c1()) --> 3
print(c2()) --> 2
So, c1 and c2 are different closures over the same function and each acts upon an independent instantiation
of the local variable i. Technically speaking, what is a value in Lua is the closure, not the function. The
function itself is just a prototype for closures. Nevertheless, we will continue to use the term "function"
to refer to a closure whenever there is no possibility of confusion.
Closures provide a valuable tool in many contexts. As we have seen, they are useful as arguments to higher
-order functions such as sort. Closures are valuable for functions that build other functions too, like our
newCounter example; this mechanism allows Lua programs to incorporate fancy programming techniques from the
functional world. Closures are useful for callback functions, too. The typical example here occurs when you
create buttons in a typical GUI toolkit. Each button has a callback function to be called when the user
presses the button; you want different buttons to do slightly different things when pressed. For instance
, a digital calculator needs ten similar buttons, one for each digit. You can create each of them with a
function like the next one:
function digitButton (digit)
return Button{ label = digit,
action = function ()
add_to_display(digit)
end
}
end
Anonymous in Delphi
with TForm.Create(self) do begin
BorderStyle := bsNone;
WindowState := wsMaximized;
Show;
end;
This JSR provides support for operating on an arbitrary "block of Java code", or body, which is either a
statement list, an expression, or a combination of both. We call the mechanism a closure expression.
Wrapping statements or an expression in a closure expression does not change their meaning, but merely
defers their execution. Evaluating a closure expression produces a closure object. The closure object
can later be invoked, which results in execution of the body, yielding the value of the expression (if
one was present) to the invoker. A closure expression can have parameters, which act as variables whose
scope is the body. In this case the invoker of the closure object must provide compatible arguments, which
become the values for the parameters.'