Jump to content
Sign in to follow this  
Guest Vorlin

Loops, If's, Case and other coding stuff...

Recommended Posts

Guest Vorlin

I had a thought while I was at work... perhaps we can create a resource for all of us and do it the easy way. There are many constructs that a lot of us struggle with and by that I mean that we can't get things put together solidly enough to even start to play with them and experiment. I hope that this thread will eventually become a sticky and be a resource for the syntax of various constructs, in each different kind of situation.One rule: To ask for something, you have to offer something... a tip, an example syntax, an explanation of why something works the way it does... anything. Offer something you know and then ask for something you'd like someone else to explain. I'll kick it off:************************This isn't the one I was hoping to put here but testing revealed a syntax problem in my first choice that I haven't been able to fix yet...It's been mentioned to use macros in order to save yourself a lot of work when it comes to typing out repeated values such as

Share this post


Link to post
Share on other sites

Scott,I'll give my two cents, starting inverse:*****************************************************%((@c:WaypointAirportFrequenciesNumber) s2 0 !=)--> this line retrieves max WaypointAirportFrequenciesNumber and tests whether it is greater than 0, if true (1) then the entire code that follows will execute:%{if}%(0 sp1) ---> The initial "index" for WaypointAirportFrequenciesNumber%{loop} ---> Start the loop until (#) condition %(l1 (>@c:WaypointAirportCurrentFrequency)) --> assigns the index value to WaypointAirportCurrentFrequency, so to be able to retrieve the related var values . Index starts at 0 and raises until it is equal to l2 (max WaypointAirportFrequenciesNumber)%((@g:listCurrent) l1 == (@g:listItems) 1 ==and)%{if}{blnk}%{end}%((@c:WaypointAirportFrequencyName))%!s!{nr}t%((@c:WaypointAirportFrequencyLimit))%{case}%{:1}{dpl=RX}%{:2}{dpl=TX}%{end}t%((@c:WaypointAirportFrequencyValue,Mhz) 100 * near d 100div)%!03d!.{fnt1}%(100 %)%!02d!{fnt}n--->Does a bunch of conditional prints %(l1 ++ s1 l2 <) --->(#) condition, increments the "index" by one and compares it with l2 (max WaypointAirportFrequenciesNumber)%{next}---> loops while the stack's last value is true (1) otherwise it resumes executing the rest of the code%{end}***********************************************************Hope that I didn't miss anything :-). Notice that this kind of {loop/next} construction applies only to structures, and is very useful just when dealing with conditional string outputs. But if you need to work with pure data, IMHO it is way better to use stack operators directly. Regarding your first "big" macro:Suppose you write a 100-line macro named "Macro1" and then call it once in a gauge like @Macro1(lvar,value,etc)It would be exactly the same to put the entire group of lines whithin the and not use the macro.Remember that macros are recognized only by code inside the gauge where they are definend, and are not visible by code inside other gauges. However, you can "repeat" them into as many gauges as you wish, even using the same name.And again, in your "st"{:1}{:2} macro example, what you are placing inside it is a whole set of string code, it will never return a stack value because it is precisely a string construction. So you can't do this "@st(nnn) (>LVarValue)" UNLESS a lonely and "isolated" value is left forgotten (or ex-profeso) in any of those %((var))% stack's portions.As already stated, my two (dollar would be ok for you?) cents ...Tom

Share this post


Link to post
Share on other sites
Guest Vorlin

