Created by [CecilWesterhof]. '''FizzBuzz: Simpe Program To Show Possibilities Of TCL''' I wrote a little program to implement FizzBuzz. Just a simple program to show how easy it is to write a program in tcl and how to split the program in small simple logical parts. FizzBuzz is a simple problem. You print a range with numbers, with the following rules: * When a number can be divided by 3 print Fizz * When a number can be divided by 5 print Buzz * When a number can be divided by both print FizzBuzz * When a number cannot be divided by these values, print the number I wanted the following extra's: * The program should be easily understandable * The program should be easily changable For example it should be easy to change the values to divide by, or the text used with a value. '''The main part is:''' ====== init for {set i ${startVal}} {${i} <= ${endVal}} {incr i} { puts [transformNumber ${i}] } ====== Initialise the program and then print the possible transformed numbers. '''Transforming the number:''' ====== proc transformNumber {number} { global fizzBuzzLst set output "" foreach {fizzBuzz} ${fizzBuzzLst} { lassign ${fizzBuzz} value string if {${number} % ${value} == 0} { append output ${string} } } if {${output} == ""} { set output ${number} } return ${output} } ====== For all values to use append the string to use when necessary. If the number is transformed return the transformation, otherwise return the number. '''Initialising:''' ====== proc init {} { global endVal global fizzBuzzLst global startVal set fizzBuzzLst [list \ [list 3 "Fizz"] \ [list 5 "Buzz"] \ ] set startVal 1 set endVal 100 readParams if {![string is integer -strict ${startVal}]} { giveError "startValue is not an integer" } if {![string is integer -strict ${endVal}]} { giveError "endValue is not an integer" } if {${endVal} < ${startVal}} { giveError "endValue < startValue" } } ====== Create the list with transformations and initialise the start and end value. Read the parameters. After this check the values. By checking the values in init and not readParams the default values are also checked. '''Reading of the Parameters:''' ====== proc readParams {} { global argc global argv global endVal global startVal if {(${argc} == 1) && ([lindex ${argv} 0 ] == "--help")} { puts [usageStr] exit } switch ${argc} { 0 {} 1 { set endVal [lindex ${argv} 0] } 2 { set startVal [lindex ${argv} 0] set endVal [lindex ${argv} 1] } default { giveError "WRONG ARGUMENTS\n[usageStr]" } } } ====== If the parameter is --help show the usage of the script. If there are no parameters use default values. If there is one parameter this is the end value. If there are two parameters these are the start and end values. Otherwise there is an error, signal this, give the usage and exit. '''Usage:''' ====== proc usageStr {} { set command [file tail ${::argv0}] return "USAGE: ${command} ${command} endValue ${command} startValue endValue ${command} --help Default: startValue = ${::startVal}, endValue = ${::endVal}" } ====== Just returns a string with the usage of the script. '''Give Error:''' ====== proc giveError {message {error 1}} { giveWarning ${message} exit ${error} } proc giveWarning {message} { puts stderr ${message} } ====== An error is a warning that exits the program. In this case warnings are not given, but I prefer to be prepared for this kind of situations. In my opinion errors and warnings should go to the same place. In this way there is not the risk that when warnings are added that they not go to the same place as the errors. ---- As always: comments, tips and questions are appreciated. ---- '''The complete program:''' ====== #!/usr/bin/env tclsh proc giveError {message {error 1}} { giveWarning ${message} exit ${error} } proc giveWarning {message} { puts stderr ${message} } proc init {} { global endVal global fizzBuzzLst global startVal set fizzBuzzLst [list \ [list 3 "Fizz"] \ [list 5 "Buzz"] \ ] set startVal 1 set endVal 100 readParams if {![string is integer -strict ${startVal}]} { giveError "startValue is not an integer" } if {![string is integer -strict ${endVal}]} { giveError "endValue is not an integer" } if {${endVal} < ${startVal}} { giveError "endValue < startValue" } } proc readParams {} { global argc global argv global endVal global startVal if {(${argc} == 1) && ([lindex ${argv} 0 ] == "--help")} { puts [usageStr] exit } switch ${argc} { 0 {} 1 { set endVal [lindex ${argv} 0] } 2 { set startVal [lindex ${argv} 0] set endVal [lindex ${argv} 1] } default { giveError "WRONG ARGUMENTS\n[usageStr]" } } } proc transformNumber {number} { global fizzBuzzLst set output "" foreach {fizzBuzz} ${fizzBuzzLst} { lassign ${fizzBuzz} value string if {${number} % ${value} == 0} { append output ${string} } } if {${output} == ""} { set output ${number} } return ${output} } proc usageStr {} { set command [file tail ${::argv0}] return "USAGE: ${command} ${command} endValue ${command} startValue endValue ${command} --help Default: startValue = ${::startVal}, endValue = ${::endVal}" } init for {set i ${startVal}} {${i} <= ${endVal}} {incr i} { puts [transformNumber ${i}] } ====== <>Example