DIY: Ternary operator

DIY: Ternary operator

  • Comments 12

<Changed DYI => DIY.  jps>  

At Microsoft, "to ship is to choose".  One of the things we were very disappointed in not being able to ship in V1.0 is a ternary operator. Here is a description of a ternary operator from Wikipedia:

Many programming languages that use C-like syntax feature a ternary operator, ?:; unqualified, "ternary operator" usually refers to this. The ?: operator is used as a shorthand replacement for the if-then-else conditional statement; the general form is condition ? op1 : op2. If condition is true, the statement evaluates as op1; otherwise, it evaluates as op2.

Imagine the scenario where you give a discount of 10% all orders but 25% off those with a quanity greater than 10.  Without a ternary operator, you code it this way:

if ($quantity -le 10)
{   $total = ($quantity * $price) * .9
}else
{   $total = ($quantity * $price) * .75
}

If (when?) PowerShell supports ternary operators you will be able to do it this way:


$total = ($quantity * $price ) * ({$quantity -le 10} ? {.9} : {.75})

This would evalue the expression {$quantity -le 10} and if it is TRUE, it evaluates the first script block and uses that value {.9}, otherwise, it evaluates the second scriptblock and uses that value {.75}.

Did I mention that we didn't get around to supporting this in V1.0?  Well, the community comes to the rescue. 

In the PowerShell Community Extensions project over on CodePlex.com (which you can download/install from  http://www.codeplex.com/Wiki/View.aspx?ProjectName=PowerShellCX ), they have a ton of great stuff.  I installed this a while ago and am now getting around to investigating what is there (silly me - I should have done this on day 1).  Karl Prosser contributed a script called Invoke-Ternary and then defined an alias ?:  .  Using this does not provide the same syntax that you would get if/when the PowerShell parser supports this but it is pretty great. 

Here is the code:


# ---------------------------------------------------------------------------
# Name:   Invoke-Ternary
# Alias:  ?:
# Author: Karl Prosser
# Desc:   Similar to the C# ? : operator e.g.
#            _name = (value != null) ? String.Empty : value;
# Usage:  1..10 | ?: {$_ -gt 5} {"Greater than 5;$_} {"Not greater than 5";$_}
# ---------------------------------------------------------------------------
set-alias ?: Invoke-Ternary -Option AllScope -Description "PSCX filter alias"
filter Invoke-Ternary ([scriptblock]$decider, [scriptblock]$ifTrue, [scriptblock]$ifFalse)
{
   if (&$decider) { 
      &$ifTrue
   } else { 
      &$ifFalse 
   }
}

This is a very tight, concise piece of code which is worthy of study.  What Karl is doing here is to leverage PowerShell's ability to treat code as a first class datatype [SCRIPTBLOCK] to CREATE HIS OWN CONTROL STRUCTURE!  He accepts 3 scriptblocks, he evaluates the first and if it is TRUE, he evaluates and returns the value of the second, otherwise it evalutes and returns the value of the third.  So going back to our original example, you can now code it this way (Notice the Syntax):


$total = ($quantity * $price ) * (?:  {$quantity -le 10} {.9} {.75})

Enjoy!

Jeffrey Snover [MSFT]
Windows PowerShell/MMC Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Leave a Comment
  • Please add 1 and 3 and type the answer here:
  • Post
  • PingBack from http://www.leedesmond.com/weblog/?p=50

  • Made a few modifications for myself, and also added ?? (the coalescence operator):

    <pre>

    filter Invoke-Ternary ($Predicate, $Then, $Otherwise = $null)

    {

      if($predicate -is [scriptblock]) { $predicate = &$predicate }

      if ($predicate) {

         if($then -is [ScriptBlock]) { &$then }

     else { $then }

      } elseif($otherwise) {

         if($otherwise -is [ScriptBlock]) { &$otherwise }

     else { $otherwise }

      }

    }

    filter Invoke-Coalescence($predicate, $alternative) {

    if($predicate -is [scriptblock]) { $predicate = &$predicate }

    Invoke-Ternary $predicate $predicate $alternative

    }

    set-alias ?: Invoke-Ternary -Option AllScope

    set-alias ?? Invoke-Coalescence -Option AllScope

    </pre>

  • FYI thew equivalent of your Invoke-Coalescence is already in PSCX and is aliased to ?? e.g.:

    $logdir = ?? {$env:logdir} {'C:\Temp'}

    Again if PoSH supported this natively the syntax could be nicer:

    $logdir = $env:logdir ?? 'c:\Temp'

    Although we call the function rather lamely Inovke-NullAlt.  Mind if we steal this name for PSCX?  :-)  BTW, this is a nice equivalent of the KornShell "Foo = ${paramaeter:-word}" functionality.

  • FYI DYI != DIY

    ttfn

    -mp

  • Keith, feel free. If you want to be really picky, you might want to call it Invoke-NullCoalescence (which is probably the most accurate term for it).

    I'm now considering something like this:

    filter Invoke-NullCoalescenceMethod($Object, $Method, [array]$Parameters = @()) {

       ?: $Object {$item = $Object.PsObject.Members.Item($Method); if($item.Invoke) { $item.Invoke($Parameters) } elseif($item.get_Value -and $Parameters) { $item.Value = $Parameters } else { $item.Value }} $null;

    }

    Set-Alias ?. Invoke-NullCoalescenceMethod

    It should work for getters, setters, and methods.

    This is sortof like the null-drilldown offered in Cω (C-omega). It would be really super-fantastic if we could get some of these things in the language. I *really* like the idea of having this: $x?.StartInfo?.CreateNoWindow ?= $true;

    That would just be handy.

  • Powershell Refactored. – Introduction and Invoke Ternary. The purpose of this column is to present powershell

  • Check out Invoke-Ternary here [link] Shay [link]

  • Powershell Refactored. – Introduction and Invoke Ternary. The purpose of this column is to present powershell

  • In the place of a ternary operator, I have been using the switch statement and assigning the result to the variable.  While the syntax is not quite as compact, it is every bit as powerful, if not more so.

    Example:

    $val = $( switch($someValue) { 1 { "One" } default { "Unknown" } } )

  • @Anthony

    In V2 you don't need to use the $(), you can just say:

    $val = switch($someValue) { 1 { "One" } default { "Unknown" } }

    jps

  • Since we're talking about ternary, if statements would be more appropriate than switch statements...

    Example 1:

    $var =  if ($gE) { $gE.ObjectClass } else { $false }

    Example 2:

    $var =  if ($gE) { $gE.ObjectClass } elseif ($uE) { $uE.ObjectClass } else { $false }

    --

    VertigoRay

    http://vertigion.com

  • My favourite technique:  Index the results of the logical test into a two-element hashtable.

    Structure:

    @{$false= <VALUE_IF_FALSE>; $true=<VALUE_IF_TRUE} [ LOGICAL_TEST ]

    Example:

    @{$false='DoesNotExist'; $true='Exists'} [(Get-ChildItem $file).Exists]

    Compact, yet the left-to-right ordering reads well.

    Tada!

Page 1 of 1 (12 items)