Fizz Buzz is a programming interview problem and exercise derived from a game meant to teach children arithmetic.
CecilWesterhof 2018-05-28: 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:
I wanted the following extras:
For example it should be easy to change the values to divide by, or the text used with a value.
init for {set i $startVal} {$i <= $endVal} {incr i} { puts [transformNumber $i] }
Initialise the program and then print the possible transformed numbers.
proc transformNumber {number} { global fizzBuzz set output "" dict for {value string} $fizzBuzz { 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.
proc init {} { global endVal global fizzBuzz global startVal set fizzBuzz [dict create 3 Fizz 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.
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.
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.
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.
#!/usr/bin/env tclsh proc giveError {message {error 1}} { giveWarning $message exit $error } proc giveWarning {message} { puts stderr $message } proc init {} { global endVal global fizzBuzz global startVal set fizzBuzz [dict create 3 Fizz 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 fizzBuzz set output "" dict for {value string} $fizzBuzz { if {$number % $value == 0} { append output $string } } if {$output eq ""} { 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] }
dbohdan 2018-04-01: The canonical way to solve Fizz Buzz in Tcl is this:
for {set i 1} {$i <= 100} {incr i} { switch -glob [expr {$i % 3}]_[expr {$i % 5}] { 0_0 { puts {Fizz Buzz} } 0_* { puts Fizz } *_0 { puts Buzz } *_* { puts $i } } }
sebres 2019-07-31: as single-liner using dict getdef (tcl >= 8.7):
for {set i 1} {$i <= 100} {incr i} { puts [dict getdef {0 "Fizz Buzz" 1 Fizz 2 Buzz} [expr {bool($i % 3)<<1 | bool($i % 5)}] $i] }