Difference between revisions of "Ternary Conditional"

From NWN Lexicon
Jump to: navigation, search
(Advanced Usage)
 
(2 intermediate revisions by 2 users not shown)
Line 1: Line 1:
=Ternary Conditional=
+
<nwscript>expression1 ? expression2 : expression3;</nwscript>
 
+
===Basic Usage===
<nwscript>
 
 
 
expression1 ? expression2 : expression3;
 
</nwscript>
 
 
 
====Basic Usage====
 
 
 
 
The above code syntax would simply read:
 
The above code syntax would simply read:
  
 
<nwscript>
 
<nwscript>
 
 
if (expression1)
 
if (expression1)
 
{
 
{
Line 23: Line 15:
  
 
This operator can be a more efficient way to do a simple if statement, especially if the resulting value is being stored in a common variable. Sometimes this operator is referred to as an "inline if" statement.
 
This operator can be a more efficient way to do a simple if statement, especially if the resulting value is being stored in a common variable. Sometimes this operator is referred to as an "inline if" statement.
 
<br />
 
  
 
The example below will assign the larger of x or y to the common variable z:
 
The example below will assign the larger of x or y to the common variable z:
  
 
<nwscript>
 
<nwscript>
 
 
int x = 5;
 
int x = 5;
 
int y = 10;
 
int y = 10;
int z = (x &gt; y) ? x : y;
+
int z = (x > y) ? x : y;
 
// z = 10
 
// z = 10
 
 
</nwscript>
 
</nwscript>
  
====Advanced Usage====
+
===Advanced Usage===
 
 
 
The ''Basic Usage'' compares the ternary operator with a simple if statement.
 
The ''Basic Usage'' compares the ternary operator with a simple if statement.
  
Line 44: Line 31:
  
 
<nwscript>
 
<nwscript>
 
+
if (condition_A)
<span class="syntax-highlight-code">if</span> (condition_A)
 
 
{
 
{
 
     expression_1;
 
     expression_1;
 
}
 
}
<span class="syntax-highlight-code">else</span>
+
else
 
{
 
{
     <span class="syntax-highlight-code">if</span> (condition_B)
+
     if (condition_B)
 
     {
 
     {
 
         expression_2;
 
         expression_2;
 
     }
 
     }
     <span class="syntax-highlight-code">else</span>
+
     else
 
     {
 
     {
 
         expression_3;
 
         expression_3;
Line 65: Line 51:
  
 
<nwscript>
 
<nwscript>
 
 
condition_A ? expression_1 : condition_B ? expression_2 : expression_3
 
condition_A ? expression_1 : condition_B ? expression_2 : expression_3
 
</nwscript>
 
</nwscript>
Line 72: Line 57:
  
 
<nwscript>
 
<nwscript>
 
 
condition_A
 
condition_A
 
     ? expression_1
 
     ? expression_1
Line 89: Line 73:
  
 
<nwscript>
 
<nwscript>
 
+
int z = x > y ? 1 : x < y ? -1 : 0;
<span class="syntax-highlight-code">int</span> z = x &gt; y ? <span class="syntax-highlight-number">1</span> : x &lt; y ? <span class="syntax-highlight-number">-1</span> : <span class="syntax-highlight-number">0</span>;
 
 
</nwscript>
 
</nwscript>
  
====Master Class====
+
===Master Class===
 
+
The ''Advanced Usage'' showed one level of nesting; however, there is no "pre-programmed maximum depth"€  of nesting. By "pre-programmed maximum depth", I mean there is no rule that says you can only nest to 3 or 4 or 7 or whatever levels.
The ''Advanced Usage'' showed one level of nesting; however, there is no ''pre-programmed maximum depth''† of nesting.
 
 
 
† '' By pre-programmed maximum depth, I mean there is no rule that says you can only nest to 3 or 4 or 7 or whatever levels. ''
 
  
 
Let's develop some real, and hopefully useful, code which will:
 
Let's develop some real, and hopefully useful, code which will:
Line 112: Line 92:
 
<nwscript>
 
<nwscript>
  
<span class="syntax-highlight-comment">/************************************************************************\
+
/************************************************************************\
 
     GetMovementRate() returns a heap of numbers I won't remember.
 
     GetMovementRate() returns a heap of numbers I won't remember.
 
     Better give them names.
 
     Better give them names.
 
     I'll use my usual HB_ prefix - I hope no one else is using this.
 
     I'll use my usual HB_ prefix - I hope no one else is using this.
\************************************************************************/</span>
+
\************************************************************************/
  
<span class="syntax-highlight-code">const int</span> HB_MOVERATEID_PC        = <span class="syntax-highlight-number">0</span>;
+
const int HB_MOVERATEID_PC        = 0;
<span class="syntax-highlight-code">const int</span> HB_MOVERATEID_IMMOBILE  = <span class="syntax-highlight-number">1</span>;
+
const int HB_MOVERATEID_IMMOBILE  = 1;
<span class="syntax-highlight-code">const int</span> HB_MOVERATEID_VERY_SLOW = <span class="syntax-highlight-number">2</span>;
+
const int HB_MOVERATEID_VERY_SLOW = 2;
<span class="syntax-highlight-code">const int</span> HB_MOVERATEID_SLOW      = <span class="syntax-highlight-number">3</span>;
+
const int HB_MOVERATEID_SLOW      = 3;
<span class="syntax-highlight-code">const int</span> HB_MOVERATEID_NORMAL    = <span class="syntax-highlight-number">4</span>;
+
const int HB_MOVERATEID_NORMAL    = 4;
<span class="syntax-highlight-code">const int</span> HB_MOVERATEID_FAST      = <span class="syntax-highlight-number">5</span>;
+
const int HB_MOVERATEID_FAST      = 5;
<span class="syntax-highlight-code">const int</span> HB_MOVERATEID_VERY_FAST = <span class="syntax-highlight-number">6</span>;
+
const int HB_MOVERATEID_VERY_FAST = 6;
<span class="syntax-highlight-code">const int</span> HB_MOVERATEID_CREATURE  = <span class="syntax-highlight-number">7</span>;
+
const int HB_MOVERATEID_CREATURE  = 7;
<span class="syntax-highlight-code">const int</span> HB_MOVERATEID_DM_FAST  = <span class="syntax-highlight-number">8</span>;
+
const int HB_MOVERATEID_DM_FAST  = 8;
  
<span class="syntax-highlight-comment">/************************************************************************\
+
/************************************************************************\
 
     GetMovementRate() doco has a heap of numbers I won't remember.
 
     GetMovementRate() doco has a heap of numbers I won't remember.
 
     Better give them names too. I'll start with Walkrate.
 
     Better give them names too. I'll start with Walkrate.
 
     * TODO * Implement Runrate when Walkrate works. Use prefix HB_RNRT_
 
     * TODO * Implement Runrate when Walkrate works. Use prefix HB_RNRT_
 
     Divide values by 6 - I want meters per second (not per round).
 
     Divide values by 6 - I want meters per second (not per round).
\************************************************************************/</span>
+
\************************************************************************/
  
<span class="syntax-highlight-code">const float</span> HB_WLKRT_PC        = <span class="syntax-highlight-number">0.333333333</span>;
+
const float HB_WLKRT_PC        = 0.333333333;
<span class="syntax-highlight-code">const float</span> HB_WLKRT_IMMOBILE  = <span class="syntax-highlight-number">0.000000001</span>;
+
const float HB_WLKRT_IMMOBILE  = 0.000000001;
<span class="syntax-highlight-code">const float</span> HB_WLKRT_VERY_SLOW = <span class="syntax-highlight-number">0.125000000</span>;
+
const float HB_WLKRT_VERY_SLOW = 0.125000000;
<span class="syntax-highlight-code">const float</span> HB_WLKRT_SLOW      = <span class="syntax-highlight-number">0.208333333</span>;
+
const float HB_WLKRT_SLOW      = 0.208333333;
<span class="syntax-highlight-code">const float</span> HB_WLKRT_NORMAL    = <span class="syntax-highlight-number">0.291666667</span>;
+
const float HB_WLKRT_NORMAL    = 0.291666667;
<span class="syntax-highlight-code">const float</span> HB_WLKRT_FAST      = <span class="syntax-highlight-number">0.375000000</span>;
+
const float HB_WLKRT_FAST      = 0.375000000;
<span class="syntax-highlight-code">const float</span> HB_WLKRT_VERY_FAST = <span class="syntax-highlight-number">0.458333333</span>;
+
const float HB_WLKRT_VERY_FAST = 0.458333333;
<span class="syntax-highlight-code">const float</span> HB_WLKRT_DM_FAST  = <span class="syntax-highlight-number">0.916666667</span>;
+
const float HB_WLKRT_DM_FAST  = 0.916666667;
  
<span class="syntax-highlight-comment">/************************************************************************\
+
/************************************************************************\
 
     Prototypes
 
     Prototypes
 
     * STICKY TODO * Check there's a prototype for every function.
 
     * STICKY TODO * Check there's a prototype for every function.
\************************************************************************/</span>
+
\************************************************************************/
  
<span class="syntax-highlight-code">float</span> get_walkrate_by_creature_ms(<span class="syntax-highlight-code">object</span> creature=<span class="syntax-highlight-code">OBJECT_SELF</span>);
+
float get_walkrate_by_creature_ms(object creature=OBJECT_SELF);
  
<span class="syntax-highlight-comment">/************************************************************************\
+
/************************************************************************\
 
     Functions
 
     Functions
\************************************************************************/</span>
+
\************************************************************************/
  
<span class="syntax-highlight-code">float</span> get_walkrate_by_creature_ms(<span class="syntax-highlight-code">object</span> creature=<span class="syntax-highlight-code">OBJECT_SELF</span>)
+
float get_walkrate_by_creature_ms(object creature=OBJECT_SELF)
 
{
 
{
  
     <span class="syntax-highlight-code">int</span> iMoveRateId = GetMovementRate(creature);
+
     int iMoveRateId = GetMovementRate(creature);
  
     <span class="syntax-highlight-code">return</span>
+
     return
 
           iMoveRateId == HB_MOVERATEID_PC        ? HB_WLKRT_PC
 
           iMoveRateId == HB_MOVERATEID_PC        ? HB_WLKRT_PC
 
         : iMoveRateId == HB_MOVERATEID_CREATURE  ? HB_WLKRT_NORMAL
 
         : iMoveRateId == HB_MOVERATEID_CREATURE  ? HB_WLKRT_NORMAL
Line 170: Line 150:
 
         : iMoveRateId == HB_MOVERATEID_DM_FAST  ? HB_WLKRT_DM_FAST
 
         : iMoveRateId == HB_MOVERATEID_DM_FAST  ? HB_WLKRT_DM_FAST
 
         : iMoveRateId == HB_MOVERATEID_IMMOBILE  ? HB_WLKRT_IMMOBILE
 
         : iMoveRateId == HB_MOVERATEID_IMMOBILE  ? HB_WLKRT_IMMOBILE
         : <span class="syntax-highlight-comment">/* undocumented return value */</span>         HB_WLKRT_NORMAL;
+
         : /* undocumented return value */          HB_WLKRT_NORMAL;
 
}
 
}
 
</nwscript>
 
</nwscript>
Line 185: Line 165:
  
 
<nwscript>
 
<nwscript>
 
 
If this is the PC's movement rate ID return the PC's Walkrate
 
If this is the PC's movement rate ID return the PC's Walkrate
 
otherwise
 
otherwise
Line 218: Line 197:
 
<nwscript>
 
<nwscript>
  
<span class="syntax-highlight-code">float</span> GetProximityRadius(<span class="syntax-highlight-code">object</span> creature=<span class="syntax-highlight-code">OBJECT_SELF</span>)
+
float GetProximityRadius(object creature=OBJECT_SELF)
 
{
 
{
     <span class="syntax-highlight-code">int</span> iSizeIndex = GetCreatureSize(creature);
+
     int iSizeIndex = GetCreatureSize(creature);
  
     <span class="syntax-highlight-code">return</span>
+
     return
 
           iSizeIndex == CREATURE_SIZE_MEDIUM  ? HB_PROXIMITY_RADIUS_MEDIUM
 
           iSizeIndex == CREATURE_SIZE_MEDIUM  ? HB_PROXIMITY_RADIUS_MEDIUM
 
         : iSizeIndex == CREATURE_SIZE_SMALL  ? HB_PROXIMITY_RADIUS_SMALL
 
         : iSizeIndex == CREATURE_SIZE_SMALL  ? HB_PROXIMITY_RADIUS_SMALL
Line 229: Line 208:
 
         : iSizeIndex == CREATURE_SIZE_HUGE    ? HB_PROXIMITY_RADIUS_HUGE
 
         : iSizeIndex == CREATURE_SIZE_HUGE    ? HB_PROXIMITY_RADIUS_HUGE
 
         : iSizeIndex == CREATURE_SIZE_INVALID ? HB_PROXIMITY_RADIUS_INVALID
 
         : iSizeIndex == CREATURE_SIZE_INVALID ? HB_PROXIMITY_RADIUS_INVALID
         : <span class="syntax-highlight-comment">/* undocumented return value */</span>       HB_PROXIMITY_RADIUS_INVALID;
+
         : /* undocumented return value */      HB_PROXIMITY_RADIUS_INVALID;
 
}
 
}
 
</nwscript>
 
</nwscript>

Latest revision as of 03:04, 17 September 2012

expression1 ? expression2 : expression3;

Basic Usage

The above code syntax would simply read:

if (expression1)
{
     expression2;
}
else
{
     expression3;
}

This operator can be a more efficient way to do a simple if statement, especially if the resulting value is being stored in a common variable. Sometimes this operator is referred to as an "inline if" statement.

The example below will assign the larger of x or y to the common variable z:

int x = 5;
int y = 10;
int z = (x > y) ? x : y;
// z = 10

Advanced Usage

The Basic Usage compares the ternary operator with a simple if statement.

You can nest if statements within if statements:

if (condition_A)
{
    expression_1;
}
else
{
    if (condition_B)
    {
        expression_2;
    }
    else
    {
        expression_3;
    }
}

Similarly, you can nest ternary operators within ternary operators:

condition_A ? expression_1 : condition_B ? expression_2 : expression_3

Let's add some formatting to make that a little more readable:

condition_A
    ? expression_1
    : condition_B
        ? expression_2
        : expression_3

The Basic Usage code example found the larger of two numbers.

Suppose you want to compare two numbers and return:

  • 1 if x is larger than y
  • -1 if x is smaller than y
  • 0 if x and y are equal
int z = x > y ? 1 : x < y ? -1 : 0;

Master Class

The Advanced Usage showed one level of nesting; however, there is no "pre-programmed maximum depth"€  of nesting. By "pre-programmed maximum depth", I mean there is no rule that says you can only nest to 3 or 4 or 7 or whatever levels.

Let's develop some real, and hopefully useful, code which will:

  • return a movement speed for a creature (in meters per second)
  • be easy to use and adapt by any NWScript coder
  • show 8 levels of ternary operator nesting
  • be very easy to read through the use of a tabular code layout (see Acknowledgements )
  • provide the functionality of a switch statement with 9 conditional cases and a default case
  • provide the switch functionality in a single, easy to read, NWScript statement

The following makes reference to the function GetMovementRate() .

/************************************************************************\
    GetMovementRate() returns a heap of numbers I won't remember.
    Better give them names.
    I'll use my usual HB_ prefix - I hope no one else is using this.
\************************************************************************/
 
const int HB_MOVERATEID_PC        = 0;
const int HB_MOVERATEID_IMMOBILE  = 1;
const int HB_MOVERATEID_VERY_SLOW = 2;
const int HB_MOVERATEID_SLOW      = 3;
const int HB_MOVERATEID_NORMAL    = 4;
const int HB_MOVERATEID_FAST      = 5;
const int HB_MOVERATEID_VERY_FAST = 6;
const int HB_MOVERATEID_CREATURE  = 7;
const int HB_MOVERATEID_DM_FAST   = 8;
 
/************************************************************************\
    GetMovementRate() doco has a heap of numbers I won't remember.
    Better give them names too. I'll start with Walkrate.
    * TODO * Implement Runrate when Walkrate works. Use prefix HB_RNRT_
    Divide values by 6 - I want meters per second (not per round).
\************************************************************************/
 
const float HB_WLKRT_PC        = 0.333333333;
const float HB_WLKRT_IMMOBILE  = 0.000000001;
const float HB_WLKRT_VERY_SLOW = 0.125000000;
const float HB_WLKRT_SLOW      = 0.208333333;
const float HB_WLKRT_NORMAL    = 0.291666667;
const float HB_WLKRT_FAST      = 0.375000000;
const float HB_WLKRT_VERY_FAST = 0.458333333;
const float HB_WLKRT_DM_FAST   = 0.916666667;
 
/************************************************************************\
    Prototypes
    * STICKY TODO * Check there's a prototype for every function.
\************************************************************************/
 
float get_walkrate_by_creature_ms(object creature=OBJECT_SELF);
 
/************************************************************************\
    Functions
\************************************************************************/
 
float get_walkrate_by_creature_ms(object creature=OBJECT_SELF)
{
 
    int iMoveRateId = GetMovementRate(creature);
 
    return
          iMoveRateId == HB_MOVERATEID_PC        ? HB_WLKRT_PC
        : iMoveRateId == HB_MOVERATEID_CREATURE  ? HB_WLKRT_NORMAL
        : iMoveRateId == HB_MOVERATEID_NORMAL    ? HB_WLKRT_NORMAL
        : iMoveRateId == HB_MOVERATEID_SLOW      ? HB_WLKRT_SLOW
        : iMoveRateId == HB_MOVERATEID_FAST      ? HB_WLKRT_FAST
        : iMoveRateId == HB_MOVERATEID_VERY_FAST ? HB_WLKRT_VERY_FAST
        : iMoveRateId == HB_MOVERATEID_VERY_SLOW ? HB_WLKRT_VERY_SLOW
        : iMoveRateId == HB_MOVERATEID_DM_FAST   ? HB_WLKRT_DM_FAST
        : iMoveRateId == HB_MOVERATEID_IMMOBILE  ? HB_WLKRT_IMMOBILE
        : /* undocumented return value */          HB_WLKRT_NORMAL;
}

Now, I did say: be easy to use and adapt by any NWScript coder.

It is not important to understand how this syntax works!

The 9 conditions are all on the left: iMoveRateId == HB_MOVERATEID_

The values on the right are the Walkrates (in meters per second).

It can be read pretty much as English:

If this is the PC's movement rate ID return the PC's Walkrate
otherwise
    If this is the Creature's movement rate ID return the Creature's Walkrate
    otherwise
        ... and so on ...

The default condition is the /* undocumented return value */. Yes, this is a comment: use whatever text is appropriate; /* default */ is fine and holds to the switch metaphor. Here I've decided to return the Normal Walkrate if GetMovementRate() returns an undocumented ID. I use a default condition because any future update to NWScript may enhance the GetMovementRate() function by adding one or more new movement rates; if these were not trapped, my module could crash. Doing this makes your code forwardly compatible.

Gotchas

Four things that might trip the unwary:

  • The first line has no colon
  • The last line has no question mark
  • The last line ends in a semicolon
  • The default condition must always be on the last line
Optimisation

If you think certain conditions are more likely to be TRUE than others, put them nearer to the top of the list.

This is because the statement ends as soon as a TRUE condition is found.

Here I'm guessing that get_walkrate_by_creature_ms() will be targetting creatures which walk at a more normal speed than those which are particularly fast or slow.

Additional Example Code

Here's another example that returns an indication of what might be described as a creature's personal space based on the creature's size. Note the similarity to the previous function. Also note the optimisation: expecting to target medium sized creatures more often than huge or tiny ones.

float GetProximityRadius(object creature=OBJECT_SELF)
{
    int iSizeIndex = GetCreatureSize(creature);
 
    return
          iSizeIndex == CREATURE_SIZE_MEDIUM  ? HB_PROXIMITY_RADIUS_MEDIUM
        : iSizeIndex == CREATURE_SIZE_SMALL   ? HB_PROXIMITY_RADIUS_SMALL
        : iSizeIndex == CREATURE_SIZE_LARGE   ? HB_PROXIMITY_RADIUS_LARGE
        : iSizeIndex == CREATURE_SIZE_TINY    ? HB_PROXIMITY_RADIUS_TINY
        : iSizeIndex == CREATURE_SIZE_HUGE    ? HB_PROXIMITY_RADIUS_HUGE
        : iSizeIndex == CREATURE_SIZE_INVALID ? HB_PROXIMITY_RADIUS_INVALID
        : /* undocumented return value */       HB_PROXIMITY_RADIUS_INVALID;
}
Acknowledgements

The idea for the tabular layout comes from Damian Conway's excellent book Perl Best Practices (ISBN: 0-596-00173-8). Published by O'Reilly: http://search.oreilly.com/?q=perl+best+practices


 authors: Ryan Hunt (Basic Usage), Ken Cotterill (Advanced Usage and Master Class), editor: Charles Feduke