So I'm learning Rust and I'm learning about pattern matching and "if let" statements as alternatives to matching expressions. I was watching this video regarding "if let" which is mentioned at 11:00 and they give this example:
fn main() {
let some_value: Option<i32> = Some(3);
if let Some(3) = some_value {
println!("three");
}
}
I get that this is useful if you only have one specific pattern you want to match and the matching expression is too verbose, but if this is the case, couldn't you simply do this?:
fn main() {
let some_value: Option<i32> = Some(3);
if some_value == Some(3) {
println!("three");
}
}
Is there something about this expression that is inferior to the "if let" statement that I'm not aware of?
Well the example isn't very good. But consider this:
fn foo(o: Option<i32>) {
if let Some(n) = o {
println!("{n}");
}
}
Which prints the value inside the option only if it is Some(...).
if let is a shortand version of match if you're only interested in one specific case.
Also note that == can only be used with types implementing PartialEq, while if let works with
any type you can pattern match on.
The if let form lets you be more generic than your example. You can't do if some_value == Some(n) unless n is defined, but you can do:
if let Some(n) = some_value { ... }
Where that now gives you an n you can work with.
I think you'll find as you work with Rust more that the match form Some(n) shows up a lot more often than Some(1) for some specific value.
Don't forget you can also do this:
if let Some(1) | Some(2) = some_value { ... }
Where that's a lot more thorny with a regular if unless you're using matching like this:
if matches!(some_value, Some(1) | Some(2)) { ... }
So there's a number of useful options here that might fit better in some situations.
Don't forget about cargo clippy which will point out if there's better ways of expressing something.
Another advantage of if let besides what mentioned in the other answers is that not always you can use ==. There are types that do not support comparisons (they do not implement PartialEq) for various possible reasons (the author of the library didn't think about it/the types contain some structs that doesn't support comparisons/comparisons don't make sense for those types). In those cases, you cannot use ==, but you still can use if let.
Related
There are some known rules, which refer to various combinations of IF statements (or if/else or switch/case statements etc). These rules can be used for refactoring.
Example 1:
"Nested IFs with single body can be always replaced by one IF with AND conditions."
if (a) {
if (b) {
doSomething();
}
}
can be rewritten as:
if (a && b) {
doSomething();
}
Example 2:
"Subsequent IFs with the same body can be always replaced by one IF with OR condition."
int i = 0;
if (a) {i = 1;}
if (b) {i = 1;}
can be rewritten as:
if (a || b) {i = 1;}
I'm sure there are many more similar rules. I would like to know if there is a known theory behind this, which gives generic pattern for creating such rules. I would like to see a list of other known rules. I think boolean algebra does not exacly handle this, because it uses mathematical syntax which does not exactly translate to programming syntax.
There are similar questions, which always ask about one rule or another. This question is completely generic, not about particular rules, but about the theory behind them.
Why can I do this:
if (int result=getValue(); result > 100) {
}
but cannot do this:
while (int result=getValue(); result > 100) {
}
Why discriminate against while? A condition is a condition. Why while cannot evaluate it like if can?
In order to achieve the desired behavior with while, I'd have to implement it this this way:
int result = getValue();
while (result > 100) {
//do something
result = getValue();
}
Because we already have a while-loop-with-initializer. It's spelled:
for (int result=getValue(); result > 100;) {
}
There are 3 reasons I can think of for not adding this syntax.
There is a perfectly suitable construct, namely the for loop, that can be used for exactly this purpose.
Adding any feature to the language is a lot of work, and a very strong case needs to be made for such a proposal. Given the first point, I don't see this happening.
In my opinion this is the most important one: If this feature were added to the language (let's say for a convenient syntax, or something like that), then it can essentially never be removed from the language. This means that the while (;) syntax is forever banned, and there could very well be some other semantics that we would like to express using such a syntax, and giving up this option is not something that should be done without careful thought.
Simple example: I have some functions and I need to call them all, to modify a structure, only in one function. With these simple functions the task can be made in ways that don't use void, but in others tasks you have to use void. So what can you do?
type player = { mutable name : string; mutable points : int } ;;
let putname brad = match brad with
{ name = x; points = y } -> { name = brad; points = y } ;;
let putpoint guy score = match guy with
{ name = x; points = y } -> { name = x; points = score } ;;
let loosers listplayer guy = guy :: listplayer ;;
Here is the problem - How can I do the next function?
let someoneloses guy = void
guy = putpoint guy 0 ;;
listplayer = loosers (listplayer guy) ;;
Given you are using the name "void" I'm assuming you are more familiar with C (or C++). In OCaml the equivalent of "void" (the name of the type for no value) is "unit". There is another difference though: while in C the syntax is complex enough that it have constructs for no values (for instance, you can either "return a_value;" or "return;", two differents yet syntactically valid use cases for the keyword "return"), in OCaml the syntax is simpler and always require a value. So we have a notation for "nothing", which is, astutely but maybe also confusedly, is written "()".
So, the OCaml equivalent of the C:
void do_nothing(void) { return; }
is written:
let do_nothing () = ()
(notice how OCaml syntax is simpler and easier to grok once you got the "()" trick).
Now that this is hopefully clearer, back to your question.
A function that returns nothing is a function that return "()", either explicitly (as "do_nothing" above) or because it ends with an expression that has "()" as its value. For instance, an assignment (something tell me you'll love assignments), such as:
let putpoint guy score = guy.points <- score
Now back to your problem. You seem to be doing some kind of game with players represented as mutable records and some functions modifying those records as the game develop. You need not use pattern matching for that. Actually, functions such as "putpoint" above is probably what you want. But then you need some more state in your program: the list of loosers for instance is probably going to be a reference to a list that you modify etc.
This is the "imperative" side of OCaml but there is another one, which is usually regarded as more elegant although often slower in general (but not in functional languages which are optimised for this technique), consisting of refraining from altering state (changing values of things) but instead using functions merely taking values and returning values. Implemented like this, a player would be represented as an immutable record and each function acting a user would take an "old user" and return a "new user", and the same goes with the list of loosers, and so on. Actually, the whole game state would be represented as a big value that the "main loop" of your program would, given the previous value, and possible also the time and user inputs, would compute the "new state" and return it.
Have fun!
Also, your question has nothing to do with ocaml-batteries.
since you are using mutable data, you just have to assigned the value directly.
let p = {name = "me";points=0};;
let update x = x.name <- "you";
x.points <- 3;;
update p ;;
I would like to perform the following:
if(x == true)
{
// do this on behalf of x
// do this on behalf of x
// do this on behalf of x
}
Using a conditional operator, is this correct?
x == true ? { /*do a*/, /*do b*/, /*do c*/ } : y == true ? ... ;
Is this malformed?
I am not nesting more than one level with a conditional operator.
The expressions I intend to use are highly terse and simple making a conditional operator, in my opinion, worth using.
P.S. I am not asking A. Which I should use? B. Which is better C. Which is more appropriate
P.S. I am asking how to convert an if-else statement to a ternary conditional operator.
Any advice given on this question regarding coding standards etc. are simply undesired.
Don't compare booleans to true and false. There's no point because they're true or false already! Just write
if (x)
{
// do this on behalf of x
// do this on behalf of x
// do this on behalf of x
}
Your second example doesn't compile because you use { and }. But this might
x ? ( /*do a*/, /*do b*/, /*do c*/ ) : y ? ... ;
but it does depend on what /*do a*/ etc are.
Using comma operator to string different expressions together is within the rules of the language, but it makes the code harder to read (because you have to spot the comma, which isn't always easy, especially if the expression isn't really simple.
The other factor is of course that you can ONLY do this for if (x) ... else if(y) ... type conditionals state.
Sometimes, it seems like people prefer "short code" from "readable code", which is of course great if you are in a competition of "who can write this in the fewest lines", but for everything else, particularly code that "on show" or shared with colleagues that also need to understand it - once a software project gets sufficiently large, it usually becomes hard to understand how the code works WITHOUT obfuscation that makes the code harder to read. I don't really see any benefit in using conditional statements in the way your second example described. It is possible that the example is bad, but generally, I'd say "don't do that".
Of course it works (with C++11). I have not tried a solution but following Herb Sutters way you can use ether a function call or a lambda which is immediately executed:
cond ?
[&]{
int i = some_default_value;
if(someConditionIstrue)
{
Do some operations ancalculate the value of i;
i = some calculated value;
}
return i;
} ()
:
somefun() ;
I have not tried to compile it but here you have an result whih is either computed with an lambda or an normal function.
For example
int f(int a) {
...
return a > 10;
}
is that considered acceptable (not legal, I mean is it ``good code''), or should it always be in a conditional, like this
int f(int a) {
...
if (a > 10)
return 1;
else
return 0;
}
It would be acceptable - if your return type was bool.
This is absolutely acceptable! In fact, Joel mentioned this on the latest stackoverflow podcast. He said it was the one thing he's had to show almost every programmer that starts at Fog Creek.
return a > 10 ? 1 : 0;
... makes more sense because you're returning an int, not a bool.
The first case is perfectly good, far better than the second, IMHO. As a matter of readability, I personally would do
return (a > 10);
but that is a minor nit, and not one everyone would agree on.
I don't see anything wrong with it. If anything it's more concise and I think most developers with moderate experience would prefer it.
The first is much preferable to me, since it is more concise. (And it avoids multiple returns:)
I'd rather write bool f(int); and the first form as bool is the boolean type in C++. If I really need to return an int, I'd write something like
int f(int) {
...
const int res = (i>42) ? 1 : 0;
return res;
}
I'd never understood why people write
if (expr == true)
mybool = true ;
else
mybool = false;
instead of the plain
mybool = expr;
Boolean algebra is a tool that any developer should be able to handle instinctively
Moreover, I'd rather define a named temporary as some debuggers don't handle function return values very well.
I think its perfectly acceptable, provided that you ensure that you make an extra effort to maintain readability. Like I would make sure that the method name is very unambiguous and you use good variable names.
The second alternative that you provided I think is almost worse because it involves a branch statement and multiple return statements and these things increase the complexity of the method while themselves reducing its readability.
Not only is that syntax 100% acceptable, you should also feel free to use boolean expressions outside of if statements, i.e. int x = i && ( j || k ); (or returning values like that).
I think part of it has to do with the style and culture of the language. The first example you have written is what would be expected from an experienced C programmer. They would much rather strangle themselves than put in an unnecessary block of statements.
I think it is perfectly acceptable when the language allows it and the usage is part of the paradigm of that language
I just tried three different variants with GCC:
int one(int x) { return (x > 42) ? 1 : 0; }
int two(int x) { return x > 42; }
int thr(int x) { if (x > 42) return 1; else return 0; }
As soon as you enable some optimization, the generated code for all of them is the same. So you should use the variant that is easiest to read.
I'll typically do the former over the latter.