What are the benefits and drawbacks of the ?: operator as opposed to the standard if-else statement. The obvious ones being:
Conditional ?: Operator
Shorter and more concise when dealing with direct value comparisons and assignments
Doesn't seem to be as flexible as the if/else construct
Standard If/Else
Can be applied to more situations (such as function calls)
Often are unnecessarily long
Readability seems to vary for each depending on the statement. For a little while after first being exposed to the ?: operator, it took me some time to digest exactly how it worked. Would you recommend using it wherever possible, or sticking to if/else given that I work with many non-programmers?
I would basically recommend using it only when the resulting statement is extremely short and represents a significant increase in conciseness over the if/else equivalent without sacrificing readability.
Good example:
int result = Check() ? 1 : 0;
Bad example:
int result = FirstCheck() ? 1 : SecondCheck() ? 1 : ThirdCheck() ? 1 : 0;
This is pretty much covered by the other answers, but "it's an expression" doesn't really explain why that is so useful...
In languages like C++ and C#, you can define local readonly fields (within a method body) using them. This is not possible with a conventional if/then statement because the value of a readonly field has to be assigned within that single statement:
readonly int speed = (shiftKeyDown) ? 10 : 1;
is not the same as:
readonly int speed;
if (shifKeyDown)
speed = 10; // error - can't assign to a readonly
else
speed = 1; // error
In a similar way you can embed a tertiary expression in other code. As well as making the source code more compact (and in some cases more readable as a result) it can also make the generated machine code more compact and efficient:
MoveCar((shiftKeyDown) ? 10 : 1);
...may generate less code than having to call the same method twice:
if (shiftKeyDown)
MoveCar(10);
else
MoveCar(1);
Of course, it's also a more convenient and concise form (less typing, less repetition, and can reduce the chance of errors if you have to duplicate chunks of code in an if/else). In clean "common pattern" cases like this:
object thing = (reference == null) ? null : reference.Thing;
... it is simply faster to read/parse/understand (once you're used to it) than the long-winded if/else equivalent, so it can help you to 'grok' code faster.
Of course, just because it is useful does not mean it is the best thing to use in every case. I'd advise only using it for short bits of code where the meaning is clear (or made more clear) by using ?: - if you use it in more complex code, or nest ternary operators within each other it can make code horribly difficult to read.
I usually choose a ternary operator when I'd have a lot of duplicate code otherwise.
if (a > 0)
answer = compute(a, b, c, d, e);
else
answer = compute(-a, b, c, d, e);
With a ternary operator, this could be accomplished with the following.
answer = compute(a > 0 ? a : -a, b, c, d, e);
I find it particularly helpful when doing web development if I want to set a variable to a value sent in the request if it is defined or to some default value if it is not.
A really cool usage is:
x = foo ? 1 :
bar ? 2 :
baz ? 3 :
4;
Sometimes it can make the assignment of a bool value easier to read at first glance:
// With
button.IsEnabled = someControl.HasError ? false : true;
// Without
button.IsEnabled = !someControl.HasError;
I'd recommend limiting the use of the ternary(?:) operator to simple single line assignment if/else logic. Something resembling this pattern:
if(<boolCondition>) {
<variable> = <value>;
}
else {
<variable> = <anotherValue>;
}
Could be easily converted to:
<variable> = <boolCondition> ? <value> : <anotherValue>;
I would avoid using the ternary operator in situations that require if/else if/else, nested if/else, or if/else branch logic that results in the evaluation of multiple lines. Applying the ternary operator in these situations would likely result in unreadable, confusing, and unmanageable code. Hope this helps.
The conditional operator is great for short conditions, like this:
varA = boolB ? valC : valD;
I use it occasionally because it takes less time to write something that way... unfortunately, this branching can sometimes be missed by another developer browsing over your code. Plus, code isn't usually that short, so I usually help readability by putting the ? and : on separate lines, like this:
doSomeStuffToSomething(shouldSomethingBeDone()
? getTheThingThatNeedsStuffDone()
: getTheOtherThingThatNeedsStuffDone());
However, the big advantage to using if/else blocks (and why I prefer them) is that it's easier to come in later and add some additional logic to the branch,
if (shouldSomethingBeDone()) {
doSomeStuffToSomething(getTheThingThatNeedsStuffDone());
doSomeAdditionalStuff();
} else {
doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone());
}
or add another condition:
if (shouldSomethingBeDone()) {
doSomeStuffToSomething(getTheThingThatNeedsStuffDone());
doSomeAdditionalStuff();
} else if (shouldThisOtherThingBeDone()){
doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone());
}
So, in the end, it's about convenience for you now (shorter to use :?) vs. convenience for you (and others) later. It's a judgment call... but like all other code-formatting issues, the only real rule is to be consistent, and be visually courteous to those who have to maintain (or grade!) your code.
(all code eye-compiled)
One thing to recognize when using the ternary operator that it is an expression not a statement.
In functional languages like scheme the distinction doesn't exists:
(if (> a b) a b)
Conditional ?: Operator
"Doesn't seem to be as flexible as the if/else construct"
In functional languages it is.
When programming in imperative languages I apply the ternary operator in situations where I typically would use expressions (assignment, conditional statements, etc).
While the above answers are valid, and I agree with readability being important, there are 2 further points to consider:
In C#6, you can have expression-bodied methods.
This makes it particularly concise to use the ternary:
string GetDrink(DayOfWeek day)
=> day == DayOfWeek.Friday
? "Beer" : "Tea";
Behaviour differs when it comes to implicit type conversion.
If you have types T1 and T2 that can both be implicitly converted to T, then the below does not work:
T GetT() => true ? new T1() : new T2();
(because the compiler tries to determine the type of the ternary expression, and there is no conversion between T1 and T2.)
On the other hand, the if/else version below does work:
T GetT()
{
if (true) return new T1();
return new T2();
}
because T1 is converted to T and so is T2
If I'm setting a value and I know it will always be one line of code to do so, I typically use the ternary (conditional) operator. If there's a chance my code and logic will change in the future, I use an if/else as it's more clear to other programmers.
Of further interest to you may be the ?? operator.
The advantage of the conditional operator is that it is an operator. In other words, it returns a value. Since if is a statement, it cannot return a value.
There is some performance benefit of using the the ? operator in eg. MS Visual C++, but this is a really a compiler specific thing. The compiler can actually optimize out the conditional branch in some cases.
The scenario I most find myself using it is for defaulting values and especially in returns
return someIndex < maxIndex ? someIndex : maxIndex;
Those are really the only places I find it nice, but for them I do.
Though if you're looking for a boolean this might sometimes look like an appropriate thing to do:
bool hey = whatever < whatever_else ? true : false;
Because it's so easy to read and understand, but that idea should always be tossed for the more obvious:
bool hey = (whatever < whatever_else);
If you need multiple branches on the same condition, use an if:
if (A == 6)
f(1, 2, 3);
else
f(4, 5, 6);
If you need multiple branches with different conditions, then if statement count would snowball, you'll want to use the ternary:
f( (A == 6)? 1: 4, (B == 6)? 2: 5, (C == 6)? 3: 6 );
Also, you can use the ternary operator in initialization.
const int i = (A == 6)? 1 : 4;
Doing that with if is very messy:
int i_temp;
if (A == 6)
i_temp = 1;
else
i_temp = 4;
const int i = i_temp;
You can't put the initialization inside the if/else, because it changes the scope. But references and const variables can only be bound at initialization.
The ternary operator can be included within an rvalue, whereas an if-then-else cannot; on the other hand, an if-then-else can execute loops and other statements, whereas the ternary operator can only execute (possibly void) rvalues.
On a related note, the && and || operators allow some execution patterns which are harder to implement with if-then-else. For example, if one has several functions to call and wishes to execute a piece of code if any of them fail, it can be done nicely using the && operator. Doing it without that operator will either require redundant code, a goto, or an extra flag variable.
With C# 7, you can use the new ref locals feature to simplify the conditional assignment of ref-compatible variables. So now, not only can you do:
int i = 0;
T b = default(T), c = default(T);
// initialization of C#7 'ref-local' variable using a conditional r-value⁽¹⁾
ref T a = ref (i == 0 ? ref b : ref c);
...but also the extremely wonderful:
// assignment of l-value⁽²⁾ conditioned by C#7 'ref-locals'
(i == 0 ? ref b : ref c) = a;
That line of code assigns the value of a to either b or c, depending on the value of i.
Notes
1. r-value is the right-hand side of an assignment, the value that gets assigned.
2. l-value is the left-hand side of an assignment, the variable that receives the assigned value.
Related
I'd like the example computation expression and values below to return 6. For some the numbers aren't yielding like I'd expect. What's the step I'm missing to get my result? Thanks!
type AddBuilder() =
let mutable x = 0
member _.Yield i = x <- x + i
member _.Zero() = 0
member _.Return() = x
let add = AddBuilder()
(* Compiler tells me that each of the numbers in add don't do anything
and suggests putting '|> ignore' in front of each *)
let result = add { 1; 2; 3 }
(* Currently the result is 0 *)
printfn "%i should be 6" result
Note: This is just for creating my own computation expression to expand my learning. Seq.sum would be a better approach. I'm open to the idea that this example completely misses the value of computation expressions and is no good for learning.
There is a lot wrong here.
First, let's start with mere mechanics.
In order for the Yield method to be called, the code inside the curly braces must use the yield keyword:
let result = add { yield 1; yield 2; yield 3 }
But now the compiler will complain that you also need a Combine method. See, the semantics of yield is that each of them produces a finished computation, a resulting value. And therefore, if you want to have more than one, you need some way to "glue" them together. This is what the Combine method does.
Since your computation builder doesn't actually produce any results, but instead mutates its internal variable, the ultimate result of the computation should be the value of that internal variable. So that's what Combine needs to return:
member _.Combine(a, b) = x
But now the compiler complains again: you need a Delay method. Delay is not strictly necessary, but it's required in order to mitigate performance pitfalls. When the computation consists of many "parts" (like in the case of multiple yields), it's often the case that some of them should be discarded. In these situation, it would be inefficient to evaluate all of them and then discard some. So the compiler inserts a call to Delay: it receives a function, which, when called, would evaluate a "part" of the computation, and Delay has the opportunity to put this function in some sort of deferred container, so that later Combine can decide which of those containers to discard and which to evaluate.
In your case, however, since the result of the computation doesn't matter (remember: you're not returning any results, you're just mutating the internal variable), Delay can just execute the function it receives to have it produce the side effects (which are - mutating the variable):
member _.Delay(f) = f ()
And now the computation finally compiles, and behold: its result is 6. This result comes from whatever Combine is returning. Try modifying it like this:
member _.Combine(a, b) = "foo"
Now suddenly the result of your computation becomes "foo".
And now, let's move on to semantics.
The above modifications will let your program compile and even produce expected result. However, I think you misunderstood the whole idea of the computation expressions in the first place.
The builder isn't supposed to have any internal state. Instead, its methods are supposed to manipulate complex values of some sort, some methods creating new values, some modifying existing ones. For example, the seq builder1 manipulates sequences. That's the type of values it handles. Different methods create new sequences (Yield) or transform them in some way (e.g. Combine), and the ultimate result is also a sequence.
In your case, it looks like the values that your builder needs to manipulate are numbers. And the ultimate result would also be a number.
So let's look at the methods' semantics.
The Yield method is supposed to create one of those values that you're manipulating. Since your values are numbers, that's what Yield should return:
member _.Yield x = x
The Combine method, as explained above, is supposed to combine two of such values that got created by different parts of the expression. In your case, since you want the ultimate result to be a sum, that's what Combine should do:
member _.Combine(a, b) = a + b
Finally, the Delay method should just execute the provided function. In your case, since your values are numbers, it doesn't make sense to discard any of them:
member _.Delay(f) = f()
And that's it! With these three methods, you can add numbers:
type AddBuilder() =
member _.Yield x = x
member _.Combine(a, b) = a + b
member _.Delay(f) = f ()
let add = AddBuilder()
let result = add { yield 1; yield 2; yield 3 }
I think numbers are not a very good example for learning about computation expressions, because numbers lack the inner structure that computation expressions are supposed to handle. Try instead creating a maybe builder to manipulate Option<'a> values.
Added bonus - there are already implementations you can find online and use for reference.
1 seq is not actually a computation expression. It predates computation expressions and is treated in a special way by the compiler. But good enough for examples and comparisons.
I've been told in Java that I should avoid modifying the original parameters such as
public int doStuff(int begin, int end) {
/* loop or something */
begin++; //bad
end--; //also bad
/* end loop */
return
}
instead, I should do something like
public int doStuff(int begin, int end) {
int myBegin = begin; //something like this
int myEnd = end;
/* stuff */
return
}
So, I've been doing this in lua
function do_stuff(begin, last)
local my_begin = begin
local my_last = last
--stuff
my_begin = my_begin + 1
my_last = my_last - 1
--stuff
end
But, I'm wondering if
function do_stuff(begin, last)
--stuff
begin = begin + 1
last = last - 1
--stuff
end
is also discouraged, or is it nice and concise?
There are no rules. Let taste, clarity, and need decide.
Nevetheless, a common idiom is to provide default values for parameters as in
function log(x,b)
b = b or 10
...
end
If you were told not to modify the parameters of functions, then there was probably a reasoning associated with that. Whatever that reasoning is would apply as much to Lua as to Java, since they have similar function argument semantics. Those reasons could be one or more of (but not limited to):
If you modify a parameter... you don't have it anymore. If you suddenly have a need for the original value you were passed, it's gone now.
Creating confusion, depending on how the parameters are named. The word "begin" suggests the beginning of something. If you change it, it isn't necessarily the beginning anymore, but merely the current element you're operating on.
Creating potential errors, if dealing with reference types (non-basic types in Java, tables and such in Lua). When you modify an object, you're changing it for everyone. Whereas incrementing an integer is just changing your local value. So if you're frequently modifying parameters, you still need to think about which ones you ought to be poking at and which ones you shouldn't be.
To put it another way, if you agreed with the suggestion for doing so in Java, then it applies just as much to Lua. If you didn't agree with the suggestion in Java, then you have no more reason to follow it under Lua.
In Lua functions, threads, tables and userdata types are passed by reference. So unless you have one of those you are working with a local copy anyway.
So in your example:
function do_stuff(begin, last)
--stuff
begin = begin + 1
last = last - 1
--stuff
end
begin and last are local non-reference variables in do_stuff's scope.
The only reason to make a copy of them is that you might want to store there initial value for later use. For that purpose you can either create a backup copy of the initial value or you create a working copy of it. Whatever you prefer.
Only make sure you know what is passed by reference and what by value so you avoid changing things you don't want to change and the other way around.
I am looking for a more succinct F# equivalent of:
myNumber >= 2 && myNumber <= 4
I imagine something like
myNumber >=< (2, 4)
Is there some kind of operation like this?
There is no native operator, but you could define your own one.
let inline (>=<) a (b,c) = a >= b && a<= c
John's answer is exactly what you asked for, and the most practical solution. But this got me wondering if one could define operator(s) to enable a syntax closer to normal mathematical notation, i.e., a <= b <= c.
Here's one such solution:
let inline (<=.) left middle = (left <= middle, middle)
let inline (.<=) (leftResult, middle) right = leftResult && (middle <= right)
let inline (.<=.) middleLeft middleRight = (middleLeft .<= middleRight, middleRight)
1 <=. 3 .<=. 5 .<= 9 // true
1 <=. 10 .<= 5 // false
A few comments on this:
I used the . character to indicate the "middle" of the expression
. was a very deliberate choice, and is not easily changeable to some other character you like better (e.g. if you perhaps like the look of 1 <=# 3 #<= 5 better). The F# compiler changes the associativity and/or precedence of an operator based on the operator symbol's first character. We want standard left-to-right evaluation/short-circuiting, and . enables this.
A 3-number comparison is optimized away completely, but a 4+ number comparison results in CIL that allocates tuples and does various other business that isn't strictly necessary:
Is there some kind of operation like this?
Great question! The answer is "no", there isn't, but I wish there was.
Latkin's answer is nice, but it doesn't short-circuit evaluate. So if the first test fails the remaining subexpressions still get evaluated, even though their results are irrelevant.
FWIW, in Mathematica you can do 1<x<2 just like mathematics.
Any way to declare a new variable in F# without assigning a value to it?
See Aidan's comment.
If you insist, you can do this:
let mutable x = Unchecked.defaultof<int>
This will assign the absolute zero value (0 for numeric types, null for reference types, struct-zero for value types).
It would be interesting to know why the author needs this in F# (simple example of intended use would suffice).
But I guess one of the common cases when you may use uninitialised variable in C# is when you call a function with out parameter:
TResult Foo<TKey, TResult>(IDictionary<TKey, TResult> dictionary, TKey key)
{
TResult value;
if (dictionary.TryGetValue(key, out value))
{
return value;
}
else
{
throw new ApplicationException("Not found");
}
}
Luckily in F# you can handle this situation using much nicer syntax:
let foo (dict : IDictionary<_,_>) key =
match dict.TryGetValue(key) with
| (true, value) -> value
| (false, _) -> raise <| ApplicationException("Not Found")
You can also use explicit field syntax:
type T =
val mutable x : int
I agree with everyone who has said "don't do it". However, if you are convinced that you are in a case where it really is necessary, you can do this:
let mutable naughty : int option = None
...then later to assign a value.
naughty <- Some(1)
But bear in mind that everyone who has said 'change your approach instead' is probably right. I code in F# full time and I've never had to declare an unassigned 'variable'.
Another point: although you say it wasn't your choice to use F#, I predict you'll soon consider yourself lucky to be using it!
F# variables are by default immutable, so you can't assign a value later. Therefore declaring them without an initial value makes them quite useless, and as such there is no mechanism to do so.
Arguably, a mutable variable declaration could be declared without an initial value and still be useful (it could acquire an initial default like C# variables do), but F#'s syntax does not support this. I would guess this is for consistency and because mutable variable slots are not idiomatic F# so there's little incentive to make special cases to support them.
We (two people) are doing a project using Lua as an embedded language.
My teammate uses argument type checks almost everywhere:
function someFunction( a, b,c )
if a == nil then return end
--Some stuff here
if type( b ) ~= "function" then
Error( "blah" )
end
--More here
if someTable[ c ] == nil then someTable[ c ] = {}
end
I don't really like that as I think that most of those checks are unneccessary.. it kind of takes the "spirit" of using Lua. It also makes the code longer, slower and less readable in my opinion.
In general I would do it this way:
function someFunction( a, b,c )
--More here
if someTable[ c ] == nil then someTable[ c ] = {}
end
I leave out almost all type/argument checks and only do those who have a high chance of actually happening.
Now we are unsure of what solution is better and decided to ask you: Security checks in Lua - yes or no?
I'm not familiar with Lua, but Wikipedia seems to think it is duck-typed. I'm going to draw an analogy with Python, so forgive me if it's not appropriate.
In Python, functions are designed with the principle that they need an object that meets certain criteria. If you pass in a different object than what the original author intended, as long as it meets the criteria of the function, it should work. The idea being, "if it looks like a duck, walks like a duck, and quacks like a duck, it is a duck." (Hence the name.) That said, there are a few rare instances where you need an object of a specific type, but this is not the general case.
At any rate, you appear to be "fighting the language", which is a code smell in my book. Most languages are designed and intended to be used in certain ways - figure out what principles and design/coding guidelines the Lua community uses, and follow those.
I type check public functions in my api. But do not for only internally used functions.
Good type checking:
function ( a , b )
assert ( type ( a ) == "number" , "Invalid a" )
b = b or {}
assert ( type ( b ) == "table" , "B must be a table" )
c = assert ( type ( c ) == "string" ) and c or "default"
end
Keep in mind though, lua also has a bit of "duck" typing: if all that is required in an object is callable, then a table with a __call method should be allowable. Same for an indexable object: a table and a userdata can both be indexed (not to mention the other types).
I don't know Lua either, and it's a little unclear whether you're asking only about checking the argument types [type(b)~="function"] or do you want to check their values too [a==nil], but here's what I do:
If the function can only ever be called by some other functions of your own, and those other functions have already checked the argument, then there's no need to check it again. On the other hand, if your design doesn't guarantee the safety of your arguments then you do need to check it yourself.
Basically, what can go wrong will go wrong, but it will wait until after you've finished testing and have shipped. You can't take chances - you do need a cast-iron guarantee. The key to choosing your guarantee is to inspect your design and see what you really need.
(Even if the function is only called by your own other functions, you might still want to include checks if you think you might later forget about all this and call it from somewhere else without checking.)
I guess it depends on what you plan to do next: if other people should actually write Lua code to work with your code, it would be useful to check arguments (or make it possible by having something like enableDebug). Something useful I came along yesterday is DecoratorsAndDocstrings which makes it possible to document/typecheck functions without altering the implementation.
Another idiom used for this purpose is :
argument = argument or default -- or
argument = type(argument)=="number" and argument or error'I should get a number'
Now we are unsure of what solution is better and decided to ask you: Security checks in Lua - yes or no?
It depends on the type of the project. If your project is small - i.e. only you and your teammate are going to manage it - it is OK to skip the checks, because you should know that you are passing to functions, and it will make the code small and readable. The downside is that when error occurs - and it may happen somewhere totally unexpected - you'll have to spend time debugging and tracing your functions.
On the other hand, if you are creating an library/API to be used by others, it is very appropriate to do type checking. For the user of your library who doesn't know of the internals, it is very helpful to know when he is passing the wrong arguments.
You need to check types where it is important (and should not check where it is not important). Usually I type-check most of the public function arguments plus I add a check anywhere I encounter a type error when debugging.
To shorten the type checks, I use a small arguments() helper function:
https://github.com/lua-nucleo/lua-nucleo/blob/master/lua-nucleo/args.lua
Example:
https://github.com/lua-aplicado/lua-aplicado/blob/master/lua-aplicado/filesystem.lua#L63-66
local write_file = function(filename, new_data)
arguments(
"string", filename,
"string", new_data
)
local file, err = io.open(filename, "w")
if not file then
return nil, err
end
file:write(new_data)
file:close()
file = nil
return true
end