TCL: Make code cleaner from nested if-statements -


this procedure of 1 of items of status bar on i3 window manager on linux. run every second. deals frequency governors. if temperature reaches number switch powersave mode, or if application running, e.g. steam, or laptop running on batteries. if temperature reaches lower point switch performance, etc.

the procedure runs far, no issues. code has many nested if-else statements, hard maintain , everytime add code becomes more, well.... nested.

proc cpu_freq {} {     set app steam     set cpu_power [exec sudo cpupower frequency-info | sed -ne /speed/p]     set cpu_temp [exec sensors | grep core | sed -n  {2p} | awk {{print $3}} | cut -c2-3]     set battery [exec acpi]     if {[string match *performance* $cpu_power]} {set cpu_freq high; set color "$::green"}     if {[string match *powersave* $cpu_power]}   {set cpu_freq low;  set color "$::red"}     if {![file isfile $::i3dir/powersave.freq] && ![file isfile $::i3dir/performance.freq]} {         set switch auto     }         # on battery      if {[string match *discharging* $battery]} {         # when in performance mode         if {[string match *performance* $cpu_power]} {            if {![file isfile $::i3dir/performance.freq]} {                # , not in manual                # switch powersave                 exec sudo cpupower frequency-set -g powersave                set cpu_freq low                set switch auto                set color "$::red"                set ::on_battery true           } else {                # switch manual performance mode               if {[file isfile $::i3dir/performance.freq]} {                   exec sudo cpupower frequency-set -g performance                   set cpu_freq high                   set switch man                   set color "$::green"                   set ::on_battery true               } else {              if {[file isfile $::i3dir/powersave.freq]} {                  # switch manual powersave mode                   exec sudo cpupower frequency-set -g powersave                  set cpu_freq low                  set switch man                  set color "$::red"                  set ::on_battery true               }               }                            }         } else {        # when in powersave mode (auto)        # switch manual powersave        if {[string match *powersave* $cpu_power]} {           if {[file isfile $::i3dir/powersave.freq]} {               exec sudo cpupower frequency-set -g powersave               set cpu_freq low               set switch man               set color "$::red"               set ::on_battery true           } else {        # switch manual performance           if {[file isfile $::i3dir/performance.freq]} {               exec sudo cpupower frequency-set -g performance               set cpu_freq high               set switch man               set color "$::green"               set ::on_battery true              }           }        }     }        # on mains     } else {          # when in powersave mode          if {[string match *powersave* $cpu_power]} {                 # running app or manual switch             if {[file isfile $::i3dir/powersave.freq]} {                 set cpu_freq low                 set switch man                 } else {             if {[isrunning $app]} {                 set cpu_freq low                 set switch auto                 # nothing, keep running in powersave mode                 } else {                 # switch performance after running on batteries                 if {$::on_battery==true} {                     exec sudo cpupower frequency-set -g performance                     set cpu_freq high                     set switch auto                     set color "$::green"                     set ::on_battery false                 # switch performance when reaching lower temps                 } elseif {$cpu_temp <= 55} {                     exec sudo cpupower frequency-set -g performance                     set cpu_freq high                     set switch auto                     set color "$::green"                    }                 }             }          # when in performance mode         } else {                 # manual switch             if {[file isfile $::i3dir/performance.freq]} {                 set switch man                 set cpu_freq high                 # nothing, keep running in performance mode                 } else {                 # hot temperature or running app                 # switch powersave                 if {$cpu_temp >= 75 || [isrunning $app] } {                     exec sudo cpupower frequency-set -g powersave                     set cpu_freq low                     set switch auto                     set color "$::red"                 } else {                     set cpu_freq high                     set switch auto                 }             }         }      }     set stdout {{"name":"cpu_freq","full_text":"$switch:$cpu_freq","color":"$color"}}     set stdout [subst -nocommands $stdout]     puts -nonewline $stdout } 

when see think finite state machines/state transition diagrams. have starting state , switch other states based on results of procs call in if statements, @ point reach end state no further transitions possible.

so i'd @ restructuring following example:

# value process set value "this big red ball"  # starting state set state 1  # state transtions , functions implement them set states [dict create "1,3" "isred" "1,2" "isblue" "2,4" "isbig" "2,5" "issmall" "3,4" "isbig" "3,5" "issmall"]  # procs implement state transitions proc isred {next} {     global value state     if {[string first "red" $value] != -1} {         puts "red"         set state $next         return true     }     return false }  proc isblue {next} {     global value state     if {[string first "blue" $value] != -1} {         puts "blue"         set state $next         return true     }     return false } proc issmall {next} {     global value state     if {[string first "small" $value] != -1} {         puts "small"         set state $next         return true     }     return false }  proc isbig {next} {     global value state     if {[string first "big" $value] != -1} {         puts "big"         set state $next         return true     }     return false }  # proc run state machine until state stops changing proc runmachine { states } {     global state     set startstate -1     while { $state != $startstate } {         set startstate $state         foreach key [dict keys $states "$state,*"] {             set next [lindex [split $key ","] 1]             set res [[dict $states $key] $next]             # if state changes no need more processing             if { $res == true } {                break              }         }     } }  runmachine $states 

this 1 possible approach , it's simpler need shows basic idea. dictionary shows allowed state transitions , proc run in order test if transition allowed. i've put processing code (the puts statement) in function simple have function processing, either called directly or held value in dictionary , called runmachine proc.

set states [dict create 21,3" [list "isred" "redaction"]] 

this approach lets seperate actions , transitions out , draw state transition diagram shows what's going on.

a quick google tcl finite state machine shows lots of other ways implement idea.


Comments