Page 1 of 3

IIF functionality made simple

Posted: Sun Nov 08, 2009 2:54 am
by mesozorn
Meant to post this here from a while back now.. classic IIF functionality in a simple-to-use syntax, which can also be used as a parameter in any procedure, statement etc..

One version for returning a string value:

Code: Select all

Macro IIF(expr,y,n)
Right(y,Len(y)*Val(Str((1 And expr))))+Right(n,Len(n)*(Not (1 And expr)))
EndMacro

Debug IIF(5.5=5.5,"Yes","No")
Debug IIF(5.5=5.7,"Yes","No")
And another for returning numerical values:

Code: Select all

Macro IIFnumerical(expr,y,n)
y*Val(Str(1 And expr))+n*(Not (1 And expr))
EndMacro

Debug IIFnumerical(5.5=5.5,100,200)
Debug IIFnumerical(5.5=5.7,100,200)

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 5:11 am
by STARGĂ…TE
Please encircle the Macro!

Bug:

Code: Select all

Macro IIFnumerical(expr,y,n)
y*Val(Str(1 And expr))+n*(Not (1 And expr))
EndMacro

Debug 100*IIFnumerical(5.5=5.5,1,2)
Debug 100*IIFnumerical(5.5=5.7,1,2)
so, use:

Code: Select all

Macro IIFnumerical(expr,y,n)
(y*Val(Str(1 And expr))+n*(Not (1 And expr)))
EndMacro
and then here is another error:
you lead the expression of two times so:

Code: Select all

Macro IIF(expr,y,n)
Right(y,Len(y)*Val(Str((1 And expr))))+Right(n,Len(n)*(Not (1 And expr)))
EndMacro

If CreateFile(0, "MacroTest.txt")
 CloseFile(0)
 Debug "Delete File : "+IIF(DeleteFile("MacroTest.txt"),"Yes","No")
EndIf
The code is therefore not always suitable, but still useful

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 6:44 am
by idle
like the idea
x = iff(a > b, c,d)
but not sure about its efficiency!
don't know if this any improvement.

Code: Select all


;expression macro 
Macro mxp(expr)
  If expr  
EndMacro

;use expression macro in procedure 
Procedure iiF(expr,a,b)
  mxp(expr) 
   ProcedureReturn a
  Else 
   ProcedureReturn b
  EndIf       
EndProcedure

Procedure.s iiFs(expr,a.s,b.s)
  mxp(expr) 
   ProcedureReturn a
  Else 
   ProcedureReturn b
  EndIf       
EndProcedure

;using procedure with macro
Debug Str(iif(10 > 5,200,100)) + " should be 200" 

x = iif(10 > 5 And 3 < 2 ,200,100) 

Debug Str(x) + " should be 100" 


If CreateFile(0, "MacroTest.txt")
CloseFile(0)
Debug "Delete File : " + iiFs(DeleteFile("MacroTest.txt"),"yes","No")
EndIf

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 7:23 am
by mesozorn
I've come up with this alternative which works for all examples given. Idle, I see that you came up with yours at the same time I was working on mine and we came up with almost the same approach. One slight difference: your version runs into trouble when decimal numbers are used such as:

Code: Select all

Debug iifs(5.5>5.6,"ya","na")
Debug iif(5.5>5.6,200,100)
...because procedures don't seem to evaluate decimal comparison expression parameters as true/false booleans very well. The code above returns "ya" and 200 in each case even though 5.5 is not greater than 5.6 for this reason. To get around this I used a macro first, and had it call a procedure, rather than vice versa:

Code: Select all

Procedure.s IIFEval(expr,y$,n$)
If expr=1:ProcedureReturn y$
Else:ProcedureReturn n$
EndIf
EndProcedure

Procedure IIFEvaln(expr,y,n)
If expr=1
ProcedureReturn y
Else
ProcedureReturn n
EndIf
EndProcedure


Macro IIFn(expr,y,n)
IIFEvaln(Val(Str(1 And expr)),y,n)
EndMacro

Macro IIF(expr,y,n)
IIFEval(Val(Str(1 And expr)),y,n)
EndMacro

