TCL:: How to print numbers from 10 to 1 using while loop in TCL (How to decrement the variiable value) - while-loop

TCL Script:
set a 10
while {$a < 1} {
puts $a
incr a
}
Expected output:
10
9
8
7
6
5
4
3
2
1
I am trying to print numbers from 10 to 1. But its not working (It prints nothing).
Is there a way to "decrement" the variable value (decr a)?
Thanks,
Kumar

Change the condition to $a > 1 and to decrement the value, you have to use incr a -1. Here we gave the step as -1.

same can be done by for loop also
for {set i 10} {$i > 1} {incr i -1} {
puts $i
}

I think your loop body is never executed because the condition yields false the very first time. You probably wanted to write ">" instead of "<".

Related

Reading fields in previous lines for moving average

Main Question
What is the correct syntax for recursively calling AWK inside of another AWK program, and then saving the output to a (numeric) variable?
I want to call AWK using 2/3 variables:
N -> Can be read from Bash or from container AWK script.
Linenum -> Read from container AWK program
J -> Field that I would like to read
This is my attempt.
Container AWk program:
BEGIN {}
{
...
# Loop in j
...
k=NR
# Call to other instance of AWK
var=(awk -f -v n="$n_steps" linenum=k input-file 'linenum-n {printf "%5.4E", $j}'
...
}
END{}
Background for more general questions:
I have a file for which I would like to calculate a moving average of n (for example 2280) steps.
Ideally, for the first n rows the average is of the values 1 to k,
where k <= n.
For rows k > n the average would be of the last n values.
I will eventually execute the code in many large files, with several columns, and thousands to millions of rows, so I'm interested in streamlining the code as much as possible.
Code Excerpt and Description
The code I'm trying to develop looks something like this:
NR>1
{
# Loop over fields
for (j in columns)
{
# Rows before full moving average is done
if ( $1 <= n )
{
cumsum[j]=cumsum[j]+$j #Cumulative sum
$j=cumsum[j]/$1 # Average
}
#moving average
if ( $1 > n )
{
k=NR
last[j]=(awk -f -v n="$n_steps" ln=k input-file 'ln-n {printf "%5.4E", $j}') # Obtain value that will get ubstracted from moving average
cumsum[j]=cumsum[j]+$j-last[j] # Cumulative sum adds last step and deleted unwanted value
$j=cumsum[j]/n # Moving average
}
}
}
My input file contains several columns. The first column contains the row number, and the other columns contain values.
For the cumulative sum of the moving average: If I am in row k, I want to add it to the cumulative sum, but also start subtracting the first value that I don't need (k-n).
I don't want to have to create an array of cumulative sums for the last steps, because I feel it could impact performance. I prefer to directly select the values that I want to substract.
For that I need to call AWK once again (but on a different line). I attempt to do it in this line:
k=NR
last[j]=(awk -f -v n="$n_steps" ln=k input-file 'ln-n {printf "%5.4E", $j}'
I am sure that this code cannot be correct.
Discussion Questions
What is the best way to obtain information about a field in a previous line to the one that AWK is working on? Can it be then saved into a variable?
Is this recursive use of AWK allowed or even recommended?
If not, what could be the most efficient way to update the cumulative sum values so that I get an efficient enough code?
Sample input and Output
Here is a sample of the input (second column) and the desired output (third column). I'm using 3 as the number of averaging steps (n)
N VAL AVG_VAL
1 1 1
2 2 1.5
3 3 2
4 4 3
5 5 4
6 6 5
7 7 6
8 8 7
9 9 8
10 10 9
11 11 10
12 12 11
13 13 12
14 14 13
14 15 14
If you want to do a running average of a single column, you can do it this way:
BEGIN{n=2280; c=7}
{ s += $c - a[NR%n]; a[NR%n] = $c }
{ print $0, s /(NR < n : NR ? n) }
Here we store the last n values in an array a and keep track of the cumulative sum s. Every time we update the sum we correct by first removing the last value from it.
If you want to do this for a couple of columns, you have to be a bit handy with keeping track of your arrays
BEGIN{n=2280; c[0]=7; c[1]=8; c[2]=9}
{ for(i in c) { s[i] += $c[i] - a[n*i + NR%n]; a[n*i + NR%n] = $c[i] } }
{ printf $0
for(i=0;i<length(c);++i) printf OFS (s[i]/(NR < n : NR ? n))
printf ORS
}
However, you mentioned that you have to add millions of entries. That is where it becomes a bit more tricky. Summing a lot of values will introduce numeric errors as you loose precision bit by bit (when you add floats). So in this case, I would suggest implementing the Kahan summation.
For a single column you get:
BEGIN{n=2280; c=7}
{ y = $c - a[NR%n] - k; t = s + y; k = (t - s) - y; s = t; a[NR%n] = $c }
{ print $0, s /(NR < n : NR ? n) }
or a bit more expanded as:
BEGIN{n=2280; c=7}
{ y = $c - k; t = s + y; k = (t - s) - y; s = t; }
{ y = -a[NR%n] - k; t = s + y; k = (t - s) - y; s = t; }
{ a[NR%n] = $c }
{ print $0, s /(NR < n : NR ? n) }
For a multi-column problem, it is now straightforward to adjust the above script. All you need to know is that y and t are temporary values and k is the compensation term which needs to be stored in memory.

Tcl formatting :how to display multiple rows and columns with formatting

I need to print the following based on the variable n.
Example: if n=2
I need to print:
1. -3 0
2. 3 0
3. 0 -3
4. 0 3
If n=3
I need to print:
1. -3 0 0
2. 3 0 0
3. 0 -3 0
4. 0 3 0
5. 0 0 -3
6. 0 0 3
If n=4, I need to print:
1. -3 0 0 0
2. 3 0 0 0
3. 0 -3 0 0
4. 0 3 0 0
5. 0 0 -3 0
6. 0 0 3 0
7. 0 0 0 -3
8. 0 0 0 3
The key thing you need here is format. It's great for producing output in fixed-width text form. Now, writing the format-items for a format string is an art (very closely related to doing the same for sprintf() in C) that quite a few people lack nowadays, but something like this is what you want were you just doing the n == 4 case:
puts [format "%d. %2d %2d %2d" $count $c1 $c2 $c3]
In this case, however, you've got a more complex problem because you have a variable number of fields. That makes things trickier; you're probably best building things up piecemeal with the help of a procedure to do the formatting of a single line:
proc generateLine {n i} {
set line [format "%d." $i]
for {set x 1} {$x <= $n} {incr x} {
# Double-ternary conditional operator
set v [expr {$i == $x*2-1 ? -3 : $i == $x*2 ? 3 : 0}]
append line [format " %2d" $v]
}
return $line
}
Now that we have that, the rest of the program is simple enough:
# Assume that the n variable has been set already
for {set i 1} {$i <= $n*2} {incr i} {
puts [generateLine $n $i]
}
Very often in programming, it's easiest if you split a program into several pieces with sensible boundaries between. Knowing where to split is something that you get better at with experience, but very often the split is in the right place if you can give a sensible name to the split out piece. For example, above I saw that I wanted to do some work for each line and some work to iterate over all the lines needed; that was the obvious place to break things apart and make a procedure, generateLine. The outer part is also quite nameable (perhaps generateListOfLines?) but that isn't so important here.

What's the equivalent in Perl 6 to star expressions in Python?

In Python 3, suppose you run a course and decide at the end of the semester that you’re going to drop the first and last homework grades, and only average the rest of them:
def drop_first_last(grades):
first, *middle, last = grades
return avg(middle)
print drop_first_last([100,68,67,66,23]);
In Perl 6:
sub drop_first_last(#grades) {
my ($first, *#middle, $last) = #grades;
return avg(#middle);
}
say drop_first_last(100,68,67,66,23);
Leads to the error "Cannot put required parameter $last after variadic parameters".
So, what's the equivalent express in Perl 6 as star expressions in Python?
sub drop_first_last(Seq() \seq, $n = 1) { seq.skip($n).head(*-$n) };
say drop_first_last( 1..10 ); # (2 3 4 5 6 7 8 9)
say drop_first_last( 1..10, 2 ); # (3 4 5 6 7 8)
The way it works: convert whatever the first argument is to a Seq, then skip $n elements, and then keep all except the last $n elements.
Perl5:
sub drop_first_last { avg( #_[ 1 .. $#_-1 ] ) } #this
sub drop_first_last { shift;pop;avg#_ } #or this
Perl6:
sub drop_first_last { avg( #_[ 1 .. #_.end-1 ] ) }
Use a slice.
sub drop_first_last (#grades) {
return avg(#grades[1..*-2])
}
Workarounds such as have been shown in the rest of the answer are correct, but the short answer to your question is that there is no equivalent expression in Perl 6 to the * in Python.
This kind of arguments are called, in general, variadic, and *slurpy+ in Perl 6, because they slurp the rest of the arguments. And that's the key, the rest. There can be no argument declared after an slurpy argument in a subroutine's signature. This example below also uses a workaround:
sub avg( #grades ) {
return ([+] #grades) / +#grades;
}
sub drop_first_last($first, *#other-grades) {
return avg(#other-grades[0..*-1]);
}
my #grades = <10 4 8 9 10 8>;
say drop_first_last( |#grades );
but is first using the slurpy * in the signature to show how it works, and then, by calling it with |#grades, is flattening the array instead of binding it into an array argument. So the long answer is that there is actually an * or variadic symbol in signatures in Perl 6, and it works similarly to how it would do it in Python, but it can only be placed last in those signatures since it captures the rest of the elements of the expression.
In case the first and last values are needed for some other reason,
unflattened list structure inside slices maps across to the results
in most cases, so you can do this (you have to use a $ sigil on
$middle to prevent autoflattening):
my #grades = (1,2,3,4,5,6);
my ($first, $middle, $last) = #grades[0,(0^..^*-1),*-1];
$first.say; $middle.say; $last.say;
#1
#(2 3 4 5)
#6

Why does 100 ~~ ^100 return false in Perl 6?

perl6 -e '100 ~~ ^100' returns False, where it looks like to me it should return True, as 100 is in the range between 0 and 100. Is this a part of the design of the Range class that I'm just not understanding here or is this a bug?
The syntax ^100 is short-hand for 0 ..^ 100 and the ^ means "excluding". 0 ..^ 100 is actually the numbers 0 through 99. That's because with ^100 you get a list with exactly 100 elements - which is very useful for for loops.
Don't forget you can output the whole list with say (^100).list.
In addition to that, there's also ^.. and ^..^ which exclude the first element or the first and last element.
The caret ^ indicates that the endpoint is excluded from the range, so 100 is actually not included.
perl6 -e '100 ~~ 100' will return true.
Read as: part of the design, cf. https://doc.perl6.org/type/Range

Formula in gawk

I have a problem that I’m trying to work out in gawk. This should be so simple, but my attempts ended up with a divide by zero error.
What I trying to accomplish is as follows –
maxlines = 22 (fixed value)
maxnumber = > max lines (unknown value)
Example:
maxlines=22
maxnumber=60
My output should look like the following:
print lines:
1
2
...
22
print lines:
23
24
...
45
print lines:
46 (remainder of 60 (maxnumber))
47
...
60
It's not clear what you're asking, but I assume you want to loop through input lines and print a new header (page header?) after every 22 lines. Using a simple counter and check for
count % 22 == 1
which tells you it's time to print the next page.
Or you could keep two counters, one for the absolute line number and another for the line number within the current page. When the second counter exceeds 22, reset it to zero and print the next page heading.
Worked out gawk precedence with some help and this works -
maxlines = 22
maxnumber = 60
for (i = 1; i <= maxnumber; i++){
if ( ! ( (i-1) % maxlines) ){
print "\nprint lines:"
}
print i
}