if 0 {[Richard Suchenwirth] 2005-03-22 - Rational numbers, a.k.a. fractions (see [Fraction math]), can be thought of as pairs of integers {numerator denominator}, such that their "real" numerical value is ''numerator/denominator'' (and not in integer nor "double" division!). They can be more precise than any "float" or "double" numbers on computers, as those can't exactly represent any fractions whose denominator isn't a power of 2 - consider 1/3 which can not at any precision be exactly represented as floating-point number to base 2, nor as decimal fraction (base 10), even if bignum. Reading in [SICP] once more, I wanted to play with rationals in [Tcl] again - so here's another evening fun project. An obvious string representation of a rational is of course "n/d". The following "constructor" does that, plus it normalizes the signs, reduces to lowest terms, and returns just the integer n if d==1:} proc rat {n d} { if {!$d} {error "denominator can't be 0"} if {$d<0} {set n [- $n]; set d [- $d]} set g [gcd $n $d] set n [/ $n $g] set d [/ $d $g] expr {$d==1? $n: "$n/$d" } } if 0 {Conversely, this "deconstructor" splits zero or more rational or integer strings into num and den variables, such that [[ratsplit 1/3 a b]] assigns 1 to a and 3 to b:} proc ratsplit args { foreach {r _n _d} $args { upvar 1 $_n n $_d d foreach {n d} [split $r /] break if {$d eq ""} {set d 1} } } #-- Four-species math on "rats": proc rat+ {r s} { ratsplit $r a b $s c d rat [+ [* $a $d] [* $c $b]] [* $b $d] } proc rat- {r s} { ratsplit $r a b $s c d rat [- [* $a $d] [* $c $b]] [* $b $d] } proc rat* {r s} { ratsplit $r a b $s c d rat [* $a $c] [* $b $d] } proc rat/ {r s} { ratsplit $r a b $s c d rat [* $a $d] [* $b $c] } if 0 { Arithmetical helper functions can be wrapped with [func] if they only consist of one call of [expr]:} proc func {name argl body} {proc $name $argl [list expr $body]} #-- [Greatest common denominator]: func gcd {u v} {$u? [gcd [% $v $u] $u]: abs($v)} #-- Binary [expr] operators exported: foreach op {+ * / %} {func $op {a b} \$a$op\$b} #-- "-" can have 1 or 2 operands: func - {a {b ""}} {$b eq ""? -$a: $a-$b} #-- a little tester reports the unexpected: proc ? {cmd expected} { catch {uplevel 1 $cmd} res if {$res ne $expected} {puts "$cmd -> $res, expected $expected"} } #-- The test suite should silently pass when this file is [source]d: ? {rat 42 6} 7 ? {rat 1 -2} -1/2 ? {rat -1 -2} 1/2 ? {rat 1 0} "denominator can't be 0" ? {rat+ 1/3 1/3} 2/3 ? {rat+ 1/2 1/2} 1 ? {rat+ 1/2 1/3} 5/6 ? {rat+ 1 1/2} 3/2 ? {rat- 1/2 1/8} 3/8 ? {rat- 1/2 1/-8} 5/8 ? {rat- 1/7 1/7} 0 ? {rat* 1/2 1/2} 1/4 ? {rat/ 1/4 1/4} 1 ? {rat/ 4 -6} -2/3 if 0 { See also [Fraction Math] ---- [Arts and crafts of Tcl-Tk programming] [Category Mathematics] }