The problem here is that I do not understand well the difference between statements and blocks in control flow.
Looking the ternary operator I can use it to assign a variable. But this is an operator, so it is like applying a function--isn't it?
> my $variable = True ?? 34 !! 42;
34
since in the raku documentation says:
if documentation
if
To conditionally run a block of code, use an if followed by a
condition. The condition, an expression, will be evaluated immediately
after the statement before the if finishes. The block attached to the
condition will only be evaluated if the condition means True when
coerced to Bool. Unlike some languages the condition does not have to
be parenthesized, instead the { and } around the block are mandatory:
do documentation
do
The simplest way to run a block where it cannot be a stand-alone statement is by writing do before it:
so this should work in both cases:
> my $variable = do {34};
34
> my $variable = if True {34;} else {43;}
===SORRY!===
Word 'if' interpreted as a listop; please use 'do if' to introduce the statement control word
------> my $variable = if⏏ True {34;} else {43;}
Unexpected block in infix position (two terms in a row)
------> my $variable = if True⏏ {34;} else {43;}
as said in the error I need to add the do:
> my $variable = do if True {34;} else {43;}
34
So the if really does not run the block...or what is the real problem here?
TL;DR: The actual difference is between statement and expression, not statement and block. do is a statement prefix that creates expressions.
if actually creates a statement (anything that is run in Raku is), however, what it's not is an expression. do is a statement prefix, and what it does is turn statements into expressions.
However, if is not really a first-class function that you can assign to a variable or handle around. Whenever you find pieces of syntax such as that one (or for, for instance), you need to prefix them with do to "expressionify" them.
say &say.^name;# OUTPUT: «Sub» say &do.^name; # OUTPUT: «===SORRY!=== Error while compiling <tmp>Undeclared routine:...
say &if.^name; # OUTPUT: «===SORRY!=== Error while compiling <tmp>Undeclared routine: if used at line 1»
So if, by itself, does not create a block, it does not create an expression, it simply creates a statement. You need to precede it with do if you want it to actually turn it into a expression. It does run the block that's behind it, however.
Let's go back to the original question, statements and blocks. Blocks are objects, first-class citizens. You can use them, apply them, pass them around.
my &ifs = { if $_ {34} else {43}};
ifs(True).say; # OUTPUT: «34»
Statements are throwaway blocks you simply run. In some cases, they are also expressions: they yield a result which, then, you can assign.
my &ifs = { if $_ {34} else {43}};
my $result = ifs(True).say; # OUTPUT: «34»
say $result; # OUTPUT: «True»
The ifs(True).say statement prints to output, it also produces a result that can be assigned. All three lines are also statements, and as a matter of fact, expressions too.
Some control structures, however, do not create expressions.
Some others do; for creates a expression; while does not.
if is an example of this. They don't produce a result. You use them for the side effects: running a statement (if true) or another (if not). You might turn them into a block, as above, and pass them around. Or you can just precede them with do and make them produce a throwaway result, which you can then use.
So it will very much depend on your actual use case. You can surround the if statement with curly braces and create a block; or you can simply use the result creating an expression. Or you might want to use it just for the side effects, doing nothing.
Related
I'd like to make a string substitution in a for block using a named capture. I've expected to get the numbers 1,2,3 as output. But it is Nil for the first run, and then 1 and 2 for the 2nd and 3rd run. How do I use the .subst correctly in the loop construct? I see the same behavior when using a map construct instead the for loop. It does work as expected, if I replace with a fixed string value.
for <a1 b2 c3> -> $var {
say $var;
say $var.subst(/.$<nr>=(\d)/, $<nr>); #.subst(/.$<nr>=(\d)/, 'X'); #OK
}
#`[
This is Rakudo version 2019.11 built on MoarVM version 2019.11
Output:
a1
Use of Nil in string context
in block at test3.pl6 line 3
b2
1
c3
2
]
TL;DR Defer evaluation of $<nr> until after evaluation of the regex. #JoKing++ suggests one way. Another is to just wrap the replacement with braces ({$<nr>}).
What happens when your original code calls subst
Before Raku attempts to call the subst routine, it puts together a list of arguments to pass to it.
There are two values. The first is a regex. It does not run. The second value is $<nr>. It evaluates to Nil because, at the start of a program, the current match object variable is bound to something that claims its value is Nil and any attempt to access the value of a key within it -- $<nr> -- also returns Nil. So things have already gone wrong at this point, before subst ever runs.
Once Raku has assembled this list of arguments, it attempts to call subst. It succeeds, and subst runs.
To get the next match, subst runs the regex. This updates the current match object variable $/. But it's too late to make any difference to the substitution value that has already been passed to subst.
With match in hand, subst next looks at the substitution argument. It finds it's Nil and acts accordingly.
For the second call of subst, $<nr> has taken on the value from the first call of subst. And so on.
Two ways to defer evaluation of $<nr>
#JoKing suggests considering use of S///. This construct evaluates the regex (between the first pair of /s) first, then the replacement (between the last pair of /s). (The same principle applies if you use other valid S syntaxes like S[...] = ....)
If you use subst, then, as explained in the previous section, Raku puts together the argument list for it before calling it. It finds a regex (which it does not run) and a closure (which it does not run either). It then attempts to call subst with those arguments and succeeds in doing so.
Next, subst starts running. It has received code for both the match (a regex) and the substitution (a closure).
It runs the regex as the matching operation. If the regex returns a match then subst runs the closure and uses the value it returns as the substitution.
Thus, because we switched from passing $<nr> as a naked value, which meant it got frozen into Nil, to passing it wrapped in a closure, which deferred its evaluation until $/ had been set to a match with a populated <nr> entry, we solved the problem.
Note that this only works because whoever designed/implemented subst was smart/nice enough to allow both the match and substitution arguments to be forms of Code (a regex for the match and ordinary closure for the substitution) if a user wants that. It then runs the match first and only then runs the substitution closure if it's been passed one, using the result of that latter call as the final substitution. Similarly, S/// works because that has been designed to only evaluate the replacement after it's first evaluated the substitution.
I was trying to understand the following code:
def() ->commands
if(deferred_passive_abilities != [],
let [{ability: class passive_ability, creature: class creature}] items = [];
let found = false;
map(deferred_passive_abilities,
if(cmd = null, add(items, [value]), [cmd, set(found, true)])
where cmd = value.ability.static_effect(me, value.creature));
if(found,
set(deferred_passive_abilities, items);
evaluate_deferred_passive_abilities(),
set(deferred_passive_abilities, []))
)
Haskell appears to have both let and where, but I didn't learn much by a superficial reading of their haskell docs. They also have a let...in, which I didn't understand but it would be good to know if FFL has that.
So, what is the significance of using let versus where? Was it necessary to use let here? (Also, possibly another question: why does it need those semicolons?)
Using let introduces a variable that can be modified. Note how found and items are modified. By contrast, where always introduces immutable symbols.
Semi-colons are used in FFL to create a command pipeline. Normally in FFL, an entire formula is evaluated, resulting in a command or list of commands, and then the commands are executed.
When a semi-colon is present, everything before the semi-colon is treated as an entirely separate formula to everything after the semi-colon. The first formula is evaluated and executed and then the second formula is evaluated and executed.
Semi-colons effectively allow a much more procedural programming style in FFL, without semi-colons it is a purely functional language.
Never knew of let in FFL before this, must be very rare.
Regardless of the insights, the semicolon has to be absolutely necessary, in order to force execution before using the bound variable. In other words, until used the semicolon, the variable does not exist. Does not have a bound value.
This is a big difference to where, which doesn't need of semicolons.
Given the semicolon is not a construction for complete beginners, I could somewhat recommend beginners about variables to stick in where until understanding the trickery of the semicolons.
Is there any reason that this syntax shouldn't work in Qlikview load script??
Let v_myNumber = year(today());
Let v_myString = '2017-08';
If left($(v_myString),4) = text($(v_myNumber)) Then
'do something
Else
'do something else
End If;
I've tried both ways where I convert variable string to number and evaluate against the number variable directly and this way. They won't evaluate to equivalence when they should..
Left function is expecting a string as is getting something else as a parameter. As you are currently doing, the function will be called as Left(2017-08, 4) which is unhandle by QlikView.
If you use Left('$(v_myString)',4), it will evaluate as Left('2017-08', 4) as work as expected. Just adding quotes around the variable it should work.
Although QlikView calls them variables, they should really be seen as "stuff to replaced (at sometimes evaluated) at runtime", which is slightly different from a standard "variable" behaviour.
Dollar sign expansion is a big subject, but in short:
if you are setting a variable - no need for $().
if you are using a variable - you can use $(). depends on its context.
if you are using a variable that needs to be evaluated - you have to use $().
for example in a load script: let var1 = 'if(a=1,1,2)' - here later on the script you will probably want to use this variable as $(var1) so it will be evaluated on the fly...
I hope its a little more clear now. variable can be used in many ways at even can take parameters!
for example:
var2 = $1*$2
and then you can use like this: $(var2(2,3)) which will yield 6
For further exploration of this, I would suggest reading this
I have the following variables:
[string]$eth_netmask = "ext_netmask"
[string]$ext_netmask = "255.255.252.0"
$($eth_netmask) is returning ext_netmask. I was expecting 255.255.252.0
What am I doing wrong?
Thanks for the help in advance!
The command $eth_netmask returns the value of the variable named eth_netmask. The expression $(something) has nothing to do with variables, but instead evaluates the contents of the parentheses before evaluating the rest of the statement. That means that the statement $($eth_netmask) will evaluate in two steps:
1: $($eth_netmask) evaluates to the command "ext_netmask"
2: "ext_netmask" evaluates as a command which has the result of printing ext_netmask to the output.
This format is unnecessary since variables are normally resolved before the rest of the command anyway. My recommendation would be to avoid needing to do this at all if there is any alternative. Putting this kind of roundabout referencing into a piece of code can only cause problems. However, if you can't avoid it for some reason, it is possible to reference a variable the name of which is stored in another variable.
[string]$eth_netmask = "ext_netmask"
[string]$ext_netmask = "255.255.252.0"
Get-Variable -Name $eth_netmask -ValueOnly
This is the point at which the $(something) syntax becomes useful. If you need to use the value that you have just returned in another command, such as if the value was an ip that you were trying to ping, you might do something like this:
Test-Connection $(Get-Variable -Name $eth_netmask -ValueOnly)
I'm working with LINQ in VB.NET and sometimes I get to a query like
For i = 0 To 10
Dim num = (From n In numbers Where n Mod i = 0 Select n).First()
Next
and then it comes the warning "Using the iteration variable in a lambda expression may have unexpected results. Instead, create a local variable within the loop and assign it the value of the iteration variable."
I know it's not a good practice to use the iteration variable in the lambda expression, because lambda expressions are evaluated only when needed. (This question is about that)
Now my question is, how to suppress this warning in cases where the expression is evaluated in-place, with constructions like First(), Single(), ToList(), etc. (It's only a warning, but i like my code clean.)
(Declaring a local variable and passing the iteration variable to it is an option, but I'm looking for a clean solution.)
In this particular case where the lambda is evaluated immediately, then you can safely eliminate the warning by moving the declaration of the iteration variable outside the for loop.
Dim i = 0
For i = 0 To 10
...
I do want to stress though that this only works if the lambda does not escape the for loop (true for your scenario).
Also here is a detailed link to an article I wrote on this warning (why it exists, how to avoid it, etc ...)
http://blogs.msdn.com/b/jaredpar/archive/2007/07/26/closures-in-vb-part-5-looping.aspx