Map range: Difference between revisions
m J: add comments |
Changed to a function |
||
Line 115: | Line 115: | ||
{{incorrect|PicoLisp|Task asks for a generic function of two input ranges and a value.}} |
{{incorrect|PicoLisp|Task asks for a generic function of two input ranges and a value.}} |
||
<lang PicoLisp>(scl 1) |
<lang PicoLisp>(scl 1) |
||
(let (A1 0 A2 10.0 B1 -1.0 B2 0) |
|||
(de mapRange (Val A1 A2 B1 B2) |
|||
⚫ | |||
⚫ | |||
(format |
|||
⚫ | |||
(for Val (range 0 10.0 1.0) |
|||
⚫ | |||
⚫ | |||
⚫ | |||
Output: |
Output: |
||
<pre>-1.0 |
<pre>-1.0 |
Revision as of 07:31, 1 December 2010
Given two ranges, and ; then a value in range is linearly mapped to a value in range when:
The task is to write a function/subroutine/... that takes two ranges and a real number, and returns the mapping of the real number from the first to the second range. Use this function to map values from the range [0, 10]
to the range [-1, 0]
.
Extra credit: Show additional idiomatic ways of performing the mapping, using tools available to the language.
C++
This example defines a template function to handle the mapping, using two std::pair objects to define the source and destination ranges. It returns the provided value mapped into the target range.
It's not written efficiently; certainly, there can be fewer explicit temporary variables. The use of the template offers a choice in types for precision and accuracy considerations, though one area for improvement might be to allow a different type for intermediate calculations.
<lang cpp>#include <iostream>
- include <utility>
template<typename tVal> tVal map_value(std::pair<tVal,tVal> a, std::pair<tVal, tVal> b, tVal inVal) {
tVal inValNorm = inVal - a.first; tVal aUpperNorm = a.second - a.first; tVal normPosition = inValNorm / aUpperNorm;
tVal bUpperNorm = b.second - b.first; tVal bValNorm = normPosition * bUpperNorm; tVal outVal = b.first + bValNorm;
return outVal;
}
int main() {
std::pair<float,float> a(0,10), b(-1,0);
for(float value = 0.0; 10.0 >= value; ++value) std::cout << "map_value(" << value << ") = " << map_value(a, b, value) << std::endl;
return 0;
}</lang>
Output:
map_value(0) = -1 map_value(1) = -0.9 map_value(2) = -0.8 map_value(3) = -0.7 map_value(4) = -0.6 map_value(5) = -0.5 map_value(6) = -0.4 map_value(7) = -0.3 map_value(8) = -0.2 map_value(9) = -0.1 map_value(10) = 0
Forth
<lang forth>\ linear interpolation
- lerp ( b2 b1 a2 a1 s -- t )
fover f- frot frot f- f/ frot frot fswap fover f- frot f* f+ ;
- test 11 0 do 0e -1e 10e 0e i s>f lerp f. loop ;</lang>
There is less stack shuffling if you use origin and range instead of endpoints for intervals. (o = a1, r = a2-a1)
<lang forth>: lerp ( o2 r2 r1 o1 s -- t ) fswap f- fswap f/ f* f+ ;
- test 11 0 do -1e 1e 10e 0e i s>f lerp f. loop ;</lang>
J
<lang j>maprange=:2 :0
'a1 a2'=.m 'b1 b2'=.n b1+((y-a1)*b2-b1)%a2-a1
) NB. this version defers all calculations to runtime, but mirrors exactly the task formulation</lang>
Or
<lang j>maprange=:2 :0
'a1 a2'=.m 'b1 b2'=.n b1 + ((b2-b1)%a2-a1) * -&a1
) NB. this version precomputes the scaling ratio</lang>
Example use:
<lang j> 2 4 maprange 5 11 (2.718282 3 3.141592) 7.15485 8 8.42478</lang>
or
<lang j> adjust=:2 4 maprange 5 11 NB. save the derived function as a named entity
adjust 2.718282 3 3.141592
7.15485 8 8.42478</lang>
Required example:
<lang j> 0 10 maprange _1 0 i.11 _1 _0.9 _0.8 _0.7 _0.6 _0.5 _0.4 _0.3 _0.2 _0.1 0</lang>
Logo
<lang logo>to interpolate :s :a1 :a2 :b1 :b2
output (:s-:a1) / (:a2-:a1) * (:b2-:b1) + :b1
end
for [i 0 10] [print interpolate :i 0 10 -1 0]</lang>
PicoLisp
<lang PicoLisp>(scl 1)
(de mapRange (Val A1 A2 B1 B2)
(+ B1 (*/ (- Val A1) (- B2 B1) (- A2 A1))) )
(for Val (range 0 10.0 1.0)
(prinl (format (mapRange Val 0 10.0 -1.0 0) *Scl) ) )</lang>
Output:
-1.0 -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1 0.0
PureBasic
<lang PureBasic>Structure RR
a.f b.f
EndStructure
Procedure.f MapRange(*a.RR, *b.RR, s)
Protected.f a1, a2, b1, b2 a1=*a\a: a2=*a\b b1=*b\a: b2=*b\b ProcedureReturn b1 + ((s - a1) * (b2 - b1) / (a2 - a1))
EndProcedure
- - Test the function
If OpenConsole()
Define.RR Range1, Range2 Range1\a=0: Range1\b=10 Range2\a=-1:Range2\b=0 ; For i=0 To 10 PrintN(RSet(Str(i),2)+" maps to "+StrF(MapRange(@Range1, @Range2, i),1)) Next
EndIf</lang>
0 maps to -1.0 1 maps to -0.9 2 maps to -0.8 3 maps to -0.7 4 maps to -0.6 5 maps to -0.5 6 maps to -0.4 7 maps to -0.3 8 maps to -0.2 9 maps to -0.1 10 maps to 0.0
Python
<lang python>>>> def maprange( a, b, s): (a1, a2), (b1, b2) = a, b return b1 + ((s - a1) * (b2 - b1) / (a2 - a1))
>>> for s in range(11): print("%2g maps to %g" % (s, maprange( (0, 10), (-1, 0), s)))
0 maps to -1 1 maps to -0.9 2 maps to -0.8 3 maps to -0.7 4 maps to -0.6 5 maps to -0.5 6 maps to -0.4 7 maps to -0.3 8 maps to -0.2 9 maps to -0.1
10 maps to 0</lang>
Tcl
<lang tcl>package require Tcl 8.5 proc rangemap {rangeA rangeB value} {
lassign $rangeA a1 a2 lassign $rangeB b1 b2 expr {$b1 + ($value - $a1)*double($b2 - $b1)/($a2 - $a1)}
}</lang> Demonstration (using a curried alias to bind the ranges mapped from and to): <lang tcl>interp alias {} demomap {} rangemap {0 10} {-1 0} for {set i 0} {$i <= 10} {incr i} {
puts [format "%2d -> %5.2f" $i [demomap $i]]
}</lang> Output:
0 -> -1.00 1 -> -0.90 2 -> -0.80 3 -> -0.70 4 -> -0.60 5 -> -0.50 6 -> -0.40 7 -> -0.30 8 -> -0.20 9 -> -0.10 10 -> 0.00