MathGroup Archive 2010

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: Re: looping

  • To: mathgroup at smc.vnet.net
  • Subject: [mg106853] Re: [mg106766] Re: [mg106704] looping
  • From: Leonid Shifrin <lshifr at gmail.com>
  • Date: Mon, 25 Jan 2010 05:06:08 -0500 (EST)
  • References: <201001210955.EAA16523@smc.vnet.net>

Bobby,

this is one of the nice (IMO) features of local variables in Module. By
default, they have the attribute Temporary, which means that they are
destroyed once the execution exits Module. However, if some global symbols
refer to them (like the functions you've mentioned), they are not destroyed,
but kept in a symbol table. I use this trick all the time - this allows for
example  to share local variables (and functions) between several functions,
which enables us to implement something similar to classes in OOP (If I
remember corerctly, this idea has been fully exploited by Roman Maeder in
his implementation of OOP in Mathematica - classes.m package).

Have a look at my post in this thread, if you will

http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/ec4958c35f99758d/

there I implement the <pair> data type using this idea. In this thread:

http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/4d566f1993c252c8/

I used this trick to implement traversals of nested directory structure with
possible directory skips which can be set at run-time based on a skip
function provided by the user.

One of the many other ways to use this which I find useful is to propagate
exceptions of a given type without explicitly making the exception tag
global. One particular such use I discussed in the thread:

http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/bc41b4a5f51fbcb8/

Generally, this is a good option when you  want to make an essentially
global variable available to a given set of functions but not to others -
you make it like

Module[{youvar},

f1[args]:=(body-referring-to-yourvar)

f2[args]:=(body-referring-to-yourvar)

...
];

This is a cheap way to introduce namespaces without making a full-blown
package. This allows us to use some of the nice OOP-like stuff (such as
private variables / methods) without the need to employ a general OOP
machinery (that is, when we just need nice encapsulation but not so much
OO-style polymorphism / inheritance). As long as the user never uses
variables with a dollar sign in their names (which can possibly collide with
those generated by Module), this should be safe enough.

One use of this is to make functions with "memory", similar to static local
variables in C functions - some of the function's variables remember their
values in between function calls. For example, the following function will
produce the next Fibonacci number on demand, and yet it will be as fast as
the iterative loop implementation for generation of consecutive Fibonacci
numbers (since Module is invoked only once, when the function is defined):

In[1]:= Module[{prev, prevprev, this},
  reset[] := (prev = 1; prevprev = 1);
  reset[];
  nextFib[] := (this = prev + prevprev; prevprev = prev; prev = this)];


In[2]:=
reset[];
Table[nextFib[], {1000}]; // Timing

Out[3]= {0.01, Null}

In some cases, this can also improve performance, since some of the local
variables in a function can be made "semi-global" by this trick which may
eliminate the need of Module invocation in each function call, and the
associated overhead:

In[4]:=
Clear[f, f1];
f[x_, y_, z_] := Module[{xl = x, yl = y, zl = z}, (xl + yl + zl)^2];

In[6]:=
Module[{xl, yl, zl},
  f1[x_, y_, z_] := (xl = x; yl = y; zl = z; (xl + yl + zl)^2)];


In[8]:= test = RandomInteger[100, {10000, 3}];

In[9]:= f @@@ test; // Timing

Out[9]= {0.43, Null}

In[10]:= f1 @@@ test; // Timing

Out[10]= {0.15, Null}


I generally find this technique very useful, and also I think that it has
not been fully exploited (at least I did not fully exploit it yet). For
example, you can do nice things when you couple it with Dynamic
functionality, since Dynamic happens to work also on these Module-generated
variables.

It may however have some garbage-collection issues (I discussed this in the
first of the threads I mentioned above), since once you abandon such local
variables/functions, they will not be automatically garbage-collected by
Mathematica and can soak up memory (I have been bitten by this a few times).
Of course, this can be dealt with as well, it's just not automatic.

Regards,
Leonid






On Sat, Jan 23, 2010 at 5:18 PM, DrMajorBob <btreat1 at austin.rr.com> wrote:

> Thanks! I've just noticed something I don't understand, however.
>
> How can displayPanel[] be used outside the makeScorePanel function, when it
> uses studentInfo, text, buttons, and class -- all of which are local to the
> Module??
>
> Bobby
>
>
> On Sat, 23 Jan 2010 14:57:25 -0600, Leonid Shifrin <lshifr at gmail.com>
> wrote:
>
>  Hi Bobby,
>>
>> I agree - your modification makes it more elegant. I  particularly liked
>> your  use of indexed variables and RandomChoice.
>>
>> Cheers,
>> Leonid
>>
>>
>> On Sat, Jan 23, 2010 at 12:48 PM, DrMajorBob <btreat1 at austin.rr.com>
>> wrote:
>>
>>  I'd style this a bit differently:
>>>
>>> Clear[makeScorePanel];
>>> makeScorePanel[names : {__String}] :=
>>>  Module[{text, k = Length@names, name = RandomChoice@names,
>>>   studentInfo, classFlag = True, buttons, class, percent, next,
>>>   correct, questions},
>>>
>>>  "internal functions";
>>>  correct[_] = 0; questions[_] = 0;
>>>  percent[name_] /; questions[name] == 0 = 0;
>>>  percent[name_] := Round[100*correct[name]/questions[name]];
>>>
>>>  "display elements";
>>>
>>>  text = "Was the answer correct?";
>>>  next := (questions[name]++; name = RandomChoice@names);
>>>
>>>  class :=
>>>   If[classFlag,
>>>    Panel[Grid[
>>>      Prepend[Transpose[{names, getQuestionCount[],
>>>         getCorrectCount[], getPercent[]}], {"Name", "Questions",
>>>        "# correct", "% correct"}], Spacings -> 3,
>>>      Dividers -> Center]], ""];
>>>  buttons :=
>>>   Row[{Button["    Yes    ", correct[name]++; next],
>>>     Button["    No    ", next],
>>>     Button[If[classFlag, "Hide Roster", "Show Roster"],
>>>      classFlag = ! classFlag]}];
>>>  studentInfo :=
>>>   Panel[
>>>    Grid[{{"Name", name}, {"Questions",
>>>       questions@name}, {"Correct answers",
>>>       correct@name}, {"% correct", percent@name}},
>>>     Alignment -> Left]];
>>>
>>>  "exported functions";
>>>  Clear[getCorrectCount, getQuestionCount, getPercent,
>>>   displayPanel];
>>>  getCorrectCount[] := correct /@ names;
>>>  getQuestionCount[] := questions /@ names;
>>>  getPercent[] := percent /@ names;
>>>  displayPanel[] :=
>>>
>>>   Dynamic@Panel[Column[{studentInfo, text, buttons, class}]]
>>>  ];
>>> makeScorePanel[{"apple", "bob", "cat", "dog", "ear", "frog", "greg",
>>>  "hippo", "i9", "joe"}]
>>> displayPanel[]
>>>
>>> Bobby
>>>
>>>
>>> On Sat, 23 Jan 2010 06:30:56 -0600, Leonid Shifrin <lshifr at gmail.com>
>>> wrote:
>>>
>>>  Hi Glenn,
>>>
>>>>
>>>> You don't need a loop at all, if I understand your goal correctly. What
>>>> you
>>>> need is a bit of Dynamic functionality. Hope this will get you started:
>>>>
>>>> Clear[makeScorePanel, getScores, getCalled, getPrcntp, bpanel];
>>>> makeScorePanel[names : {__String}] :=
>>>>  Module[{text, n = RandomInteger[Length[names] - 1] + 1, studentInfo,
>>>>   classFlag = False, buttons, class, percent, next,
>>>>   score , called , prcnt},
>>>>  Clear[getScores, getCalled, getPrcntp, bpanel];
>>>>  score = called = prcnt = Table[0, {Length[names]}];
>>>>  getScores[] := score;
>>>>  getCalled[] := called;
>>>>  getPrcntp[] := prcnt;
>>>>  text := "Was the answer correct?";
>>>>  next := (called[[n]]++; n = RandomInteger[Length[names] - 1] + 1);
>>>>  class :=
>>>>   If[classFlag,
>>>>     Panel[Grid[
>>>>      Transpose[{names, score,
>>>>        IntegerPart[100*score/(called /. (0 -> 1))]/100.}
>>>>       ],
>>>>      Spacings -> 3, Dividers -> Center]],
>>>>    ""];
>>>>  buttons :=
>>>>   Row[{
>>>>     Button["    Yes    ", score[[n]]++; next],
>>>>     Button["Class", classFlag = ! classFlag],
>>>>     Button["    No    ", next]}];
>>>>  percent :=
>>>>   If[called[[n]] == 0, 0,
>>>>    IntegerPart[100*score[[n]]/called[[n]]]/100.];
>>>>  studentInfo :=
>>>>   Panel[Grid[
>>>>     {{"Name", names[[n]]},
>>>>      {"Times called", called[[n]]},
>>>>      {"# of correct answers", score[[n]]},
>>>>      {"Your %     ", percent}},
>>>>     Alignment -> Left]];
>>>>  bpanel[] :=
>>>>   Dynamic@Panel[Column[{
>>>>       studentInfo,
>>>>       text,
>>>>       buttons,
>>>>       class
>>>>       }]]];
>>>>
>>>> The way to use: first call the makeScorePanel[] with your actual list of
>>>> names, then call bpanel[]:
>>>>
>>>> makeScorePanel[{"apple", "bob", "cat", "dog", "ear", "frog", "greg",
>>>>  "hippo", "i9", "joe"}]
>>>>
>>>> bpanel[]
>>>>
>>>> You stop when you feel like it. You can also call   getScores[],
>>>> getCalled[]
>>>> ,  getPrcntp[] at any time to get the current state of these variables
>>>> (if
>>>> you need that for further processing).
>>>>
>>>> Regards,
>>>> Leonid
>>>>
>>>>
>>>>
>>>> On Thu, Jan 21, 2010 at 12:55 PM, glenn kuhaneck <mcguyver128 at yahoo.com
>>>> >wrote:
>>>>
>>>>  this code is supposed to randomly select a student from a list, keep
>>>>
>>>>> track
>>>>> of how many times he/she has been called, and how many answers they
>>>>> have
>>>>> gotten correct.
>>>>>
>>>>> I am having problems getting the following code to repeat on request: i
>>>>> have tried do loops, while loops, and labels none have worked.   please
>>>>> help
>>>>>
>>>>> These are the sample lists I am using for the code below
>>>>> name = ( {
>>>>>  {"apple"},
>>>>>  {"bob"},
>>>>>  {"cat"},
>>>>>  {"dog"},
>>>>>  {"ear"},
>>>>>  {"frog"},
>>>>>  {"greg"},
>>>>>  {"hippo"},
>>>>>  {"i9"},
>>>>>  {"joe"}
>>>>>  }); score = ({
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0}
>>>>>  }); called = ({
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0}
>>>>>  }); prcnt = ( {
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0},
>>>>>  {0}
>>>>>  } );
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> this is the code to manipulate the lists created above
>>>>> repeat = "yes";
>>>>> c = "wrong";
>>>>> l = Length[name];
>>>>> n = RandomInteger[l - 1] + 1;
>>>>>
>>>>>
>>>>> Label[begin]
>>>>> "Student #"
>>>>> n
>>>>> name[[n]]
>>>>> called[[n]] += 1;
>>>>> "# times called"
>>>>> called
>>>>> "Old Score"
>>>>> score
>>>>> Input["Was the answer correct?", Button["Yes", c = "Right"]];
>>>>> If[c == "Right", score[[n]] += 1]; c
>>>>> "New Score"
>>>>> score
>>>>> "Your # of Correct Answers"
>>>>> score[[n]]
>>>>> "Your %"
>>>>> prcnt[[n]] = score[[n]]/called[[n]]
>>>>> "Class %"
>>>>> prcnt
>>>>> Input["Another ?", Button["No", repeat = "no"]];
>>>>> If[repeat == "yes", Goto[begin], Goto[end]];
>>>>> Label[end];
>>>>> prcnt[[n]]
>>>>>
>>>>>
>>>>>
>>>>> thank you for your assistance,
>>>>>  Mr. Glenn J. Kuhaneck
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>> --
>>> DrMajorBob at yahoo.com
>>>
>>>
>
> --


  • References:
    • looping
      • From: glenn kuhaneck <mcguyver128@yahoo.com>
  • Prev by Date: Re: Re: Mathematica gets stuck, only thing I can do is quit kernel
  • Next by Date: Re: Re: Mathematica gets stuck,
  • Previous by thread: Re: Re: looping
  • Next by thread: Re: Re: Re: looping