Debug IIF(5.5>5.4,"yes","no")
Debug IIF(5.5>5.6,"yes","no")
Debug IIF(5.5=5.5,"yes","no")
Debug IIFn(5.5>5.7,200,100)
Debug iifn(5.5>5.4 And 5>3,200,100)

If CreateFile(0, "MacroTest.txt")
CloseFile(0)
Debug "Delete File : "+IIF(DeleteFile("MacroTest.txt"),"Yes","No")
EndIf

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 8:40 am
by moogle
idle wrote:like the idea
It's great to use, just like the ternary operator in other C style languages.
Would be nice if PB had this natively like

value = (expression) ? true code : false code

Thanks for the code anyway :)

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 11:31 am
by idle
mesozorn wrote:I've come up with this alternative which works for all examples given
yes that seems to work.
It's just a pity you can't find a way around using Val and Str as it tends to limit the practical use performance wise

Also I have no idea why the one I posted worked at all, maybe a case of "it's not a bug its a feature"

@moogle

What I liked was the idea of being able to assign a result on the left hand side as you would normally would only do something like this which won't compile if you try to assign the macro like x = iff(5>3,200,100)

Code: Select all

Macro iif(a,b,c)
  If a : b : Else : c : EndIf  
EndMacro

iif(3.6 > 6.6,Debug 200,Debug 100) 
iif(5 > 3 And 7 > 4, Debug 200,Debug 100) 
iif("a" <> "b", MessageRequester("test","not equal"),MessageRequester("test","equal"))
It would of course just be better if we had a ternary if , though I expect it's been requested many times already.

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 11:44 am
by Kaeru Gaman
so you needed the Val(Str()) to make floats work correctly?

what I see at first glance is that your expression in your Macro is not in brackets.
this could lead to problems sometimes.
try

Code: Select all

Macro IIFnumerical(expr,y,n)
( (y) * ( 1 And (expr) ) + (n) * (Not (1 And (expr) )) )
EndMacro
also, take a look here:
http://www.purebasic.fr/english/viewtop ... 31#p303631


PS:
it's really strange why ( 1 And (expr) ) does not work with the Len()...
I diddled around abit and came up with this:

Code: Select all

    Macro IIF(expr,y,n)
    Right( y ,Len(y) * ( Not ( Not (expr) ) ) ) + Right( n ,Len(n) * (Not (expr) ) )
    EndMacro

    Debug IIF(5.5 < 5.5,"Yes","No")
    Debug IIF(5.5 < 5.7,"Yes","No")
at least the messy superflous string-operation is eliminated...

PPS:
ok, I found a dirty, dirty, dirty hack:

Code: Select all

Macro IIF(expr,y,n)
Mid( y + n ,  Len( y )  * ( Not (expr) ) +1 , Len ( y ) )
EndMacro

Debug IIF(5.5 < 5.5, "yes", "no")
Debug IIF(5.5 < 5.7, "yes", "no")
the expression is only evaluated once, but it only works if the true-string is longer or at least same length.
if the false-string is longer, it will be cut.

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 1:36 pm
by einander
Another twist, without strings:

Code: Select all

Macro IIf(Expr,Y=1)
   (Y*(Not(Not(Expr)) + (Not(Expr))))
EndMacro

Debug IIf(5.5 > 5.5)
Debug IIf(5.5 + 5.7=11.2)
Debug "___"
For i=6 To 12
   Debug IIf(2*5=i) ;without 2nd param, returns 1 if true, 0 if false
Next
Debug "___"
For i=6 To 12
   Debug IIf(2*5=i,i) ; with 2nd param, returns 2nd param if true, 0 if false
Next

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 2:09 pm
by SFSxOI
OK, i'm getting confused over this. Brackets or no brackets, Val(Str()) or no Val(Str()) , floats OK or not, everything as string or not, procedure or macro...etc.....?

Which is correct?

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 5:51 pm
by Kaeru Gaman
SFSxOI wrote:Which is correct?
NONE!

Boolcasting an Expression is a fishy workaround.

