Please suggest improvements to my program. This program gives a 4 bit binary incremental o/p
I am looking for optimizing this where there is unnecessary code.
Please suggest improvements to my program. This program gives a 4 bit binary incremental o/p
I am looking for optimizing this where there is unnecessary code.
Please suggest improvements to my program. This program gives a 4 bit binary incremental o/p
I am looking for optimizing this where there is unnecessary code.
#!/bin/sh
# This
puts "+++++++++++++++++\n"
set ipaddr "0.0.0.0"
set limit 4
set splitip [split $ipaddr "."]
puts "Split ip address = $splitip"
# MAIN ROUTINE
set ilength [llength $splitip]
puts "Length of string is $ilength"
set first [lindex $splitip 0]
set sec [lindex $splitip 1]
set third [lindex $splitip 2]
set four [lindex $splitip 3]
for { set limit 1} { $limit >0} {} {
for { set first $first } { $first <= $limit} {} {
for { set sec $sec } { $sec <= $limit} {} {
for { set third $third } { $third <= $limit} {} {
for { set four $four } { $four <= $limit} {} {
puts " f:$first $sec $third $four"
incr four
}
set four 0
incr third; #puts " t:$four $third $sec $first\n"
}
set third 0
incr sec
}
#puts " f:$four $third $sec $first"
set sec 0
incr first
}
incr limit -1
}
# End Main
puts "\n++++++End Program+++++++++++"
Your program essentially boils down to this, does this do what you intended?
for { set first 0 } { $first <= 1} {incr first} {
for { set sec 0 } { $sec <= 1} {incr sec} {
for { set third 0 } { $third <= 1} {incr third} {
for { set four 0 } { $four <= 1} {incr four} {
puts " f:$first $sec $third $four"
}
}
}
}
Because if so, the primary suggestion is to simply remove everything except this.
Also: [llength $splitip] does not give you the string length of $splitip, but the list length. Those are different.
You're using a very roundabout way to assign values to first et al. Instead, use
lassign $splitip first sec third four
The lassign was added in Tcl 8.5. If you're using an older version of Tcl, use assignment by foreach instead:
foreach {first sec third four} $splitip break
The construct
for { set limit 1} { $limit >0} {incr limit -1} { ... }
simply means "execute ... exactly once": it doesn't affect the program's execution in any way. Even if you remove it (keeping the code inside the body argument), the code that was inside it will still execute exactly once.
For clarity, the incr x invocations should be inside the third argument to for, not inside the fourth, body, argument.
As a final note, if your intent is to print out a sequence of binary numbers, it's a lot easier to do that this way:
for {set i 0} {$i < 16} {incr i} { puts [format %04b $i] }
Related
I'm trying to make a "safe" method of generating request ids for web sockets (just a desktop app not a real server) and want each socket to have its own id generator. All I'm doing is generating ids and recycling them after the request completes, such that the id doesn't grow unlimited throughout a user's session. I used an example concerning closures for a counter in JavaScript from David Flanagan's book and all seems to work well in Tcl but I'd greatly appreciate any advice on how to do this correctly and how I can test that these variables cannot be altered by the main program apart from calling one of the procedures within the namespaces. For example, is it possible to modify the gap list under the WEBS::$sock from the global namespace with [meant without] calling one of the procedures? Thank you.
Also, is there any difference between declaring namespace eval WEBS {} outside proc. ReqIdGenerator and using namespace eval WEBS::$sock inside the procedure? I can see that the results are the same for my little tests but wondered if there was any differences otherwise.
As an aside, in JS using the push and pop methods of arrays, it seems easier to recycle ids on a last-in-first-out basis; but using Tcl lists, it seems easier to use a first-in-first-out basis because using lassign with one variable assigns index 0 to the variable and returns the remaining elements as a new list. The equivalent of array.pop() seems to require more steps. Is that a correct observation? Thank you.
WARNING:
There is an error in this code in that the namespace references $sock and it works only because it is a global variable. If it were not global, the code would throw and error. The best I could find thus far is in this question.
proc ReqIdGenerator {sock} {
namespace eval WEBS {
namespace eval $sock {
variable max 0
variable gap {}
variable open {}
variable sock $sock
proc getId {} {
variable max
variable gap
variable open
if { [llength $gap] > 0 } {
set gap [lassign $gap id]
lappend open $id
return $id
} else {
lappend open [set id [incr max]]
return $id
}
chan puts stdout "Error in getId"
return -1
}
proc delId {id} {
variable max
variable gap
variable open
if { [set i [lsearch $open $id]] == -1 } {
return 1
} elseif { [llength $open] == 1 } {
reset
} else {
lappend gap [lindex $open $i]
set open [lreplace $open $i $i]
}
return 0
}
proc reset {} {
variable max 0
variable gap {}
variable open {}
}
proc getState {{prop "all"}} {
variable max
variable gap
variable open
variable sock
if { $prop eq "all" } {
return [list $max $gap $open]
} elseif { $prop eq "text" } {
return "State of socket $sock: max: $max; gap: $gap; open: $open"
} else {
return [set $prop]
}
}
}
}
}
set sock 123
ReqIdGenerator $sock
set sock 456
ReqIdGenerator $sock
# Add ids 1 through 10 to both sockets
for {set i 0} {$i<10} {incr i} {
WEBS::123::getId
WEBS::${sock}::getId
}
# Delete even ids from socket 456
for {set i 2 } {$i<11} {incr i 2} {
WEBS::${sock}::delId $i
}
# Delete odd ids from socket 123
for {set i 1 } {$i<10} {incr i 2} {
WEBS::123::delId $i
}
chan puts stdout [WEBS::123::getState text]
# => State of socket 123: max: 10; gap: 1 3 5 7 9; open: 2 4 6 8 10
chan puts stdout [WEBS::456::getState text]
# => State of socket 456: max: 10; gap: 2 4 6 8 10; open: 1 3 5 7 9
Lots of questions to unpack here.
how I can test that these variables cannot be altered by the main program apart from calling one of the procedures within the namespaces
You can't. There are no access controls within an interpreter. You can have multiple interpreters and there are strong access controls between them, but that's pretty heavyweight. However, it's conventional to not go rummaging around in a namespace that you don't own to peek at things you've not formally been told about on the grounds that they're liable to be changed at any moment without any sort of notification to you (usually not at runtime, but no guarantees!).
A phrase I've seen used in the community is "If you break it, you get to keep all the pieces".
For example, is it possible to modify the gap list under the WEBS::$sock from the global namespace with calling one of the procedures?
I'm sure it is. Finding it might be tricky, but once you have the name you can change it.
is there any difference between declaring namespace eval WEBS {} outside proc. ReqIdGenerator and using namespace eval WEBS::$sock inside the procedure?
There, assuming you handle the possible differences in name resolution scope of the name of the namespace itself. (That doesn't matter for fully qualified names — names beginning with :: — but relative names might resolve differently.)
The equivalent of array.pop() seems to require more steps. Is that a correct observation?
Yes. 8.7 adds lpop to address this weakness.
Your code appears to be reinventing objects. Use TclOO (or one of the other major object systems such as [incr Tcl] or XOTcl) for that; it's better at the job.
oo::class create ReqIdGenerator {
variable max gap open sock
constructor {sock} {
set max 0
set gap {}
set open {}
set [my varname sock] $sock; # messy because formal parameter
}
method getId {} {
if { [llength $gap] > 0 } {
set gap [lassign $gap id]
lappend open $id
return $id
} else {
lappend open [set id [incr max]]
return $id
}
chan puts stdout "Error in getId"
return -1
}
method delId {id} {
if { [set i [lsearch $open $id]] == -1 } {
return 1
} elseif { [llength $open] == 1 } {
my reset
} else {
lappend gap [lindex $open $i]
set open [lreplace $open $i $i]
}
return 0
}
method reset {} {
set max 0
set gap {}
set open {}
}
method getState {{prop "all"}} {
if { $prop eq "all" } {
return [list $max $gap $open]
} elseif { $prop eq "text" } {
return "State of socket $sock: max: $max; gap: $gap; open: $open"
} else {
return [set [my varname $prop]]
}
}
}
set sock 123
set s1 [ReqIdGenerator new $sock]
set sock 456
set s2 [ReqIdGenerator new $sock]
# Add ids 1 through 10 to both sockets
for {set i 0} {$i<10} {incr i} {
$s1 getId
$s2 getId
}
# Etc.
I'm trying to learn tcl scripting. My req is very simple. I need to access the array "args" in the second if condition in the for loop. I tried the code below. Since "argv" scope is limited to second if condition, it is NOT accessible in for loop
Then I tried declaring argv as global var -
array set args {}
right below the ned of first if condition. Even after declaring "args" as global array did NOT help.
How do I access the variable in the cope of second if contion, in the for loop below ?
if {$argc != 4} {
puts "Insufficient arguments"
exit 1
}
if { $::argc > 0 } {
set i 1
foreach arg $::argv {
puts "argument $i is $arg"
set args(i) arg
incr i
}
} else {
puts "no command line argument passed"
}
for {set x 0} { $x<2 } {incr x} {
puts "Arrray: [lindex $args $x]"
}
For your original code, this is the error I get:
can't read "args": variable is array
while executing
"lindex $args $x"
("for" body line 2)
invoked from within
"for {set x 0} { $x<2 } {incr x} {
puts "Arrray: [lindex $args $x]"
}"
(file "main.tcl" line 20)
In Tcl, arrays are not lists. You have to write
for {set x 0} { $x<2 } {incr x} {
puts "Arrray: $args($x)"
}
But then I get this:
can't read "args(0)": no such element in array
while executing
"puts "Arrray: $args($x)""
("for" body line 2)
invoked from within
"for {set x 0} { $x<2 } {incr x} {
puts "Arrray: $args($x)"
}"
(file "main.tcl" line 20)
Well there's several problems here. You're setting array elements starting with index 1 but reading them starting with index 0. So let's correct that to 0 everywhere:
set i 0
But also you're missing some $'s in the setting of the elements:
set args($i) $arg
That looks better. Final code:
if {$argc != 4} {
puts "Insufficient arguments"
exit 1
}
if { $::argc > 0 } {
set i 0
foreach arg $::argv {
puts "argument $i is $arg"
set args($i) $arg
incr i
}
} else {
puts "no command line argument passed"
}
for {set x 0} { $x<2 } {incr x} {
puts "Arrray: $args($x)"
}
So, scope wasn't quite the issue. You're getting there though!
Tcl does not import globals by default. You need to import your globals:
global args
set args(i) arg
Some people prefer to import globals at the top of the proc:
global args
if {$argc != 4} {
puts "Insufficient arguments"
exit 1
}
if { $::argc > 0 } {
set i 1
....
See: https://www.tcl.tk/man/tcl8.7/TclCmd/global.htm
Alternatively, you can directly access the global namespace, in fact you're already using that syntax with ::argc:
set ::args(i) arg
Brain Teaser: I self originated this question, but stuck completely.
I want to create all possible combination of all characters, but of all possible lengths. Suppose, [a-z] combination of 1 length, then [a-z] combination of 2 length, and so on till the maximum length achieved.
this could be very easily done by iterative looping.
Example for 3 length:
proc triples list {
foreach i $list {
foreach j $list {
foreach k $list {
puts [list $i $j $k]
}
}
}
}
But, it should solve using less loops (looping needs to be dynamic)
set chars "abcdefghijklmnopqrstuvwxyz"
set chars [split $chars ""]
set complete_length [llength $chars]
set start 0
set maximum_length 15
while {1} {
if {$start > $maximum_length} {
break
}
for {set i [expr $maximum_length-$start]} {$i >= 0} {incr i -1} {
# dump combinations
}
incr start
}
In this chunk, what algorithm or method i should apply? Any kind of suggestions/help/code will be appreciated.
Sry, this is not an answer, but hopefully some interesting discussion anyway:
The word "combinations" is often used way too generally, so it can be interpreted in many different ways. Let's say that you have a source list of 26 different elements, the english letters, and you want to pick 3 of them and combine in a 3 element destination list:
Can you always pick any letter from the source list, or do the elements disappear from it as you pick them? Either define "pick" (are the elements copied or moved during a pick), or define the set of source values (is there 1 of each of A-Z or an infinite amount of A-Z).
Does the order in the destination list matter? Is AHM considered to be the same combination as HAM? Define "combine".
If you have a list where not all elements are different, e.g. {2 10 10 64 100}, you have even more possibilities. Define your set of values.
Your first example prints permutations, not combinations. If that's what you want, the easiset way is a recursive procedure. Combinations are more complicated to generate.
EDIT:
I wrote this procedure for a Project Euler program. It picks all the elements, but maybe you can modify it to pick n. It takes a command prefix as argument, so you don't have to store all permutations.
package require Tcl 8.5.0
proc forEachPerm {list cmdPrefix} {
_forEachPerm {} $list $cmdPrefix
}
proc _forEachPerm {head list cmdPrefix} {
if {![llength $list]} {
{*}$cmdPrefix $head
} else {
for {set i 0} {$i < [llength $list]} {incr i} {
_forEachPerm [concat $head [lrange $list $i $i]] [lreplace $list $i $i] $cmdPrefix
}
}
}
# example use:
forEachPerm {a b c} {apply {{list} {puts [join $list]}}}
For example, in Perl, to get a sequential array of numbers from 1 to 10, you could simply do:
#myArray = (1 .. 10);
The two periods serve as shorthand for this operations instead of making a for loop or writing the whole thing out manually. Other languages I've used have something similar also.
Does a similar shorthand exist in Tcl?
You can define the method:
proc fillArray {a b} {
eval return \[list $a [string repeat "\[incr a\] " [incr b -$a]]\]
}
And use it as:
set myArray [fillArray 1 10]
You even can beautify the call of procedure to make it look as in perl. For that just redefine unknown procedure:
rename unknown __unknown
proc unknown {args} {
if {[llength $args] == 3} {
lassign $args a op b
if {[string is integer $a] && $op == ".." && [string is integer $b]} {
return [fillArray $a $b]
}
}
return [uplevel __unknown {*}$args]
}
After that you can write just simple as:
set myArray [1 .. 10]
:)
Not quite this one, but
% package require struct::list
1.6.1
% struct::list iota 10
0 1 2 3 4 5 6 7 8 9
Also search this for the "iota" keyword to see how this can be done using a one-liner.
With the exception of expressions (which are their own little language) Tcl has no operators and is always a strictly prefix-driven language. This means that there isn't such a convenient shorthand for doing loops. On the other hand, there's nothing particularly special about Tcl's standard commands (apart from some minor efficiency details that don't matter here) so making your own is no problem:
proc .. {from to} {
if {$from >= $to} {
for {set i $from} {$i <= $to} {incr i} {lappend out $i}
} else {
for {set i $from} {$i >= $to} {incr i -1} {lappend out $i}
}
return $out
}
puts [.. 1 10]; # --> “1 2 3 4 5 6 7 8 9 10”
You can fake infix operators by using an unknown handler (as in GrAnd's answer) but that's really quite slow by comparison with the above.
No, a similar shorthand does not exist in tcl.
If you really want shorthand, you can create your own command that looks almost the same. For example:
proc : {start ignore end} {
set result []
for {set i $start} {$i <= $end} {incr i} {
lappend result $i
}
return $result
}
puts "from 1 to 10: [: 1 .. 10]"
Am attempting to teach myself to program using Tcl. (I want to become more familiar with the language to understand someone else's code - SCID chess)
The task i've set myself to motivate my learing of Tcl is to solve the 8 queens problem.
My approach to creating a program is to sucessively 'prototype' a solution.
So.
I'm up to nesting a for loop holding the q pos on row 2
inside the for loop holding the q pos on row 1
Here is my code
set allowd 1
set notallowd 0
for {set r1p 1} {$r1p <= 8} {incr r1p } {
puts "1st row q placed at $r1p"
;# re-initialize r2 'free for q placemnt' array after every change of r1 q pos:
for {set i 1 } {$i <= 8} {incr i} { set r2($i) $allowd }
for { set r2($r1p) $notallowd ; set r2([eval $r1p-1]) $notallowd ;
set r2([eval $r1p+1]) $notallowd ; set r2p 1} {$r2p <= 8} {
incr r2p ;# end of 'next' arg of r2 forloop
}
;# commnd arg of r2 forloop placed below:
{puts "2nd row q placed at $r2p"
}
}
My problem is that when i run the code the interpreter is aborting with the fatal error:
"wrong #args should be for start test next command.
I've gone over my code a few times and can't see that i've missed any of the for loop arguments.
The carriage return before the command in the last for loop is what's getting you. From the first syntax rule on the Tcl man page, "Semi-colons and newlines are command separators unless quoted as described below." BTW, your eval's should be expr's.
This works for me:
set allowd 1
set notallowd 0
for {set r1p 1} {$r1p <= 8} {incr r1p } {
puts "1st row q placed at $r1p"
;# re-initialize r2 'free for q placemnt' array after every change of r1 q pos:
for {set i 1 } {$i <= 8} {incr i} { set r2($i) $allowd }
for { set r2($r1p) $notallowd ; set r2([expr $r1p-1]) $notallowd ;
set r2([expr $r1p+1]) $notallowd ; set r2p 1} {$r2p <= 8} {
incr r2p ;# end of 'next' arg of r2 forloop
} {
# commnd arg of r2 forloop placed below:
puts "2nd row q placed at $r2p"
}
}