TCL:通过嵌套的if语句使代码更清晰

时间:2015-06-03 05:06:22

标签: if-statement tcl

这是linux上i3窗口管理器上状态栏的一个项目的过程。每秒运行一次。基本上与频率调节器有关。如果温度达到一定数量,则切换到powersave模式,或者如果某个应用程序正在运行,例如蒸汽,或笔记本电脑使用电池运行。如果温度达到较低点,则会切换到性能等。

到目前为止,程序运行良好,没有问题。但是代码有很多嵌套的if-else语句,很难维护,每次我添加一些代码变得更多,好......嵌套。

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]} {
               # AND NOT IN MANUAL
               # SWITCH TO POWERSAVE 
               exec sudo cpupower frequency-set -g powersave
               set cpu_freq LOW
               set switch AUTO
               set color "$::red"
               set ::on_battery true
          } else { 
              # SWITCH TO 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 TO 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 TO 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 TO 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
                # DO NOTHING, KEEP RUNNING IN POWERSAVE MODE
                } else {
                # SWITCH TO 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 TO 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
                # DO NOTHING, KEEP RUNNING IN PERFORMANCE MODE
                } else {
                # HOT TEMPERATURE OR RUNNING APP
                # SWITCH TO 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
}

3 个答案:

答案 0 :(得分:2)

将其分解为一组函数。

Tcl有switch声明,有时可以提供帮助。它还有elseif来帮助减少嵌套。但是在显示的代码中,将其分解为具有合理名称的函数,您可以将其简化为处理逻辑的函数和处理特定情况下发生的事件的集合。

答案 1 :(得分:1)

按照patthoyts的建议将代码分解为单独的函数是一个很好的解决方案,但可能会有些慢(但是你不太可能注意到)。使代码更易于使用的另一个解决方案是在启动期间动态创建cpu_freq

要做到这一点,请编写一个冗长且具有所需文档的脚本,这样可以为cpu_freq生成所需的简洁高效的主体。当您需要扩展它时,您只需向脚本添加更多部分。使用生成的主体作为第三个参数调用proc,并在第一次调用时编译它。

答案 2 :(得分:1)

当我看到这样的东西时,我立即想到有限状态机/状态转换图。你有一个起始状态,然后你根据你在if语句中调用的procs的结果切换到其他状态,在某些时候你达到了一个结束状态,从而无法进一步转换。

所以我会考虑重组到类似下面的例子:

# The value to process
set value "This is a big red ball"

# The starting state
set state 1

# The state transtions and the functions to 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 that implement the 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 to run the state machine until the 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 get $states $key] $next]
            # If the state changes then no need to do any more processing
            if { $res == true } {
               break 
            }
        }
    }
}

runMachine $states

这是一种可行的方法,它比你需要做的简单得多,但显示了基本的想法。字典显示允许的状态转换和要运行的proc,以测试是否允许转换。我已将处理代码(puts语句)放在此函数中,但是让另一个函数执行处理会很简单,可以直接调用,也可以作为字典中的另一个值保存,并从runMachine proc调用。

set states [dict create 21,3" [list "IsRed" "RedAction"]]

这种方法可让您分离所有操作和转换,并绘制一个状态转换图,清楚地显示正在发生的事情。

快速谷歌的TCL有限状态机显示了许多其他方式来实现这个想法。