Version 5 of Let's assign with let

Updated 2001-12-06 18:38:11

Larry Smith posted in the comp.lang.tcl newsgroup:

Is it possible to assign to a $b �s value and to b $a �s value without using a third variable? Is there a function to do so?

Why not just:

 let a b @= $b $a

Which, by the way, also allows:

 let a b c = 1              ;#  this sets a b and c to 1
 let a b c = 1 + 4          ;# "=" uses expr to process the value to assign
 let a b c += 1             ;# computed assignments are allowed, +-*/&| supported
 let a b c := info commands ;# uses eval to process right side
 let a b c @= 1 2 3         ;# "hoisting" assignment, foreach replacement
 let a b c @:= info commands;# uses eval and hoists for assignment
 let a ++                   ;# incr and
 let a --                   ;#   decr are supported.

 proc let { args } {
  if { [llength $args ] == 2 } {
    if [string equal [ lindex $args 1 ] "++" ] {
      set result [ uplevel incr [ lindex $args 0 ] ]
    } elseif [string equal [ lindex $args 1 ] "--" ] {
      set result [ uplevel incr [ lindex $args 0 ] -1 ]
    } else {
      set result [ uplevel set "$args" ]
    }
  } else {
    regexp {([^=:+\-*/&|@]*)([:+\-*/&|@]?)([@]*)=(.*)} $args -> vars op optional rest
    if ![ info exists op ] {
      return -code error -errorcode 1 "no valid assignment operator in $args"
    }
    switch -- $op {
      : {
        if [llength [info commands [lindex $rest 0]]] {
          set result [uplevel $rest]
        } else {
          set result $rest                ;# this should always work...
        }
        if { "$optional" == "@" } {
          set max [ llength $result ]
          set i 0
          foreach var $vars {
            uplevel set $var [ lindex $result $i ]
            incr i
          }
        } else {
          foreach var $vars {
            set result [ uplevel set $var \"$result\" ]
          }
        }
      }
      @ {
        if { "$optional" == ":" } {
          set rest [uplevel $rest]
        }
        set max [ llength $rest ]
        if { $max == 1 } {
          eval set rest $rest
          set max [ llength $rest ]
        }
        set i 0
        foreach var $vars {
          set result [ uplevel set $var [ lindex $rest $i ] ]
          incr i
        }
      }
      + - - - * - / - & - | { 
        foreach var $vars {
          set result [ uplevel set $var \[ expr \$$var $op ( $rest ) \]]
        }
      }
      = -
      default {
        if { [ catch { set result [ uplevel expr $rest ] } ] } {
          set result $rest              ;# this should always work...
        }
        foreach var $vars {
          set result [ uplevel set $var \"$result\" ]
        }
      }
    }
  }
  return $result
 }

RS The code fragment (appears twice above)

          set i 0
          foreach var $vars {
            uplevel set $var [ lindex $result $i ]
            incr i
          }

seems like it can be replaced by

         foreach var $vars res $result {
            uplevel 1 [list set $var $res]
         }

- if foreach can do a job once, it might as well do it twice ;-) A variable saved, and possibly safer with the list wrapper...