I am using multidimensional associative array in tcl ,from fourth column to N column I am storing values and i will compare each of those with second column and store the result in third column for each of row's values.Sometimes it searches the perfectly sometimes it skips the values and gives out error.Is there any better way to search in multidimensional array in tcl ? What is the problem with this code?
for {set index 1} {$index < $count} {incr index} {
for {set val 3} {$val < $d1 } {incr val} {
if {$flag1 != 1} {
if {$asarr($index,2)== $asarr($index,$val)} {
set asarr($index,1) 50
} else {
set asarr($index,1) 100
set flag1 1
break
}
}
}
set flag1 0
}
There is a discrepancy between your code and the problem explanation in your question.
From your question I summarise these requirements:
data is stored in 2 dimensional array
each row stores one set of data
within each row, data is stored in columns where:
1st column is not mention in the original question
2nd column contains a value against which all 'data columns' will be compared
3rd column is where the result of the comparison will be stored (looking at the code, result is either a 50 or a 100)
4th to Nth column hold the data that needs to be compared against value of 2nd column
And this is what your code actually does:
# start iterating array by rows
# first row has index 1
for {set index 1} {$index < $count} {incr index} {
# start iterating over columns
# NOTE: either column indexes are assumed to start at 0
# or we, in fact, start with the 3rd column instead of 4th
for {set val 3} {$val < $d1 } {incr val} {
# check the special flag named 'flag1'
# NOTE: it would be better to give it a meaningful name
# such as 'found' since it signals that we found
# an element equal to the one in 2nd column
if {$flag1 != 1} {
if {$asarr($index,2)== $asarr($index,$val)} {
# if the element at current position ($index,$val)
# is equal to the element at ($index,2)
# write '50' at ($index,1)
# NOTE: result is stored in 1st column,
# not in 3rd as the question would assume
set asarr($index,1) 50
} else {
# if the current element is not equal to the one
# in 2nd column, write '100' at ($index, 1).
# Again, this is not the 3rd column as requested originally
set asarr($index,1) 100
# set the special flag to 1, meaning that the above
# IF clause will not be run again
set flag1 1
# execute a break, meaning that we immediately interrupt
# the inner FOR loop, meaning that the above IF clause
# will not be run again in this iteration.
# As Donal pointed out, you could completely
# remove the $flag1 and achieve the same result with
# this break clause
break
}
}
}
# reset the flag so that IF clause is executed at least once
# for the next iteration of the outer loop
set flag1 0
}
If we assume that the 'flag1' is set to 0 (or anything != 1) before the FOR loops start, then the end result is that the code iterates through array rows, compares each column (starting from 3rd!) against column number 2, it repeats this for all other columns BUT ONLY if their values are equal to column 2 and EACH TIME OVERWRITES THE SAME value '50' over column 1. As soon as a different value is encountered, the result '100' is written to column 1 and the rest of the values are immediately skipped. Execution then starts from the beginning but with the next row.
How does that compare against your original intentions?
The code could (and should) be greatly simplified at least so it doesn't overwrite the same value many times, however, I am not exactly sure what you are trying to achieve. Please try to clarify your requirements.
I don't know if this will be useful (you said that the code does not behave as you would like it to), but here is an alternative version that behaves according to the requirements I guessed from your question (and listed above):
for {set index 1} {$index < $count} {incr index} {
set asarr($index,3) 50
for {set val 4} {$val < $d1 } {incr val} {
if {$asarr($index,2)!= $asarr($index,$val)} {
set asarr($index,3) 100
break
}
}
}
Related
I want to calculate avg of { energy_level ,number of nodes & traffic's data on nodes } by one mobile sink in the network with 5 static nodes.
I have to calculate this avg with proc in tcl script code not with awk code.
Please help me
If you have collected a list of values that you want to compute the average of, use this procedure to do the computation:
proc ArithmeticMean {listOfValues} {
set length [llength $listOfValues]
if {$length == 0} {
return 0.0
}
set sum [::tcl::mathop::+ {*}$listOfValues]
return [expr {double($sum) / $length}]
}
The summing of the values uses the + “operator command” with expansion syntax, and is the cheapest way of adding them all together.
# the unit of period is picosecond
set period 625000.0
set period_sec [format %3.6g [expr $period * 1e-12]]
puts $period_sec
result: 6.25e-07
Is there a way to force tcl to get results like 625e-09
Assuming that you want to format it to the nearest exponent, you could use a proc which formats it like this:
proc fix_sci {n} {
# Not a sci-fmt number with negative exponent
if {![string match "*e-*" $n]} {return $n}
# The set of exponents
set a 9
set b 12
# Grab the number (I called it 'front') and the exponent (called 'exp')
regexp -- {(-?[0-9.]+)e-0*([0-9]+)} $n - front exp
# Check which set of exponent is closer to the exponent of the number
if {[expr {abs($exp-$a)}] < [expr {abs($exp-$b)}]} {
# If it's the first, get the difference and adjust 'front'
set dif [expr {$exp-$a}]
set front [expr {$front/(10.0**$dif)}]
set exp $a
} else {
# If it's the first, get the difference and adjust 'front'
set dif [expr {$exp-$b}]
set front [expr {$front/(10.0**$dif)}]
set exp $b
}
# Return the formatted numbers, front in max 3 digits and exponent in 2 digits
return [format %3ge-%.2d $front $exp]
}
Note that your original code returns 6.25e-007 (3 digits in the exponent).
If you need to change the rule or rounding the exponent, you will have to change the if part (i.e. [expr {abs($exp-$a)}] < [expr {abs($exp-$b)}]). For example $exp >= $a could be used to format if the exponent is 9 or below.
ideone demo of above code for 'closest' exponent.
For Tcl versions before 8.5, use pow(10.0,$dif) instead of 10.0**$dif
I do not think there is anything in the format command that will help you directly. However, if you consider a slight variation on the format code, then it may be a lot easier to get what you want (with a bit of string manipulation):
format %#3.6g $number
gives a number like: 6.25000e-007
This can be parsed more easily:
Extract the exponent
Determine the number of positions to shift the decimal point
Shift it and replace the exponent
It is not entirely straightforward, I am afraid, but it should be doable. Wiki page http://wiki.tcl.tk/5000 may give you some inspiration.
I am confused about the binary scan of types "c", as it is said that "c" refers to 8 bit character code. I have the following code to be maintained
puts ""
set b_val2 "0000021002020a042845245d868a8d9900081b000315aef0010c105d39b4f7c9a083a65e7d000306140508063024"
set msg2 [binary format H* $b_val2]
set msg2 [string range $msg2 1 end]
while {[binary scan $msg2 cc id2 len2] == 2} {
puts ""
#puts "SECOND ID is $id2 and SECOND LENGTH is $len2"
set id2 [expr {$id2 & 0xff}]
set len2 [expr {$len2 & 0xff}]
set val2 [string range $msg2 2 [expr {1+$len2}]]
switch -exact -- $id2 {
0 {
puts ""
if {$val2 == "\x10\x04"} {
puts "val is found for 04 "
} elseif {$val2 == "\x10\x02"} {
puts "ID is found for 02! CORRECT "
} else {
puts "not supported"
}
}
}
}
the idea is to take the value of "10 02" from the given hex. and this code work just fine until I Change the given Input of b_val as
00021002020903e845245ca0d29858081c00031ef7c001106d3931e7d3414e6d3df26831030614051608045c22
for the first given hex code "len" is "3" and it parses the binary correctly, but for the second hex Input, the "len2" is 16, hence parsing the wrong bytes.
I read that the binary scan cc will give back two variable of type 8 bit character code, but the above failure does not make any sense to me at all, as what i understand that what is the previous author tries to aim with the above code (expecially the set val2 where it tries to take the range) and why it fails for the second input
For starters: your code snippet never modifies msg2 within the while loop, so the scan returns the same result every loop, and you have an infinite loop. I tossed a break in to only loop once, but that leaves me uncertain I have the right behavior.
That said, the obvious issue is that when you go from your original message to the replacement, you've dropped the first byte (value of 00). Starting with line 2 (ignoring blank lines), where you set
set b_val2 "0000021002020a..."
let's parse by hand. Line 3 converts it to hex, and line 4 drops the first byte so that we start with a string of bytes with hex values of \x00 \x02 \x10 \x02 \x02 \x0a .... The binary scan on line 5 sets id2 to the first byte and len2 to the second byte; line 10 sets val2 to a string with values \x10 \x02, which matches your criteria. Success.
Now reparse with input of
set b_val2 "00021002020903e84..."
from your second input line. Again, the first byte is DISCARDED on line 4, leaving you with \x02 \x10 \x02 \x02 \x09 \x03.... Line 5 sets id2 to 2 and len2 to \x10, or decimal 16, which is what you see. That means val2 is very different from what you expected, but that's due to you dropping a byte from your input.
Byte parsers are EXTREMELY sensitive to initial position in the string. Once you mess that up, you'd better have a robust resynchronization mechanism or it's all over bar the shouting. This is one major reason that wire protocols are difficult. :)
I want to use tcl to control simulation iteration.
In each iteration, I get some random value, and save the results
set set_of_double_values{0}
foreach y_val $set_of_double_values {
set_parameter Snr $y_val ;
set numsim 2;
set_value/$env(sim_name)/numsim $numsim;
for {set i 0} {$i < $numsim} {incr i 1} {
some random value
}
run_iteration;
}
run_iteration
using this code, I always run the inner part many times, and I can not save the right results...
I'm attempting to write a function in assembly that will detect if a longer binary number contains a smaller binary pattern.
Example:
Does 100111 contain 1001?
When I read this problem I figured that I would do a bitwise-AND with the large number and its smaller pattern while shifting right (logical) each time in a loop.
So, in my head I thought it would do:
100111 AND 1001 = 0
Shift-right 1
010011 AND 1001 = 0
Shift-right 1
001001 AND 1001 = 1 // Pattern FOUND!
and repeat this until either the number was shifted until it was zero or the AND returned 1.
However, I think I must have something confused because this is returning 1 for most things I put in, on the first run of the loop. Am I confused on my usage of AND?
The problem is that "partial matches" also return a non-zero value for your AND check:
100111 AND 001001 = 000001
So this tests if any of the bits match, but you want to make sure all bits are the same. The result of the AND needs to be equal to the pattern you are searching:
x = 100111
if (x AND 1001 == 1001)
print "found"
Bitwise AND does not work the way you expect (judging from the samples and ignoring the notation which seems to suggest you are using bitwise AND as the logical AND of bits). AND only takes the bits that are set to 1 "into account". E.g 1111 AND 1001 == 1001.
You need to use XOR and compare against 0 for match (remember the mask the bits you are not comparing from the result). In your example a match is found when (N ^ 1001) & 1111 == 0000
In order to make sure that both the 0 and 1 bits match your search pattern, you need to do something like this:
if ((InputPattern AND SearchMask) == SearchPattern)
{
// then match
}
The SearchMask should be all 1 bits, of a length equal to your SearchPattern. For example, you could have SearchMask == 1111, SearchPattern == 1001.
You should AND and then test against the search pattern:
if ((TestPattern & SearchPattern) == SearchPattern)
{
// then match
}
(where & represents bitwise AND)