Case Statements: flow value set into another?
Mike_HWG
Member Posts: 104
Please bear with my lead-up before asking a question.
Background
In NAV, we have:
In C++
Question
Is there an equivalent NAV expression to match the C++ code below? I'm trying to say "If my case statement matches value set 1, start doing something. If my case statement matches value set 2 or 3, do both their stuff. If my case statement only matches value set 3, only do that stuff"
Background
In NAV, we have:
CASE <expression> OF
<value set 1> :
<statement 1>;
<value set 2> :
<statement 2>;
<value set n> :
<statement n>;
[ELSE <statement n+1>]
END;
In C++
switch( <expression> )
{
case <value set 1> :
<statement 1>;
break;
case <value set 2> :
<statement 2>;
break;
case <value set n> :
<statement n>;
break;
[default: <statement n+1>;]
}
Question
Is there an equivalent NAV expression to match the C++ code below? I'm trying to say "If my case statement matches value set 1, start doing something. If my case statement matches value set 2 or 3, do both their stuff. If my case statement only matches value set 3, only do that stuff"
switch( <expression> )
{
case <value set 1> :
<statement 1>;
break; //The code will hit the break and leave this structure
case <value set 2> :
<statement 2>;
//Notice I didn't say break! Value set 2 will flow on to value set 3!
case <value set 3> :
<statement 3>;
break;
}
Michael Hollinger
Systems Analyst
NAV 2009 R2 (6.00.34463)
Systems Analyst
NAV 2009 R2 (6.00.34463)
0
Answers
-
There's no exact replacement. One suggestion provided that <expression> has no side effects was
CASE <expression> OF <value set 1> : <statement 1>; <value set 2>, <value set n> : BEGIN IF <expression> IN [<value set 2>] THEN <statement 2>; <statement n>; END; [ELSE <statement n+1>] END;Another suggestion was to use a sequence of IF statements instead taking into account when <statement> alters the result of <expression>.0 -
Mike_HWG wrote:Is there an equivalent NAV expression to match the C++ code below? I'm trying to say "If my case statement matches value set 1, start doing something. If my case statement matches value set 2 or 3, do both their stuff. If my case statement only matches value set 3, only do that stuff"
switch( <expression> ) { case <value set 1> : <statement 1>; break; //The code will hit the break and leave this structure case <value set 2> : <statement 2>; //Notice I didn't say break! Value set 2 will flow on to value set 3! case <value set 3> : <statement 3>; break; }
Sure, try:CASE TRUE of value in [1] : <statement 1>; value in [3] : <statement 3>; value in [2,3] : <statement 2>; <statement 3>; END;
Using the "CASE TRUE of" and "IN []" you can generally achieve what ever you want.David Singleton0 -
Not sure about that C++ command, but in a C/AL CASE statement it only executes the one leg of the control value. Say you have a control variable MyInt with value 1, it will only execute the code in 1.
MyInt := 1; CASE MyInt OF 1: MyInt := 2; 2: MyInt := 3; ELSE END;
After this CASE statement is done, MyInt will be 2, because when the CASE was evaluated it only executed the leg for that value. Setting the control value to 2 as part of the code in 1 will not make it execute that code.
By the way, you can evaluate multiple values in a CASE statement like this:CASE MyInt OF 1: MESSAGE('it is one!'); 2,3: MESSAGE('it is two or three'); ELSE MESSAGE('not one, not two, not three!'); END;0 -
David's code is equivalent toDavid Singleton wrote:Sure, try:CASE TRUE of value in [1] : <statement 1>; value in [3] : <statement 3>; value in [2,3] : <statement 2>; <statement 3>; END;
Using the "CASE TRUE of" and "IN []" you can generally achieve what ever you want.CASE value of 1 : <statement 1>; 3 : <statement 3>; 2,3 : <statement 2>; <statement 3>; END;The 2nd 3 in both, David's code and my replacement given here is superfluous in that it newer will have any effect on what is executed. C/AL only executes the statements in the first matching branch, then jumps to the END.
I suppose the repeating of <statment 3> is objectionable.
David's suggestion can easily be turned intoIF value in [1] THEN <statement 1>; ELSE IF value in [3] THEN <statement 3>; ELSE IF value in [2,3] THEN BEGIN <statement 2>; <statement 3>; END;which does exactly the same as the CASE statement. (Note that I used the IF within the ELSE as though we had an IF THEN ELIF ELSE structure when it comes to indenting (and pacing BEGIN...END)). With a structured sequence of IF statements, you have more liberty, though, to approximate whatever you wantIF value IN [<value set 1>] THEN <statement 1>; ELSE BEGIN IF value IN [<value set 2>] THEN <statement 2>; IF value IN [<value set 2>,<value set 3>] THEN <statement 3>; END;0 -
No it does not. The outcome might be the same, but technically it goes about evaluating the control variable differently. In a CASE statement, it only evaluates the one single leg that evaluates to TRUE. With a nested IF statement, it could potentially evaluate multiple expressions.vaprog wrote:which does exactly the same as the CASE statement
To evaluate multiple values (meaning more than 2) for one control variable, a CASE statement is more efficient than a construction of nested IF statements, not to mention easier to read.0 -
DenSter wrote:
No it does not. The outcome might be the same, but technically it goes about evaluating the control variable differently. In a CASE statement, it only evaluates the one single leg that evaluates to TRUE. With a nested IF statement, it could potentially evaluate multiple expressions.vaprog wrote:which does exactly the same as the CASE statement
I think you may have misread, and vaprog lists what I believe makes sense: A CASE statement is essentially an IF - ELSE IF - ELSE IF(n). In both situations, CAL is going to find the first argument that evaluates true within the soonest sequential leg, execute it, and leave as soon as possible, WITHOUT touching any other leg.
If we look at David's code and pass in a value of 3, then the last leg will never be evaluated.CASE TRUE of value in [1] : <statement 1>; value in [3] : <statement 3>; value in [2,3] : //If we pass in 3, we never get here! <statement 2>; <statement 3>; END;
Therefore, if I ever want to mirror the functionality of my C++ snippet, I would want to use nested if/else statements. I could technically use the below code, but you can quickly see how nasty it becomes!case <expression> OF <value set 1>: <statement 1>; ELSE BEGIN IF <value set 2> THEN <statement 2>; IF <value> IN [<value set 2>, <value set 3>] THEN <statement 3>; END; END;
Therefore, I'm thinking vaprog's last code snippet is the implementation that we have to live with!Michael Hollinger
Systems Analyst
NAV 2009 R2 (6.00.34463)0 -
No I don't think I misread. A CASE statement is NOT the same as a nested IF/ELSEIF.Mike_HWG wrote:I think you may have misread
What makes no sense to me is that you would have two different blocks of code for the same value of the control variable, so I always assumed that having two blocks for value '3' was a typo.
My solution would be to have a block for values '2,3' in a CASE statement, and then have an IF statement inside there to catch value 2, which in that case seems to be the exception.CASE MyInt OF 1: <Statement 1>; 2,3: BEGIN IF MyInt = 2 THEN BEGIN <Statement 2>; END; <Statement 3>; END; ELSE <Statement x>; END;More than one way to skin a cat, whatever works for you
0 -
See for yourself. Save the code below in a new text file and import it into NAV. It's a form with a CASE and an ELSEIF structure. Set the value to 3 and debug both buttons. The CASE statement jumps right into value = 3, where the IF/ELSEIF evaluates each ELSE until the expression evaluates to TRUE. They are simply not the same.
OBJECT Form 50000 CASE ELSEIF { OBJECT-PROPERTIES { Date=11/14/11; Time=[ 1:37:00 PM]; Version List=; } PROPERTIES { Width=2640; Height=2420; } CONTROLS { { 1240060000;CommandButton;220;770;2200;550 ;CaptionML=ENU=CASE; OnPush=BEGIN CASE MyInt OF 1: MESSAGE('the value is one'); 2: MESSAGE('the value is two'); 3: MESSAGE('the value is three'); ELSE MESSAGE('the value is four or more'); END; END; } { 1240060001;CommandButton;220;1540;2200;550;CaptionML=ENU=IF-ELSEIF; OnPush=BEGIN IF MyInt = 1 THEN MESSAGE('the value is one') ELSE IF MyInt = 2 THEN MESSAGE('the value is two') ELSE IF MyInt = 3 THEN MESSAGE('the value is three') ELSE MESSAGE('the value is four or more'); END; } { 1240060002;TextBox;220 ;220 ;1700 ;440 ;SourceExpr=MyInt } } CODE { VAR MyInt@1240060000 : Integer; BEGIN END. } }0 -
DenSter wrote:See for yourself. Save the code below in a new text file and import it into NAV. It's a form with a CASE and an ELSEIF structure. Set the value to 3 and debug both buttons. The CASE statement jumps right into value = 3, where the IF/ELSEIF evaluates each ELSE until the expression evaluates to TRUE. They are simply not the same.
I'd be vary careful in comparing what the debugger does to what the compiler actually creates. I admit, I don't know the answer, but I haven't been very pleased in the past with how the debugger sometimes bounces around, especially after being used to Visual Studio.
That said, I see where you were going.My solution would be to have a block for values '2,3' in a CASE statement, and then have an IF statement inside there to catch value 2, which in that case seems to be the exception.
Hmm, not bad :-k I really like that.
However, when it came down to the actual implementation, I chose option X!
Here's what I've been working on. I chose this way because
-setting the Customer Group is better represented in a separate 'thought'.
The IF statement's evaluation speed is minimal. therefore, I'm not taking a critical hit every time I evaluate.IF CustomerGroupFilter <> CustomerGroupFilter::None THEN SETRANGE("Customer Group",CustomerGroupFilter) ELSE SETRANGE("Customer Group"); CASE CustomerGroupFilter OF CustomerGroupFilter::Customer: BEGIN CurrForm.DistrictBillToNoFilterCtrl.ENABLED(FALSE); DistrictBillToNoFilter := ''; END; CustomerGroupFilter::District: BEGIN IF DistrictBillToNoFilter <> '' THEN SETFILTER("District Bill-to No.",DistrictBillToNoFilter) ELSE BEGIN CurrForm.CustGroupCodeFilterCtrl.ENABLED(FALSE); DistrictBillToNoFilter := ''; CustGroupCodeFilter := ''; END; END; CustomerGroupFilter::"All Customers", CustomerGroupFilter::None: BEGIN CurrForm.DistrictBillToNoFilterCtrl.ENABLED(FALSE); CurrForm.CustGroupCodeFilterCtrl.ENABLED(FALSE); DistrictBillToNoFilter := ''; CustGroupCodeFilter := ''; END; END;
Thanks everyone for the discussions, I haven't broken down code to the basic building blocks like this for quite some time. It's very nice to step back, think it over, and 'tighten the mental belt!'Michael Hollinger
Systems Analyst
NAV 2009 R2 (6.00.34463)0 -
I think that's just a case of the NAV Debugger simplifying things. It looks like it jumps straight to the correct one, but there's no way it knows that it is the correct one unless the previous ones have been evaluated. It's not the first time I've seen the debugger mislead someone.
Here is some code proving they are actually evaluated. You'll get four messages, three from the CheckCondition function and one from the OnRun trigger.
OnRun()
CASE TRUE OF
CheckCondition(1, 3): MESSAGE('the value is one');
CheckCondition(2, 3): MESSAGE('the value is two');
CheckCondition(3, 3): MESSAGE('the value is three');
ELSE
MESSAGE('the value is four or more');
END;
CheckCondition(MyInt : Integer;Number : Integer) Result : Boolean
Result := (MyInt = Number);
MESSAGE('Condition: ' + FORMAT(Result));0 -
Well if you force it into a function with a return value of course it has to go through, but that doesn't mean that it doesn't jump right into the right value when they are literal.
The point was that a CASE statement is evaluated differently than a nested IF construction. My contention is that when evaluating multiple values for a single control variable, a CASE statement is more efficient and easier to read.0 -
DenSter wrote:My contention is that when evaluating multiple values for a single control variable, a CASE statement is more efficient and easier to read.
Agree.
I was going to say I totally disagree, but as it turns out:DenSter wrote:but that doesn't mean that it doesn't jump right into the right value when they are literal.
"While semantically these two code segments may be the same, their implementation is usually different. Whereas the IF..THEN..ELSEIF chain does a comparison for each conditional statement in the sequence, the SWITCH statement normally uses an indirect jump to transfer control to any one of several statements with a single computation."
It's always a good day when you learn something new.
0 -
matttrax wrote:the SWITCH statement normally uses an indirect jump to transfer control to any one of several statements with a single computation."
Wait a second on that... so how does it pick? Does it choose sequentially like I always thought, or pick based on quickest solution, or...? Can you then VERIFY that it will ALWAYS choose that jump?
I present the code below, which will print 'three or two'... basic, but you can see where I'm going with this. Can I ALWAYS rely on NAV to pick the first in the sequence that matches?//Pass in a value of 2 and see what happens! CASE i OF 1: MESSAGE('One'); 3,2: MESSAGE('Three or two'); 2: MESSAGE('Two'); END;Michael Hollinger
Systems Analyst
NAV 2009 R2 (6.00.34463)0 -
It's irrelevant what it does because you should not have code like that. Why would you EVER have more than one for any given value? As far as I'm concerned that's a programming error, and if you'd work for me I would have you fix it

If you need it to do something extra for the value 2, you catch it in the '2,3' block like this://Pass in a value of 2 and see what happens! CASE i OF 1: MESSAGE('One'); 3,2: BEGIN MESSAGE('Three or two'); IF i = 2 THEN MESSAGE('Two'); END; END;0 -
DenSter wrote:It's irrelevant what it does because you should not have code like that. Why would you EVER have more than one for any given value? As far as I'm concerned that's a programming error, and if you'd work for me I would have you fix it

Of course you wouldn't ever want to code that way, but saying it is irrelevant isn't a choice - you may one day have to clean it for someone else who is dead and gone (hello government coding job?
).
Unpredictable code branches seem like a recipe for spaghetti at lower levels, and there was a lazy person working on the parser logic
. Either we haven't found the answer or it's undocumented.
I know I'm being anal, but now I'm just really interested.
Anyways, mattrax said:mattrax wrote:"While semantically these two code segments may be the same, their implementation is usually different. Whereas the IF..THEN..ELSEIF chain does a comparison for each conditional statement in the sequence, the SWITCH statement normally uses an indirect jump to transfer control to any one of several statements with a single computation."
Though I find the following passage in the Microsoft Navision Development I - C/SIDE Introduction (8359B) Chapter 14, page 272 :-sWhen the CASE statement is executed, the expression is evaluated first. This
expression is sometimes called the selector. Then, in turn, each of the values in
each value set is evaluated.
If one of the values in the first value set matches the value of the expression, then
the first statement is executed. If one of the values in the second value set
matches the value of the expression, then the second statement is executed. This
continues until one of the statements is executed.
If the last value set has been reached and no statement has been executed, then
the ELSE clause is checked. If not, the second value set is checked. If there is an
ELSE clause, then its statement is executed. If there is no ELSE clause, then no
statement is executed for this CASE statement.
Note that only one of the statements is executed. If there is more than one value
set that contains the same value, only the first one of them is executed.
EDIT: Note that I won't be surprised if the MS documentation is wrong. It hasn't been the first time!Michael Hollinger
Systems Analyst
NAV 2009 R2 (6.00.34463)0 -
Note my quote was not from any NAV text. It was from a book on assembly language as I was curious how it usually works once everything is broken down that far. It's just talking about what normally happens, not what necessarily happens in NAV.
Anyway, why does this matter again?0 -
Don't know about you but I had the answer four days ago. Remove the ambiguity from the code, fix it and move on.Mike_HWG wrote:Either we haven't found the answer or it's undocumented.0 -
I'm such a geek. Why can't I leave well enough alone?
Here is some code I threw together to measure the execution time of evaluating which case is true one million times, then doing it 100 times and taking the average.Number := 1; //or 5 or 10 FOR j := 1 TO 100 DO BEGIN StartTime := TIME; FOR i := 1 TO 1000000 DO BEGIN CASE Number OF 1: BEGIN END; 2: BEGIN END; 3: BEGIN END; 4: BEGIN END; 5: BEGIN END; 6: BEGIN END; 7: BEGIN END; 8: BEGIN END; 9: BEGIN END; 10: BEGIN END; END; END; EndTime := TIME; TotalTime += EndTime - StartTime; END;Number := 1; FOR j := 1 TO 100 DO BEGIN StartTime := TIME; FOR i := 1 TO 1000000 DO BEGIN IF Number = 1 THEN BEGIN END ELSE IF Number = 2 THEN BEGIN END ELSE IF Number = 3 THEN BEGIN END ELSE IF Number = 4 THEN BEGIN END ELSE IF Number = 5 THEN BEGIN END ELSE IF Number = 6 THEN BEGIN END ELSE IF Number = 7 THEN BEGIN END ELSE IF Number = 8 THEN BEGIN END ELSE IF Number = 9 THEN BEGIN END ELSE IF Number = 10 THEN BEGIN END; END; EndTime := TIME; TotalTime += EndTime - StartTime; END;
The average time for CASE statements for the nth case:
1) 139.53
5) 498.73
10) 723.09
The average time for IF statements for the nth if:
1) 207.77
5) 915.67
10) 1745.51
So whatever is going on behind the scenes, CASE statements are faster. Regardless, correct programming will solve any issues you might have. That's going to be it for me on this as well.0 -
-
I gave up when I realized that developers really don't care about total cost of ownership of software, just go fro the geeky-est solution that wastes the most time.David Singleton0
-
-
DenSter wrote:What does TCO have to do with a question about syntax?
You said it in your earlier post. The benefit of CASE is readability, making code that is readable reduces the TCO of the system. It may take a few minutes more to write the code, and it may use more text, but in the long run it is worth it.David Singleton0 -
Ok well that's taking it a bit off topic, but I wouldn't necessarily bring a management concept such as TCO into a syntax discussion, and I disagree with your generalization that "developers don't care". I think you'll find that the best developers care a great deal. Matt writing that code actually proves that he cares so much about it, that he writes some code to actually measure the performance. In doing so, he reveals his true geekiness
, but for sure rooted in a desire to be as efficient as possible.
Surely not a reason to "give up"?0 -
Daniel I have no idea what you read into my post. Some how you twisted my words to make it look like I was disagreeing with Matt which is 100% the opposite of my intention. But as I said once I have given up on this thread so that's all for me.David Singleton0
-
OK we figured that one out offline, you were replying to someone else's postDavid Singleton wrote:Daniel I have no idea what you read into my post
0 -
Sorry all if I drug this further than it needed to go. When the things I used to take for granted no longer work here, I try to make sure I'm clear on the difference. That was done and over by about the 4th or 5th post
I was just disturbed when we were getting beyond the base question (and far off topic) and it sounded like we weren't sure what the compiler was doing, so when everyone starts saying, "stop asking questions, just believe", I can't let go :-# .
The party's over, the lights have been turned off, but I still have to dance out the last song. :oops: I've since done some separate research and discovered that denster/mattrax's initial post are probably both right:
Compilers handle SWITCH/CASE statements differently, and without actually seeing the result, we can't be sure. However, the standard methodology is thus: If the case values are basic and within a narrow range, the compiler will generate a branch table where it can directly access an indexed branch. If the case values are complex or the resulting branch table would result in a large index/large memory footprint, the compiler will generate a result closer to a sequential IF/ELSEIF.
And yes, this is my last post on this topic. :-# :-# :-#Michael Hollinger
Systems Analyst
NAV 2009 R2 (6.00.34463)0
Categories
- All Categories
- 73 General
- 73 Announcements
- 66.7K Microsoft Dynamics NAV
- 18.8K NAV Three Tier
- 38.4K NAV/Navision Classic Client
- 3.6K Navision Attain
- 2.4K Navision Financials
- 116 Navision DOS
- 851 Navision e-Commerce
- 1K NAV Tips & Tricks
- 772 NAV Dutch speaking only
- 617 NAV Courses, Exams & Certification
- 2K Microsoft Dynamics-Other
- 1.5K Dynamics AX
- 328 Dynamics CRM
- 111 Dynamics GP
- 10 Dynamics SL
- 1.5K Other
- 990 SQL General
- 383 SQL Performance
- 34 SQL Tips & Tricks
- 35 Design Patterns (General & Best Practices)
- 1 Architectural Patterns
- 10 Design Patterns
- 5 Implementation Patterns
- 53 3rd Party Products, Services & Events
- 1.6K General
- 1.1K General Chat
- 1.6K Website
- 83 Testing
- 1.2K Download section
- 23 How Tos section
- 252 Feedback
- 12 NAV TechDays 2013 Sessions
- 13 NAV TechDays 2012 Sessions