Tom,Thanks for the pointers. Let's see if I have this right:>%((MAX_ITERATIONS) s2 0 !=)>%{if}Places Max_Iterations into s2, compares s2 to 0. If false, run the code below.>%(0 sp1)Set 0 into sp1, this will be used to track the current iteration number, beginning with 0 and incrementing. >%{loop}Start the silly loop already... what are you waiting for?>%(l1 (>@c:WaypointAirportCurrentFrequency))I'm still fuzzy on the retrieval setup here... maybe it will come to me as I play with it some more. You said: >--> assigns the index value to>WaypointAirportCurrentFrequency, so to be able to retrieve the>related var values . Index starts at 0 and raises until it is>equal to l2 (max WaypointAirportFrequenciesNumber)I kind of got lost there... but that's more about retrieving the info than running the loop itself.>%((@g:listCurrent) l1 == (@g:listItems) 1 ==>and)%{if}{blnk}%{end}>%((@c:WaypointAirportFrequencyName))%!s!{nr}t>%((@c:WaypointAirportFrequencyLimit))%{case}%{:1}{dpl=RX}%{:2}{dpl=TX}%{end}t>%((@c:WaypointAirportFrequencyValue,Mhz) 100 * near d 100>div)%!03d!.{fnt1}%(100 %)%!02d!{fnt}>n>--->Does a bunch of conditional prints Like you said... it does stuff for each iteration.>%(l1 ++ s1 l2 <) Here's the baby... now that you did stuff, pull out l1 increment it and then shove it back into s1. Oh, and by the way, while you do that, check to make sure that it's less than the value we set into l2 at the start of all this. If it's still less even after being incremented:>%{next}Go around again. Otherwise, if it's equal to l2 then:>%{end}***************>Hope that I didn't miss anything :-). Notice that this kind of>{loop/next} construction applies only to structures,>and is very useful just when dealing with conditional string>outputs. But if you need to work with pure data, IMHO it is>way better to use stack operators directly. So this would or would not be the same sort of loop you would use for a timer?>Regarding your first "big" macro:>>Suppose you write a 100-line macro named "Macro1" and then>call it once in a gauge like >@Macro1(lvar,value,etc)>It would be exactly the same to put the entire group of lines>whithin the and not use the macro.Right, it's only when you have to call the same things many times over, not just one or two, that the macro begins to show any real benefit. Also, it only benefits the coder and his or her fingers, not the code... because the code is compiled with the full macro spelled out everywhere it gets called.>Remember that macros are recognized only by code inside the>gauge where they are definend, and are not visible by code>inside other gauges. However, you can "repeat" them into as>many gauges as you wish, even using the same name.True, but if the returned value of a macro can be placed into a numeric L:Var, then that L:Var could be seen and used by another gauge. The question is, how to get " @Macro (>L:Var, number) " to work... if it can be done.>And again, in your "st"{:1}{:2} macro example, what you are>placing inside it is a whole set of string code, it will never>return a stack value because it is precisely a string>construction. So you can't do this "@st(nnn) (>LVarValue)">UNLESS a lonely and "isolated" value is left forgotten (or>ex-profeso) in any of those %((var))% stack's portions.I know in the above I used strings, but what I'm uncertain of is would the numeric value of an A:Var be able to be called up by a macro and inserted into an L:Var, without the string typing?IE:Instead of using:%{:7}%((A:Autopilot heading lock dir, degrees))%!3d!Could we use:%{:7}%(A:Autopilot heading lock dir, degrees)%or:%{:7}(A:Autopilot heading lock dir, degrees)Or whatever syntax returns the 3 digit numeral that's in that variable.I think that I have seen such case statements used to set {img} and call macros... I wonder...1:00 AM here... work in the morning... to be continued...Tom, once again, thanks for clearing the original question up!Scott / Vorlin

Share this post


Link to post
Share on other sites

Scott,*****************************************>%((MAX_ITERATIONS) s2 0 !=)>%{if}Places Max_Iterations into s2, compares s2 to 0. If false,run the code below.******************It's the other way. If TRUE, then run the code below.As for REGISTERS, they are memory areas used to store values that come from the stack. Instead of saving a value into a LVar o GVar, you can save it straight into a register and be able to retrieve it whenever is needed across the entire gauge. They are reset to 0 at each FS cycle, and also when a "c" operator is placed on the stack. There are 50 available, from 0 to 49.To save a value into a register WITHOUT removing it from the stack, just precede its number with "s": 45 s0 ; (LVar,unit) s3If you need to save and remove the value, write "sp" before the number: 56.34 sp4 etcTo recall a register's value, write "l" before the number : l0, l30, etc................When working with gpsdll routines, it is necessary first to pass the parent function an index so the related child funtions will return their proper valuesFor example:7 (>@c:WaypointAirportCurrentFrequency)--> means you want to make the current frequency the one is situated on the 7th position.(@c:WaypointAirportFrequencyName) --> will put on the stack the name of the current airport frequency, which means the same as to be the name of the 7th airport frequency.......................******************So this would or would not be the same sort of loop you woulduse for a timer?***************I don't know what kind of timer you are speaking of.**************************True, but if the returned value of a macro can be placed intoa numeric L:Var, then that L:Var could be seen and used byanother gauge. The question is, how to get " @Macro(>L:Var, number) " to work... if it can be done.********************Think of macros as an extension of a very "huge" stack line.Suppose you have this simple one:34 5 + 38 + (>LVar1,unit)Now using a macro:38 + 34 5 + @My (>LVar1,unit)Translate this easy example to a complex macro struct and it will behave the same.Hope all these stuff was clear enough :-)Tom

Share this post


Link to post
Share on other sites
Guest Vorlin

