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 >>> >>> > > --
- Follow-Ups:
- Re: Re: Re: looping
- From: "E. Martin-Serrano" <eMartinSerrano@telefonica.net>
- Re: Re: Re: looping
- References:
- looping
- From: glenn kuhaneck <mcguyver128@yahoo.com>
- looping