Map range: Difference between revisions
No edit summary |
|||
Line 518: | Line 518: | ||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
{{trans|Python}} |
|||
<lang ruby>def map_range(a, b, s) |
<lang ruby>def map_range(a, b, s) |
||
(a1,a2), (b1, b2) = a, b |
(a1,a2), (b1, b2) = a, b |
Revision as of 14:02, 6 February 2011
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
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.
Ada
<lang Ada>with Ada.Text_IO; procedure Map is
type First_Range is new Float range 0.0 .. 10.0; type Second_Range is new Float range -1.0 .. 0.0; function Translate (Value : First_Range) return Second_Range is B1 : Float := Float (Second_Range'First); B2 : Float := Float (Second_Range'Last); A1 : Float := Float (First_Range'First); A2 : Float := Float (First_Range'Last); Result : Float; begin Result := B1 + (Float (Value) - A1) * (B2 - B1) / (A2 - A1); return Second_Range (Result); end; function Translate (Value : Second_Range) return First_Range is B1 : Float := Float (First_Range'First); B2 : Float := Float (First_Range'Last); A1 : Float := Float (Second_Range'First); A2 : Float := Float (Second_Range'Last); Result : Float; begin Result := B1 + (Float (Value) - A1) * (B2 - B1) / (A2 - A1); return First_Range (Result); end; Test_Value : First_Range := First_Range'First;
begin
loop Ada.Text_IO.Put_Line (First_Range'Image (Test_Value) & " maps to: " & Second_Range'Image (Translate (Test_Value))); exit when Test_Value = First_Range'Last; Test_Value := Test_Value + 1.0; end loop;
end Map;</lang>
Output:
0.00000E+00 maps to: -1.00000E+00 1.00000E+00 maps to: -9.00000E-01 2.00000E+00 maps to: -8.00000E-01 3.00000E+00 maps to: -7.00000E-01 4.00000E+00 maps to: -6.00000E-01 5.00000E+00 maps to: -5.00000E-01 6.00000E+00 maps to: -4.00000E-01 7.00000E+00 maps to: -3.00000E-01 8.00000E+00 maps to: -2.00000E-01 9.00000E+00 maps to: -1.00000E-01 1.00000E+01 maps to: 0.00000E+00
C
<lang C>
- include<stdio.h>
double mapRange(double a1,double a2,double b1,double b2,double s) { return b1 + (s-a1)*(b2-b1)/(a2-a1); }
int main() { int i; printf("Mapping [0,10] to [-1,0] at intervals of 1:");
for(i=0;i<=10;i++) { printf("\nf(%d) = %f",i,mapRange(1,10,0,1,i)); }
return 0; } </lang>
The output is : <lang> Mapping [0,10] to [-1,0] at intervals of 1: f(0) = -0.111111 f(1) = 0.000000 f(2) = 0.111111 f(3) = 0.222222 f(4) = 0.333333 f(5) = 0.444444 f(6) = 0.555556 f(7) = 0.666667 f(8) = 0.777778 f(9) = 0.888889 f(10) = 1.000000 </lang>
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
Clojure
<lang clojure> (defn maprange [[a1 a2] [b1 b2] s] (+ b1 (/ (* (- s a1) (- b2 b1)) (- a2 a1))))
> (doseq [s (range 11)]
(printf "%2s maps to %s\n" s (maprange [0 10] [-1 0] s)))
0 maps to -1 1 maps to -9/10 2 maps to -4/5 3 maps to -7/10 4 maps to -3/5 5 maps to -1/2 6 maps to -2/5 7 maps to -3/10 8 maps to -1/5 9 maps to -1/10
10 maps to 0 </lang>
D
<lang d>import std.stdio, std.typecons;
alias Tuple!(double, double) Range;
double maprange(Range a, Range b, double s) {
return b[0] + ((s - a[0]) * (b[1] - b[0]) / (a[1] - a[0]));
}
void main() {
Range r1 = tuple(0.0, 10.0); Range r2 = tuple(-1.0, 0.0); foreach (s; 0 .. 11) writefln("%2d maps to %5.2f", s, maprange(r1, r2, s));
}</lang> Output:
0 maps to -1.00 1 maps to -0.90 2 maps to -0.80 3 maps to -0.70 4 maps to -0.60 5 maps to -0.50 6 maps to -0.40 7 maps to -0.30 8 maps to -0.20 9 maps to -0.10 10 maps to 0.00
Erlang
<lang erlang>-module(map_range). -export([map_value/3]).
map_value({A1,A2},{B1,B2},S) ->
B1 + (S - A1) * (B2 - B1) / (A2 - A1).
</lang>
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>
Fortran
<lang fortran>program Map
implicit none real :: t integer :: i
do i = 0, 10 t = Maprange((/0.0, 10.0/), (/-1.0, 0.0/), real(i)) write(*,*) i, " maps to ", t end do
contains
function Maprange(a, b, s)
real :: Maprange real, intent(in) :: a(2), b(2), s Maprange = (s-a(1)) * (b(2)-b(1)) / (a(2)-a(1)) + b(1)
end function Maprange end program Map</lang>
Go
For the extra credit, ", ok" is a Go idiom. It takes advantage of Go's multiple return values feature to return a success/failure disposition. In the case of this task, the result t is undefined if the input s is out of range. <lang go>package main
import "fmt"
// assumes a1 < a2 func newRangeMap(a1, a2, b1, b2 float64) func(float64) (float64, bool) {
return func(s float64) (t float64, ok bool) { if s < a1 || s > a2 { return 0, false // out of range } return b1 + (s-a1)*(b2-b1)/(a2-a1), true }
}
func main() {
rm := newRangeMap(0, 10, -1, 0) for s := float64(-2); s <= 12; s += 2 { t, ok := rm(s) if ok { fmt.Printf("s: %5.2f t: %5.2f\n", s, t) } else { fmt.Printf("s: %5.2f out of range\n", s) } }
}</lang> Output:
s: -2.00 out of range s: 0.00 t: -1.00 s: 2.00 t: -0.80 s: 4.00 t: -0.60 s: 6.00 t: -0.40 s: 8.00 t: -0.20 s: 10.00 t: 0.00 s: 12.00 out of range
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>
Java
<lang java>public class Range { public static void main(String[] args){ for(float s = 0;s <= 10; s++){ System.out.println(s + " in [0, 10] maps to "+ mapRange(0, 10, -1, 0, s)+" in [-1, 0]."); } }
public static double mapRange(double a1, double a2, double b1, double b2, double s){ return b1 + ((s - a1)*(b2 - b1))/(a2 - a1); } }</lang> Output:
0.0 in [0, 10] maps to -1.0 in [-1, 0]. 1.0 in [0, 10] maps to -0.9 in [-1, 0]. 2.0 in [0, 10] maps to -0.8 in [-1, 0]. 3.0 in [0, 10] maps to -0.7 in [-1, 0]. 4.0 in [0, 10] maps to -0.6 in [-1, 0]. 5.0 in [0, 10] maps to -0.5 in [-1, 0]. 6.0 in [0, 10] maps to -0.4 in [-1, 0]. 7.0 in [0, 10] maps to -0.30000000000000004 in [-1, 0]. 8.0 in [0, 10] maps to -0.19999999999999996 in [-1, 0]. 9.0 in [0, 10] maps to -0.09999999999999998 in [-1, 0]. 10.0 in [0, 10] maps to 0.0 in [-1, 0].
The differences in 7, 8, and 9 coem from double math. Similar issues show even when using float types.
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>
Perl 6
<lang perl6>use v6;
- Author: P. Seebauer
sub the_function(Range $a, Range $b, $s ) {
my ($a1, $a2, $b1, $b2) = ($a, $b)».bounds; return $b1 + (($s-$a1) * ($b2-$b1) / ($a2-$a1));
}
for ^11 -> $x {say "$x maps to {the_function(0..10,-1..0, $x)}"}</lang>
%perl6 map_range.p6 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
A more idiomatic way would be to return a closure that does the mapping without have to supply the ranges every time: <lang perl6>sub getmapper(Range $a, Range $b) {
my ($a1, $a2, $b1, $b2) = ($a, $b)».bounds; return -> $s { $b1 + (($s-$a1) * ($b2-$b1) / ($a2-$a1)) }
}
my &mapper = getmapper(0 .. 10, -1 .. 0); for ^11 -> $x {say "$x maps to &mapper($x)"}</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>
REXX
(The different versions don't differ idiomaticly but just in style.)
All program versions could be made more robust by checking if the high and low ends of rangeA aren't equal.
version 1
<lang rexx> /*REXX program maps numbers from one range to another range. */
rangeA='0 10' rangeB='-1 0'
do j=0 to 10 say right(j,3) ' maps to ' mapRange(rangeA, rangeB, j) end
exit
/*─────────────────────────────────────MapRANGE subroutine──────────────*/ mapRange: procedure; arg a1 a2,b1 b2,x; return b1+(x-a1)*(b2-b1)/(a2-a1) </lang> Output:
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
version 2
<lang rexx> /*REXX program maps numbers from one range to another range. */
do j=0 to 10 say right(j,3) ' maps to ' mapRange(0 10, -1 0, j) end
exit
/*─────────────────────────────────────MapRANGE subroutine──────────────*/ mapRange: procedure; arg a1 a2,b1 b2,x; return b1+(x-a1)*(b2-b1)/(a2-a1) </lang>
version 3
<lang rexx> /*REXX program maps numbers from one range to another range. */
rangeA='0 10'; parse var rangeA a1 a2 rangeB='-1 0'; parse var rangeB b1 b2
do j=0 to 10 say right(j,3) ' maps to ' b1+(x-a1)*(b2-b1)/(a2-a1) end
</lang>
Ruby
<lang ruby>def map_range(a, b, s)
(a1,a2), (b1, b2) = a, b b1 + ((s-a1)*(b2-b1)/(a2-a1))
end
11.times do |s|
print s, " maps to ", map_range([0,10], [-1,0], s), "\n"</lang>
Output:
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.30000000000000004 8 maps to -0.19999999999999996 9 maps to -0.09999999999999998 10 maps to 0.0
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
Ursala
The function f
is defined using pattern matching and substitution, taking a pair of pairs of interval endpoints and a number as parameters, and returning a number.
<lang Ursala>#import flo
f((("a1","a2"),("b1","b2")),"s") = plus("b1",div(minus("s","a1"),minus("a2","a1")))
- cast %eL
test = f* ((0.,10.),(-1.,0.))-* ari11/0. 10.</lang> output:
< -1.000000e+00, -9.000000e-01, -8.000000e-01, -7.000000e-01, -6.000000e-01, -5.000000e-01, -4.000000e-01, -3.000000e-01, -2.000000e-01, -1.000000e-01, 0.000000e+00>
A more idiomatic way is to define f as a second order function <lang Ursala>f(("a1","a2"),("b1","b2")) "s" = ...</lang> with the same right hand side as above, so that it takes a pair of intervals and returns a function mapping numbers in one interval to numbers in the other.
An even more idiomatic way is to use the standard library function plin
, which takes an arbitrarily long list of interval endpoints and returns a piecewise linear interpolation function.