Tom,I've been having trouble thinking clearly lately... I've been taking a lot of vitamins and some days they help and others they don't. I've requested Monday off to try to clear my head... I've taken 2 checks at work, 2 had the wrong amount and one had the wrong company name.That's when you know you're having trouble concentrating!I'm only going to touch on 3 points of the above that I can think of right now. 1) The idea of registers is one I know all too well. I used to have to diagnose what crashed a computer by reading the register contents in binary coded octal and determining what line was being executed at the time of the crash and what memory slot was being accessed. This was how we ID'd a bad memory chip / board. What I'm having trouble getting comfortable with is the syntax... but you have listed the basics, now I'll just have to play with it.2) This:>> %((MAX_ITERATIONS) s2 0 !=)>> %{if}>> Places Max_Iterations into s2, compares s2 to 0. If false,>> run the code below.******************>It's the other way. If TRUE, then run the code below.Ummm.... I was really confused for a second there but I see what you meant now! ROFL.... I had it right, but didn't express myself clearly. What I was saying is that if the test of MaxIterations compared to 0 was false, then run the code below. Of course, if the test is false, then the statement as a whole is true, which triggers the code in the IF.What I should have said is "If MaxIterations isn't 0, then run the code below..."3) Macros:34 5 + 38 + (>LVar1,unit)Is the same as:38 + 34 5 + @My (>LVar1,unit)Right, that's super easy. What I'm having a problem with is:---case code here-- (>L:MyVar)I have been doing a lot of graphics work over the past couple of days and so haven't had time to try something like this pseudo-code:@2 1 == if{( @1 1 ==) if{ (A:Heading, number) (>L:CV1 ) } ( @1 2 ==) if{ (A:OBS1, number) (>L:CV1 ) } ( @1 3 ==) if{ (A:OBS2, number) (>L:CV1 ) } etc, etc..........}@2 2 == if{( @1 1 ==) if{ (A:Heading, number) (>L:CV2 ) } ( @1 2 ==) if{ (A:OBS1, number) (>L:CV2 ) } ( @1 3 ==) if{ (A:OBS2, number) (>L:CV2 ) } etc, etc..........}@2 3 == if{( @1 1 ==) if{ (A:Heading, number) (>L:CV3 ) } ( @1 2 ==) if{ (A:OBS1, number) (>L:CV3 ) } ( @1 3 ==) if{ (A:OBS2, number) (>L:CV3 ) } etc, etc..........}Then when I need to call any A:Var for the current value, all I'd need to do is @My(GaugeWanted, Var to put it in)Such a setup can allow all gauges to be written to work with L:CV(n) (CurrentVar#).This should work if the macros were pasted to each gauge... I'm playing with the idea of setting up things with the macro in an include gauge and using L:Vars to trigger it, send in and retrieve data. I'm still working with it in my mind and my greatest concern is update lag and collisions between 2 or 3 gauges making requests. The include may need 4 or 5 macros that all do the same thing, but are repeated to spread the workload around and avoid collisions.It's an idea... whether it's a good one, a bad one or a silly one remains to be seen... but it's an idea.Thanks again for the tips! I'll have to send you a gauge when I finalize it so that you can see the graphics. As always, there are things to sort out. My next obstacle seems to be image resolution... and I may need to write in C++ in order to be able to keep the images looking crisp.Scott / Vorlin

Share this post


Link to post
Share on other sites
Guest Tomcattwo

Scott / Vorlin,Did you ever try the code snippet you posted?

