Version 20 of Let's assign with let

Updated 2015-01-06 01:11:05 by aspect

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          ;#instead of assigning the list {1 2 3} to a b and c,
                             ;#it instead assigns the elements in order, resulting
                             ;#in a getting the value 1, b getting 2 and so on.
 let a b c @:= info commands ;#uses eval to get result and uses @= for assignment
 let a ++                    ;#incr and
 let a --                    ;#decr are supported.

My suggestion will swap a-b and vice versa:

  foreach {a b} [list $b $a] {}

Although proc let has many other features of course.


  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 ]
          foreach var $vars res $result {
            uplevel 1 [ list set $var $res ]
          }
        } else {
          foreach var $vars {
            set result [ uplevel [list set $var $result] ]
          }
        }
      }
      @ {
        if { "$optional" == ":" } {
          set rest [uplevel $rest]
        }
        set max [ llength $rest ]
        if { $max == 1 } {
          eval set rest $rest
          set max [ llength $rest ]
        }
        foreach var $vars res $rest {
          set result [ uplevel 1 [ list set $var $res ]]
        }
      }
      + - - - * - / - & - | { 
        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 [list 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...

Larry Smith I'd clean forgotten that feature. Yes, it's much more elegant that way. The above code has been corrected.


RFox See Larry McVoy's L language? extension? to Tcl from Tcl 2006:

http://www.tcl.tk/community/tcl2006/papers/Larry_McVoy/l.pdf


aspect 2015: For interactive use I like to have the following two definitions active (from .tclshrc):

    # let <varName> <cmd> <arg>....
    proc let {name args} {
        tailcall ::set $name [uplevel 1 $args]
    }
    interp alias {} = {} expr

The mostly saves me having to contort my fingers to write expr { and }. The specific combination using = as a pun for expr I find more Tclish than = as an always-present keyword argument.

    let pi = 4*atan(1)

Of course the resulting code is suboptimal and dangerous (Brace your expressions!), but it is a lot more fun to write. The only problem is that before publication I need to clean up, but that's a few editor macros away.

You will find some of the toys I paste to the wiki use this combination, where I feel readability trumps robustness.