Price fraction: Difference between revisions
(→{{header|Python}}: Exact routine) |
(→Tcl: Added implementation) |
||
Line 165: | Line 165: | ||
return _cout[ bisect.bisect_right(_cin, centsin) ]</lang> |
return _cout[ bisect.bisect_right(_cin, centsin) ]</lang> |
||
Other options are to use the fractions or decimals modules for calculating money to a known precision. |
Other options are to use the fractions or decimals modules for calculating money to a known precision. |
||
=={{header|Tcl}}== |
|||
Structured as two functions, one to parse the input data as described in the problem into a form which Tcl can work with easily, and the other to perform the mapping. |
|||
<lang tcl># Used once to turn the table into a "nice" form |
|||
proc parseTable table { |
|||
set map {} |
|||
set LINE_RE {^ *>= *([0-9.]+) *< *([0-9.]+) *:= *([0-9.]+) *$} |
|||
foreach line [split $table \n] { |
|||
if {[string trim $line] eq ""} continue |
|||
if {[regexp $LINE_RE $line -> min max target]} { |
|||
lappend map $min $max $target |
|||
} else { |
|||
error "invalid table format: $line" |
|||
} |
|||
} |
|||
return $map |
|||
} |
|||
# How to apply the "nice" table to a particular value |
|||
proc priceFraction {map value} { |
|||
foreach {minimum maximum target} $map { |
|||
if {$value >= $minimum && $value < $maximum} {return $target} |
|||
} |
|||
# Failed to map; return the input |
|||
return $value |
|||
}</lang> |
|||
How it is used: |
|||
<lang tcl># Make the mapping |
|||
set inputTable { |
|||
>= 0.00 < 0.06 := 0.10 |
|||
>= 0.06 < 0.11 := 0.18 |
|||
>= 0.11 < 0.16 := 0.26 |
|||
>= 0.16 < 0.21 := 0.32 |
|||
>= 0.21 < 0.26 := 0.38 |
|||
>= 0.26 < 0.31 := 0.44 |
|||
>= 0.31 < 0.36 := 0.50 |
|||
>= 0.36 < 0.41 := 0.54 |
|||
>= 0.41 < 0.46 := 0.58 |
|||
>= 0.46 < 0.51 := 0.62 |
|||
>= 0.51 < 0.56 := 0.66 |
|||
>= 0.56 < 0.61 := 0.70 |
|||
>= 0.61 < 0.66 := 0.74 |
|||
>= 0.66 < 0.71 := 0.78 |
|||
>= 0.71 < 0.76 := 0.82 |
|||
>= 0.76 < 0.81 := 0.86 |
|||
>= 0.81 < 0.86 := 0.90 |
|||
>= 0.86 < 0.91 := 0.94 |
|||
>= 0.91 < 0.96 := 0.98 |
|||
>= 0.96 < 1.01 := 1.00 |
|||
} |
|||
set map [parseTable $inputTable] |
|||
# Apply the mapping to some inputs (from the Oz example) |
|||
foreach example {.7388727 .8593103 .826687 .3444635 .0491907} { |
|||
puts "$example -> [priceFraction $map $example]" |
|||
}</lang> |
|||
Output: |
|||
<pre> |
|||
.7388727 -> 0.82 |
|||
.8593103 -> 0.90 |
|||
.826687 -> 0.90 |
|||
.3444635 -> 0.50 |
|||
.0491907 -> 0.10 |
|||
</pre> |
Revision as of 09:23, 16 March 2010
You are encouraged to solve this task according to the task description, using any language you may know.
A friend of mine runs a Pharmacy. He has a specialised rounding function in his Dispensary application which receives a decimal value of currency and forces it to a standard value. This value is regulated by a government department.
Task: Given a floating point value between 0.00 and 1.00, rescale according to the following table:
>= 0.00 < 0.06 := 0.10 >= 0.06 < 0.11 := 0.18 >= 0.11 < 0.16 := 0.26 >= 0.16 < 0.21 := 0.32 >= 0.21 < 0.26 := 0.38 >= 0.26 < 0.31 := 0.44 >= 0.31 < 0.36 := 0.50 >= 0.36 < 0.41 := 0.54 >= 0.41 < 0.46 := 0.58 >= 0.46 < 0.51 := 0.62 >= 0.51 < 0.56 := 0.66 >= 0.56 < 0.61 := 0.70 >= 0.61 < 0.66 := 0.74 >= 0.66 < 0.71 := 0.78 >= 0.71 < 0.76 := 0.82 >= 0.76 < 0.81 := 0.86 >= 0.81 < 0.86 := 0.90 >= 0.86 < 0.91 := 0.94 >= 0.91 < 0.96 := 0.98 >= 0.96 < 1.01 := 1.00
BASIC
This could also be done by building an array, but I felt that this was simpler.
<lang qbasic>DECLARE FUNCTION PriceFraction! (price AS SINGLE)
RANDOMIZE TIMER DIM x AS SINGLE x = RND PRINT x, PriceFraction(x)
FUNCTION PriceFraction! (price AS SINGLE)
'returns price unchanged if invalid value SELECT CASE price CASE IS < 0! PriceFraction! = price CASE IS < .06 PriceFraction! = .1 CASE IS < .11 PriceFraction! = .18 CASE IS < .16 PriceFraction! = .26 CASE IS < .21 PriceFraction! = .32 CASE IS < .26 PriceFraction! = .38 CASE IS < .31 PriceFraction! = .44 CASE IS < .36 PriceFraction! = .5 CASE IS < .41 PriceFraction! = .54 CASE IS < .46 PriceFraction! = .58 CASE IS < .51 PriceFraction! = .62 CASE IS < .56 PriceFraction! = .66 CASE IS < .61 PriceFraction! = .7 CASE IS < .66 PriceFraction! = .74 CASE IS < .71 PriceFraction! = .78 CASE IS < .76 PriceFraction! = .82 CASE IS < .81 PriceFraction! = .86 CASE IS < .86 PriceFraction! = .9 CASE IS < .91 PriceFraction! = .94 CASE IS < .96 PriceFraction! = .98 CASE IS < 1.01 PriceFraction! = 1! CASE ELSE PriceFraction! = price END SELECT
END FUNCTION</lang>
Sample outputs (run 5 times):
.7388727 .82 .8593103 .9 .826687 .9 .3444635 .5 .0491907 .1
Clipper
<lang dbase>FUNCTION PriceFraction( npQuantDispensed )
LOCAL aPriceFraction := { {0,.06,.1},; {.06,.11,.18}, ; {.11,.16,.26}, ; {.16,.21,.32}, ; {.21,.26,.38}, ; {.26,.31,.44}, ; {.31,.36,.5}, ; {.36,.41,.54}, ; {.41,.46,.58}, ; {.46,.51,.62}, ; {.51,.56,.66}, ; {.56,.61,.7}, ; {.61,.66,.74}, ; {.66,.71,.78}, ; {.71,.76,.82}, ; {.76,.81,.86}, ; {.81,.86,.9}, ; {.86,.91,.94}, ; {.91,.96,.98} } LOCAL nResult LOCAL nScan IF npQuantDispensed = 0 nResult = 0 ELSEIF npQuantDispensed >= .96 nResult = 1 ELSE nScan := ASCAN( aPriceFraction, ; { |aItem| npQuantDispensed >= aItem[ 1 ] .AND.; npQuantDispensed < aItem[ 2 ] } ) nResult := aPriceFraction[ nScan ][ 3 ] END IF RETURN nResult</lang>
Oz
Using a for-loop with return and a default value for values >= 1.01. For out-of-range input, a "failed value" is returned, i.e. a value that throws an exception when it is accessed.
<lang oz>fun {PriceFraction X}
OutOfRange = {Value.failed outOfRange(X)}
in
for Limit#Result in [0.00#OutOfRange 0.06#0.10 0.11#0.18 0.16#0.26 0.21#0.32 0.26#0.38 0.31#0.44 0.36#0.5 0.41#0.54 0.46#0.58 0.51#0.62 0.56#0.66 0.61#0.70 0.66#0.74 0.71#0.78 0.76#0.82 0.81#0.86 0.86#0.90 0.91#0.94 0.96#0.98 1.01#1.00 ] return:Return default:OutOfRange do if X < Limit then {Return Result} end end
end</lang>
Python
Using the bisect standard module.
<lang python>>>> import bisect >>> def pricerounder(pricein): cin = [0.06, 0.11, 0.16, 0.21, 0.26, 0.31, 0.36, 0.41, 0.46, 0.51, 0.56, 0.61, 0.66, 0.71, 0.76, 0.81, 0.86, 0.91, 0.96, 1.01] cout = [0.10, 0.18, 0.26, 0.32, 0.38, 0.44, 0.50, 0.54, 0.58, 0.62, 0.66, 0.70, 0.74, 0.78, 0.82, 0.86, 0.90, 0.94, 0.98, 1.00] return cout[ bisect.bisect_right(cin, pricein) ]</lang>
When dealing with money it is good to think about possible loss of precision. If we change the units to be integer cents we could use the following exact routine: <lang python>>>> import bisect >>> _cin = [ 6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56, 61, 66, 71, 76, 81, 86, 91, 96, 101] >>> _cout = [10, 18, 26, 32, 38, 44, 50, 54, 57, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 100] >>> def centsrounder(centsin): return _cout[ bisect.bisect_right(_cin, centsin) ]</lang> Other options are to use the fractions or decimals modules for calculating money to a known precision.
Tcl
Structured as two functions, one to parse the input data as described in the problem into a form which Tcl can work with easily, and the other to perform the mapping. <lang tcl># Used once to turn the table into a "nice" form proc parseTable table {
set map {} set LINE_RE {^ *>= *([0-9.]+) *< *([0-9.]+) *:= *([0-9.]+) *$} foreach line [split $table \n] {
if {[string trim $line] eq ""} continue if {[regexp $LINE_RE $line -> min max target]} { lappend map $min $max $target } else { error "invalid table format: $line" }
} return $map
}
- How to apply the "nice" table to a particular value
proc priceFraction {map value} {
foreach {minimum maximum target} $map {
if {$value >= $minimum && $value < $maximum} {return $target}
} # Failed to map; return the input return $value
}</lang> How it is used: <lang tcl># Make the mapping set inputTable {
>= 0.00 < 0.06 := 0.10 >= 0.06 < 0.11 := 0.18 >= 0.11 < 0.16 := 0.26 >= 0.16 < 0.21 := 0.32 >= 0.21 < 0.26 := 0.38 >= 0.26 < 0.31 := 0.44 >= 0.31 < 0.36 := 0.50 >= 0.36 < 0.41 := 0.54 >= 0.41 < 0.46 := 0.58 >= 0.46 < 0.51 := 0.62 >= 0.51 < 0.56 := 0.66 >= 0.56 < 0.61 := 0.70 >= 0.61 < 0.66 := 0.74 >= 0.66 < 0.71 := 0.78 >= 0.71 < 0.76 := 0.82 >= 0.76 < 0.81 := 0.86 >= 0.81 < 0.86 := 0.90 >= 0.86 < 0.91 := 0.94 >= 0.91 < 0.96 := 0.98 >= 0.96 < 1.01 := 1.00
} set map [parseTable $inputTable]
- Apply the mapping to some inputs (from the Oz example)
foreach example {.7388727 .8593103 .826687 .3444635 .0491907} {
puts "$example -> [priceFraction $map $example]"
}</lang> Output:
.7388727 -> 0.82 .8593103 -> 0.90 .826687 -> 0.90 .3444635 -> 0.50 .0491907 -> 0.10