better wait for the Boolean() function to come.

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 6:08 pm
by SFSxOI
Oki Dokee then. Thanks :)

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 7:22 pm
by mesozorn
Okay just to clear up the confusion, so far the second version I posted with the Macro calling the Procedure, is the only one that works correctly for all situations, meaning both regular expressions and actions such as DeleteFile etc.

There really is no performance loss at all using Val(Str()), as demonstrated by the following:

Code: Select all

e=ElapsedMilliseconds()
For x=1 To 10000
z=Val(Str(5.5>5.4))
Next x
Debug ElapsedMilliseconds()-e
That is ten thousand cycles of Val(Str()), and on my machine which is not even that new or fast, the elapsed milliseconds result is ZERO. If you don't like Val(Str()) for whatever reason, you can also use the logical ( Not ( Not (expr) ) ) as Kaeru Gaman did, but in my experiments the performance is exactly the same.

Re: IIF functionality made simple

Posted: Sun Nov 08, 2009 9:58 pm
by Kaeru Gaman
mesozorn wrote:That is ten thousand cycles of Val(Str()), and on my machine which is not even that new or fast, the elapsed milliseconds result is ZERO.
so, you have no result at all.

give'em enogh circles to run 10 up to 60 whole seconds each of both tests, then you have a result.

and you can't Debug a TimeTest result, because you need to run it without Debugger :!:

Re: IIF functionality made simple

Posted: Mon Nov 09, 2009 10:30 am
by SFSxOI
mesozorn wrote:Okay just to clear up the confusion, so far the second version I posted with the Macro calling the Procedure, is the only one that works correctly for all situations, meaning both regular expressions and actions such as DeleteFile etc.


@mesozorn

You mean this example?

Code: Select all

Procedure.s IIFEval(expr,y$,n$)
If expr=1:ProcedureReturn y$
Else:ProcedureReturn n$
EndIf
EndProcedure

Procedure IIFEvaln(expr,y,n)
If expr=1
ProcedureReturn y
Else
ProcedureReturn n
EndIf
EndProcedure


Macro IIFn(expr,y,n)
IIFEvaln(Val(Str(1 And expr)),y,n)
EndMacro

Macro IIF(expr,y,n)
IIFEval(Val(Str(1 And expr)),y,n)
EndMacro

Debug IIF(5.5>5.4,"yes","no")
Debug IIF(5.5>5.6,"yes","no")
Debug IIF(5.5=5.5,"yes","no")
Debug IIFn(5.5>5.7,200,100)
Debug iifn(5.5>5.4 And 5>3,200,100)

If CreateFile(0, "MacroTest.txt")
CloseFile(0)
Debug "Delete File : "+IIF(DeleteFile("MacroTest.txt"),"Yes","No")
EndIf
And how is this example related, just for the Val ? Returns 0 here instantly, so no result at all? I don't understand what you were trying to do with the below example.

Code: Select all

e=ElapsedMilliseconds()
For x=1 To 10000
z=Val(Str(5.5>5.4))
Next x
Debug ElapsedMilliseconds()-e

Re: IIF functionality made simple

Posted: Mon Nov 09, 2009 5:16 pm
by mesozorn
SFSxOI wrote:@mesozorn
You mean this example?
Yes, that's the one which works correctly for all situations.
SFSxOI wrote:And how is this example related, just for the Val ? Returns 0 here instantly, so no result at all? I don't understand what you were trying to do with the below example.
Yes, just to demonstrate that using Val(Str()) has no significant impact on performance or resource consumption. Using it ten thousand times takes less than one millisecond, thereby showing that it's a viable approach for practical usage. Zero is actually a real result, because it means the process took less than a single millisecond. You can increase the number of cycles to 100,000 or 1,000,000 to see a non-zero result (sometimes, depending on your computer speed etc) but it's clear that the resource impact is negligible enough not to be an issue.

I'm using Val(Str()) just to force a boolean-style evaluation of a comparison involving decimal numbers and ensure that it works in all cases. There are a few other similar approaches but they all seem to take about the same amount of processor time.