How to iterate while loop results to a list in Tcl?
set i 0
while { $i < ($num +1) } {
set list $i
incr i
}
create a list by using above while loop.
You've almost got it. You just need to use lappend inside the loop (and for is more idiomatic than while for this sort of thing). Note that where you put newlines is important; you can't put them between a while condition and the body of the loop (without putting a backslash in, which is ugly).
for {set i 0} {$i < ($num + 1)} {incr i} {
lappend list $i
}
You could write the following equivalent code, but the above is clearer:
set i 0
while {$i < ($num + 1)} {
lappend list $i
incr i
}
Finally, I'd actually use this:
for {set i 0} {$i <= $num} {incr i} {
lappend list $i
}
It emphasises that $num is the limit more clearly, with <= marking it as an inclusive limit. I'd do the same in many other languages too.
$i takes a value that is always going to be a number as the code is written. This value is then assigned to list. list should then be printed or stored... exactly what you want to do with list is not clear because as the code is written, it is simply overwritten on each iteration of the loop.
You also have a problem with your loop condition possibly. Unless you are looking for a list of numbers generated by the increment of i the code should be:
set i 0
while { i < ($num + 1)} {
set list $i
[here would be where you print/store list]
incr i
}
Does this help?
Related
I want to write two things in Powershell.
For example;
We have a one list:
$a=#('ab','bc','cd','dc')
I want to write:
1 >> ab
2 >> bc
3 >> cd
4 >> dc
I want this to be dynamic based on the length of the list.
Thanks for helping.
Use a for loop so you can keep track of the index:
for( $i = 0; $i -lt $a.Count; $i++ ){
"$($i + 1) >> $($a[$i])"
}
To explain how this works:
The for loop is defined with three sections, separated by a semi-colon ;.
The first section declares variables, in this case we define $i = 0. This will be our index reference.
The second section is the condition for the loop to continue. As long as $i is less than $a.Count, the loop will continue. We don't want to go past the length of the list or you will get undesired behavior.
The third section is what happens at the end of each iteration of the loop. In this case we want to increase our counter $i by 1 each time ($i++ is shorthand for "increment $i by 1")
There is more nuance to this notation than I've included but it has no bearing on how the loop works. You can read more here on Unary Operators.
For the loop body itself, I'll explain the string
Returning an object without assigning to a variable, such as this string, is effectively the same thing as using Write-Output.
In most cases, Write-Output is actually optional (and often is not what you want for displaying text on the screen). My answer here goes into more detail about the different Write- cmdlets, output streams, and redirection.
$() is the sub-expression operator, and is used to return expressions for use within a parent expression. In this case we return the result of $i + 1 which gets inserted into the final string.
It is unique in that it can be used directly within strings unlike the similar-but-distinct array sub-expression operator and grouping operator.
Without the subexpression operator, you would get something like 0 + 1 as it will insert the value of $i but will render the + 1 literally.
After the >> we use another sub-expression to insert the value of the $ith index of $a into the string.
While simple variable expansion would insert the .ToString() value of array $a into the final string, referencing the index of the array must be done within a sub-expression or the [] will get rendered literally.
Your solution using a foreach and doing $a.IndexOf($number) within the loop does work, but while $a.IndexOf($number) works to get the current index, .IndexOf(object) works by iterating over the array until it finds the matching object reference, then returns the index. For large arrays this will take longer and longer with each iteration. The for loop does not have this restriction.
Consider the following example with a much larger array:
# Array of numbers 1 through 65535
$a = 1..65535
# Use the for loop to output "Iteration INDEXVALUE"
# Runs in 106 ms on my system
Measure-Command { for( $i = 0; $i -lt $a.Count; $i++ ) { "Iteration $($a[$i])" } }
# Use a foreach loop to do the same but obtain the index with .IndexOf(object)
# Runs in 6720 ms on my system
Measure-Command { foreach( $i in $a ){ "Iteration $($a.IndexOf($i))" } }
Another thing to watch out for is that while you can change properties and execute methods on collection elements, you can't change the element values of a non-collection collection (any collection not in the System.Concurrent.Collections namespace) when its enumerator is in use. While invisible, foreach (and relatedly ForEach-Object) implicitly invoke the collection's .GetEnumerator() method for the loop. This won't throw an error like in other .NET languages, but IMO it should. It will appear to accept a new value for the collection but once you exit the loop the value remains unchanged.
This isn't to say the foreach loop should never be used or that you did anything "wrong", but I feel these nuances should be made known before you do find yourself in a situation where a better construct would be appropriate.
Okey,
I fixed that;
$a=#('ab','bc','cd','dc')
$a.Length
foreach ($number in $a) {
$numberofIIS = $a.IndexOf($number)
Write-Host ($numberofIIS,">>>",$number)
}
Bender's answer is great, but I personally avoid for loops if at all possible. They usually require some awkward indexing into arrays and that ugly setup... The whole thing just ends up looking like hieroglyphics.
With a foreach loop it's our job to keep track of the index (which is where this answer differs from yours) but I think in the end it is more readable then a for loop.
$a = #('ab', 'bc', 'cd', 'dc')
# Pipe the items of our array to ForEach-Object
# We use the -Begin block to initialize our index variable ($x)
$a | ForEach-Object -Begin { $x = 1 } -Process {
# Output the expression
"$x" + ' >> ' + $_
# Increment $x for next loop
$x++
}
# -----------------------------------------------------------
# You can also do this with a foreach statement
# We just have to intialize our index variable
# beforehand
$x = 1
foreach ($number in $a){
# Output the expression
"$x >> $number"
# Increment $x for next loop
$x++
}
Maybe this is pretty stupid, but I really can't find a soulution. I created two variables and want to transform them into lists.
This commands are tool specific, but they work the way I want:
redirect max_transition {report_constraint -view $pargs(-scenario) -drv_violation_type {max_transition} -all_violators} -variable
redirect max_capacitance {report_constraint -view $pargs(-scenario) -drv_violation_type {max_capacitance} -all_violators} -variable
Now I want to create tcl lists out of them. I could use a loop, because the data has the same structure.
set reports {$max_transition $max_capacitance}
set report_length [llength $reports]
for {set i 0} {$i < $report_length} {incr i} {
set tns_value 0
set max_wns 0
set vios 0
set report [lindex $reports $i]
puts $report
# remove all uneccessary white spaces
set no_space [regexp -all -inline {\S+} $report]
# insert a new line for every path
set insert_lines [string map {" U_" \nU_} $no_space]
# create list out of result reports
set report_list [split $insert_lines "\n"]
if {[llength $report_list] > 1} {
for {set i 1} {$i < [llength $report_list]} {incr i} {
# get value of violation
set slack [lindex [split [lindex $report_list $i] " "] 3]
set tns_value [expr $tns_value + $slack]
if {$vios == 0} {set max_wns $slack}
incr vios
}
}
# write out values
puts "$pargs(-scenario), $report, $max_wns, $tns_value, $vios"
}
But this does not work out. The loop just puts out the variable's names (because of "puts $report") but not its content.
If I do it without a loop (so for each variable the same code consecutively), I get the lists I want.
So how can I process these variables as a whole in a loop?
The problem lies in the below loop variable i, it is overriding variable value of outer-loop. Try changing inner-loop variable to j.
for {set i 1} {$i < [llength $report_list]} {incr i} {
# get value of violation
set slack [lindex [split [lindex $report_list $i] " "] 3]
set tns_value [expr $tns_value + $slack]
if {$vios == 0} {set max_wns $slack}
incr vios
}
It's hard to write an answer for this since so much is unknown. To begin with, you should probably change to assignment by list and a foreach loop like this:
set reports [list $max_transition $max_capacitance]
foreach report $reports {
Since you don't really need to use a for loop here, it makes sense to simplify it. Please comment and I will iteratively improve the answer if I can.
I am still new to powershell, right now I study foreach with break, I understand the concept, but when it combined with additional variable and break; it confuses me, here is the code:
$i=0
$varZ = (10,20,30,40)
foreach ($var in $varZ)
{
$i++
if ($var -eq 30)
{
break
}
}
Write-Host "30 was found in array position $i"
the result I get showing that variable $i = 3, where $var = 30
but what confuses me, as I understand $i starts with 0 and there is an array $varZ (10,20,30,40), as I understand when $i = 0 $var = 10, hence $i = 3 $var = 40? please correct me and help me understand this code
You are incrementing $i before you do your conditional check; whereas; it should be done after your break statement. Although $i is set to 0 before you begin your loop, you immediately increment by 1 with your statement $i++; thus, when $var is 10, $i which was 0 not become 0+1=1 and so forth.
I have an array in my script which I want to use it in for, like this:
for j in "${list[#]}"
do
func $j
done
In function func, sometimes another member will add to the list array, but the for iterates as many time as it was initiated(before the for started)
I want "for" to iterate based on the updated array content
some lines of that function:
if [ $s1 -gt 0 ]
then
(( k = $k +1 ))
list[$k]=$id2
fi
As Eric Renouf said, modifying the list you're working on can be tricky. As long as you're only appending new elements (to the end of the list), and just want those new elements included in the iteration, you can use something like this:
for ((i=0; i<${#list[#]}; i++)); do
#...
if (( s1 > 0 )); then
list+=( "$id2" )
fi
done
Since the length of the list (${#list[#]}) is recalculated every time around, the loop will include new elements. Also, the +=( ) syntax guarantees you're always strictly appending.
It appears that k is the index of the last element, meaning your function only appends items to the end of the list. It seems the best option is to iterate while a separate counter is less than k.
i=0
while (( i < k )); do
j=${list[i]}
func "$j"
((i++))
done
I am new to Perl and am trying to write a script that will only print the even numbered lines of an array. I have tried multiple different methods of finding the size to use as the condition for my while loop, but I always end up getting an infinite loop of the first line without the program terminating. The array being input is a text file, input with the form "program.pl < foo.txt". Have I made a logic or syntax error?
#input = <STDIN>;
$i = $1;
$size = $#input + $1;
while ($size >= $i) {
print "$input[$i]";
$i = ($i + $2);
}
Don't call your problem with
program.pl < foo.txt
Instead, just pass 'foo.txt' as a parameter:
program.pl foo.txt
Inside your script, rely on default reading from <> and the line number variable $.:
use strict;
use warnings;
while (<>) {
next if $. % 2; # Skip odd numbers.
print;
}
Assuming you already have an array with all of your input, in your example #input, you can get all of the even index entries into another array using an Array Slice like so:
my #input_even_entries_only = #input[grep { $_ % 2 == 0 } 0..$#input];
The expression inside the square brackets evaluates to all of the even numbers between 0 and $#input.
You can then use a regular for/foreach loop to go through the resulting array:
for my $val (#input_even_entries_only) {
print "$val";
}
If you are trying to print lines of an array indexed at even numbers then, try this:
use strict;
use warnings;
my #input = <DATA>;
for(my $i=0; $i<=$#input; $i+=2) {
print $input[$i];
}
__DATA__
1
2
3
4
5
6
Output:
1
3
5
I've no idea what you are doing with the $1 and $2 variables. Did you think they were just numbers?
When you use a variable that has not been assigned a value, it is undefined, which will be converted to 0 when used in numerical context. If you do not use use warnings, this is done silently, and will be rather confusing.
Other than that, your code is not too far off. It should be something like:
use strict;
use warnings;
my #input = <>; # <> is more flexible and does the same thing
my $i = 1;
while ($i <= $#input) {
print $input[$i];
$i += 2;
}
Though of course, storing the entire file in an array is not necessary, and most often you should just loop over it instead. Like Miller has shown in his answer, which is probably the solution I would suggest. Using a for loop like JS shows is an excellent way to control the loop.