Version:0.9 StartHTML:0000000105 EndHTML:0000055000 StartFragment:0000001037 EndFragment:0000054984 mXScriptasHTML
{***************************************************************
/*
* 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.'