TCL:从嵌套的 if 语句中使代码更清晰
TCL: Make code cleaner from nested if-statements
这是 i3 window 管理器 linux 上状态栏项目之一的程序。每秒是运行。基本上涉及频率调节器。如果温度达到一定数值,则切换到省电模式,或者如果某个应用程序正在 运行ning,例如steam,或者笔记本电脑 运行 正在使用电池。如果温度达到较低点,那么它会切换到性能等。
程序 运行 目前为止非常好,没有问题。然而,代码有太多嵌套的 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 }} | 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
}
将其分解为一组函数。
Tcl 有一个 switch
语句,有时可以提供帮助。它还具有 elseif
以帮助减少嵌套。但在显示的代码中,将其分解为具有合理名称的函数,您可以将其简化为一个处理逻辑的函数和一个处理特定情况下发生的事情的集合。
按照 patthoyts 的建议将代码分解为单独的函数是一个很好的解决方案,但可能会稍微慢一些(但是您不太可能会注意到)。另一种使代码更易于使用的解决方案是在启动期间动态创建 cpu_freq
。
为此,请编写一个尽可能冗长且包含尽可能多的文档的脚本,从而生成您想要 cpu_freq
的简洁高效的正文。当您需要扩展它时,只需向脚本中添加更多部分即可。将生成的主体作为第三个参数调用 proc
,它将在第一次调用时编译。
当我看到这样的东西时,我会立即想到有限状态 machines/state 转换图。你有一个起始状态,然后根据你在 if 语句中调用的过程的结果切换到其他状态,在某个时候你到达一个结束状态,从那里不能进一步转换。
所以我会考虑重组为如下示例:
# 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
这是一种可能的方法,它比您需要做的要简单得多,但展示了基本思想。字典显示了允许的状态转换和到 运行 的过程,以测试是否允许转换。我已经将我的处理代码(puts 语句)放在这个函数中,但是让另一个函数进行处理会很简单,直接调用或作为字典中的另一个值保存并从 运行Machine proc 调用.
set states [dict create 21,3" [list "IsRed" "RedAction"]]
这种方法可以让您分离出所有的动作和转换,并绘制一个状态转换图,清楚地显示正在发生的事情。
TCL 有限状态机的快速 google 显示了实现此想法的许多其他方法。
这是 i3 window 管理器 linux 上状态栏项目之一的程序。每秒是运行。基本上涉及频率调节器。如果温度达到一定数值,则切换到省电模式,或者如果某个应用程序正在 运行ning,例如steam,或者笔记本电脑 运行 正在使用电池。如果温度达到较低点,那么它会切换到性能等。
程序 运行 目前为止非常好,没有问题。然而,代码有太多嵌套的 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 }} | 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
}
将其分解为一组函数。
Tcl 有一个 switch
语句,有时可以提供帮助。它还具有 elseif
以帮助减少嵌套。但在显示的代码中,将其分解为具有合理名称的函数,您可以将其简化为一个处理逻辑的函数和一个处理特定情况下发生的事情的集合。
按照 patthoyts 的建议将代码分解为单独的函数是一个很好的解决方案,但可能会稍微慢一些(但是您不太可能会注意到)。另一种使代码更易于使用的解决方案是在启动期间动态创建 cpu_freq
。
为此,请编写一个尽可能冗长且包含尽可能多的文档的脚本,从而生成您想要 cpu_freq
的简洁高效的正文。当您需要扩展它时,只需向脚本中添加更多部分即可。将生成的主体作为第三个参数调用 proc
,它将在第一次调用时编译。
当我看到这样的东西时,我会立即想到有限状态 machines/state 转换图。你有一个起始状态,然后根据你在 if 语句中调用的过程的结果切换到其他状态,在某个时候你到达一个结束状态,从那里不能进一步转换。
所以我会考虑重组为如下示例:
# 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
这是一种可能的方法,它比您需要做的要简单得多,但展示了基本思想。字典显示了允许的状态转换和到 运行 的过程,以测试是否允许转换。我已经将我的处理代码(puts 语句)放在这个函数中,但是让另一个函数进行处理会很简单,直接调用或作为字典中的另一个值保存并从 运行Machine proc 调用.
set states [dict create 21,3" [list "IsRed" "RedAction"]]
这种方法可以让您分离出所有的动作和转换,并绘制一个状态转换图,清楚地显示正在发生的事情。
TCL 有限状态机的快速 google 显示了实现此想法的许多其他方法。