I have been doing a lot of graphics work over the past coupleof days and so haven't had time to try something like thispseudo-code:@2 1 == if{( @1 1 ==) if{ (A:Heading, number) (>L:CV1 ) } ( @1 2 ==) if{ (A:OBS1, number) (>L:CV1 ) } ( @1 3 ==) if{ (A:OBS2, number) (>L:CV1 ) } etc, etc..........}@2 2 == if{( @1 1 ==) if{ (A:Heading, number) (>L:CV2 ) } ( @1 2 ==) if{ (A:OBS1, number) (>L:CV2 ) } ( @1 3 ==) if{ (A:OBS2, number) (>L:CV2 ) } etc, etc..........}@2 3 == if{( @1 1 ==) if{ (A:Heading, number) (>L:CV3 ) } ( @1 2 ==) if{ (A:OBS1, number) (>L:CV3 ) } ( @1 3 ==) if{ (A:OBS2, number) (>L:CV3 ) } etc, etc..........}Then when I need to call any A:Var for the current value, allI'd need to do is @My(GaugeWanted, Var to put it in)Such a setup can allow all gauges to be written to work withL:CV(n) (CurrentVar#).
Did it work? If so that seems like an excellent way to use a macro for indexing to store a passed variable.R/TC2

Share this post


Link to post
Share on other sites

Hi,I quickly adapted Scott's example to show a way that works great when dealing with arrays of LVars :Suppose you have an array of 100 lvars and want to assign a value to the index 4, l0 @1 == if{ l3 l2 l1 0 4 l20 case (>L:CV@1,number) }And the calling code:(L:Index,enum) sp0 (* The index value, 4 in this case *)(A:Heading, number) sp1(A:OBS1, number) sp2(A:OBS2, number) sp3(L:HeadObs1Obs2,enum) sp20 (* A counter from 1 to 3 that tells whether Heading, OBS1 or OBS2 would be used *)@My(1) @My(2) @My(3) @My(4) ...@My(100) Only (L:CV4,number) will be updated.This is very simple, but you can handle multidimensional arrays as well with few lines of code. Tom

Share this post


Link to post
Share on other sites
Guest Tomcattwo

Tom, brilliant as usual! Thanks for your reply - this looks like a dandy way to cotrol arrays of stored data. With some modification, the concept should be able to work well in the RMS gauge I am currently working on. Thank you!R/TC2

Share this post


Link to post
Share on other sites
Guest Tomcattwo

Ok Tom, I've shown in my gauge that I can transfer an index into a macro and have it use that index for storing a value into that indexed variable.Here's the macro:@2 (>L:ch@1,enum)and the call is:@chst(211,11811).This works fine, and loads (L:ch211,enum) with the value 11811. However, when I tried to pass a defined L variable back, to either call up or store into an indexed variable, it doesn't seem to work. For example:The Macro:@2 (>L:ch@1,enum)The call:@chst((L:achan,enum),11811)This doesn't work, and results in an "undefined" LVar.Similarly,Macro:(L:ch@1,enum) (>L:afreq,enum) (>L:scratch,enum)and the call:@acfreq((L:achan,enum))does not load the defined value of (L:achan,enum) into either of the (L:afreq,enum) or (L:scratch,enum) variables.I read in another thread that (L:Var,unit) can be passed in a call as a valid argument to a macro, but it doesn't look like a LVar argument can be used within the structure of another LVar in a macro. Can you confirm that this is the case? If so, might you know of any other way to send an indexed value in a macro call to a LVar within a macro? I tried it just now using a register:The Macro:(L:ch@1,enum) (>L:afreq,enum) (>L:scratch,enum)The call:(L:achan,enum) sp0 @acfreq(l0)failed to send the value of (L:achan,enum), stored in register 0, to call up (L:ch[l0's value, equal to (L:achan,enum)], enum). Instead, it created a LVar with the name "chl0". So that method didn't work either.R/TC2

Share this post


Link to post
Share on other sites
Guest Tomcattwo

To continue...in another thread, Tom provided an outstanding answer to the following question:

>would you be kind enough to explain the significance of the @>character, that is, what does @1, @4, etc. achieve?Sure The @ character symbolizes a paramater passed to a macro, and the number to the right is the order in the sequence of the total parameters passed, each of them separated by comma.For example @1 2 * @2 + @3 +Then, calling @Macro1(10,20,30) will pass 10 to @1, 20 to @2 and 30 to @3.Now, what kind of values can receive a parameter "@" ?1) literal stack values : @Macro1(10)2) A,L,G var values : @Macro1((A:Indicated Altitude, feet)) 3) Register values : @Macro1(l0,l1,l2)4) Part of var names : @Macro1(A:Indicated Altitude) or @Macro1(feet)5) Macro names : @Macro1(@NestedMacro)6) Resultant stack values : @Macro1(5 2 *,4 3 +) will pass 10 and 77) "Popped" stack values : 20 @Macro1(10) will pass 10 to @1 AND 20 to @2 if that exists in the called This is an essential description, hope it was clear enough
However, in experimenting tonight, I've found the following clarification to share with you all which may help your ubderstanding of the way macro arguments work:If the argument sent is embedded within another variable's name definition, the argument sent is a literal string, not the value that the

Share this post


Link to post
Share on other sites

Well, I see you've figured out the whole thing by yourself :-)I've stated in a bunch of earlier threads that macros are NOT functions but literal replacement of text. Therefore, anything you pass as an argument will be "inserted" in the interpreted code "as is" and, as you cleverly found, will give different results on each case, depending on its final meaning.You may notice that I wrote @My(1) @My(2) ..etc to reference a call to each "indexed" Lvar, precisely because of the limitation stated by you. Surely would be way better to have the possibility to write @My((L:Index,enum)) and pass the value of the var and not a simple text (which would be the case if a macro could work like a real function);I praised for that to be supported in FSX XML, maybe using the current "index" of an lVar( currently (L:Var1:1,number) and (L:Var1:2,number) are recognized as different) but MS didn't make any change on this, probably because it is technically impossible with the current schemas.Despite of this, it is not so tough to write a string of 100 macros if you use short names and pass only one argument :-)Also it is rather easy to handle multidim arrays, using a bunch of (a bit complex) macros...Tom

Share this post


Link to post
Share on other sites
Guest Tomcattwo

Hmmm, Tom, the following gives me cause to think:

7) "Popped" stack values : 20 @Macro1(10) will pass10 to @1 AND 20 to @2 if that exists in the called
What if I tried something like:Assumptions: (L:ch231,enum) is an LVar where I want to store a freq;(L:achan,enum) has a value of 231 assigned elsewhere;(L:myfreq,enum) has a value of 12345 assigned elsewhereThe Macro:@1 (>L:ch@2,enum)The call:(L:achan,enum) @afreq((L:myfreq,enum))Wouldn't this then "pop" the value of (L:achan,enum) off the stack (not the string representation of "(L:achan,enum)" )and pass the popped value of 231 to the macro so I would end up with:12345 (>L:ch231,enum) when the macro executes??Might this work? I'll try it tonight...R/TC2

Share this post


Link to post
Share on other sites
Guest rkruijer

Hello ScottI was looking at your macro post today and wonder:>This way, if you want the airspeed to be displayed digitallythen all you need to do is use @st(8).Like this? @st(8)Just to make sureRoelof

Share this post


Link to post
Share on other sites

TC2,<>No it won't, unfortunately. The post you quoted <<20 @Macro1(10)>> will work if the macro contains @1 and @2 as stack values and not being a string part of a variable. Funny to see you are doing the same tests I did almost a year ago...the best way to learn XML is never give up testing :-)Tom

Share this post


Link to post
Share on other sites
Guest Tomcattwo

Of course, you are right, Tom :) Thought I'd give it a try anyway, but it was close! Here's the results...The code:@1 (>L:ch@2,enum)%((L:ch456, enum) 100 /)%!5.2f!%((L:ch123, enum) 100 /)%!5.2f!13600 (>L:myfreq,enum) 123 (>L:mychan,enum) (L:mychan,enum) @test((L:myfreq,enum))11800 (>L:myfreq2,enum) 456 (>L:mychan2,enum) (L:mychan2,enum) @test((L:myfreq2,enum))And the results:1) The gauge worked (i.e. syntax was OK).2) Using XML_ID, the L:Vars ended up with the following values: (L:myfreq,enum)=13600 (L:myfreq2,enum)=11800 (L:mychan,enum)=123 (L:mychan2,enum)=456 as expected. When the macro ran: a. Macro called first time: @1=13600, @2 pulled and popped from the stack with a value of 123 b. Macro created (L:ch123,enum)=00000 c. Macro stored (L:myfreq,enum)=13600 into (L:ch,enum) (not into (L:ch123,enum) as hoped. d. Macro called the second time: @1=11800, @2 pulled and popped from the stack with a value of 456 e. Macro created (L:ch123,enum)=00000 f. Macro stored (L:myfreq2,enum)=11800 into (L:ch,enum) (not into (L:ch456,enum) as hoped.3) Final result: (L:ch123,enum)=00000 (L:ch456,enum)=00000 (L:ch,enum)=11800 (the last macro operation performed)So...close! But no cigar! It DID pull the VALUE from the stack (not the "string representation") and passed it to the macro as a valid argument, but it did not allow that argument to be concatenated with "ch" to identify the proper variable to place the @1 argument into. Looks like I have once again proven that the Master (Tom Taquilo) was right on the money, and that we will not be able to use macros for array functionality.So I am stuck with lots of "if{" statements to do a chore that, with a simple variable array call, could have been one short line. Oh well! Back to coding if{ statements :)R/TC2(and props to the Master...Tom T.)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Tom Allensworth,
    Founder of AVSIM Online


  • Flight Simulation's Premier Resource!

    AVSIM is a free service to the flight simulation community. AVSIM is staffed completely by volunteers and all funds donated to AVSIM go directly back to supporting the community. Your donation here helps to pay our bandwidth costs, emergency funding, and other general costs that crop up from time to time. Thank you for your support!

    Click here for more information and to see all donations year to date.
×
×
  • Create New...