Map range

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
where:
- Task
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.
F maprange(a, b, s)
R b[0] + (Float(s - a[0]) * (b[1] - b[0]) / (a[1] - a[0]))
L(s) 0..10
print(‘#2 maps to #.’.format(s, maprange((0, 10), (-1, 0), s)))- 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
A range like [0, n] for some natural number n < 255 can be easily mapped with a lookup table. The second range can be anything, as long as each entry is the same length and are stored consecutively in the desired order. This method requires a bit of compile-time knowledge, but is very efficient in terms of speed. Each element Bn is stored at relative offset An .
mapping:
byte $00,$00,$80,$BF ;-1.0f (stored little-endian so the bytes are backwards)
byte $66,$66,$66,$BF ;-0.9f
byte $CD,$CC,$4C,$BF ;-0.8f
byte $33,$33,$33,$BF ;-0.7f
etc.If the range isn't [0, n], but begins at some other natural number [k, n] where k,n < 255 , we can express it as [0, n-k] instead and simply subtract the "key" at runtime to get the offset.
This method is very convenient for implementing ASCII into hardware that lacks a built-in system font (like the NES). You can save graphics memory by mapping tile number 0 to ASCII 32, tile number 01 to 33, and so on. Had you mapped them "correctly," (i.e. tile number 32 to ASCII 32) you would still need a "blank tile" as tile number zero. So the easier solution is to subtract 32 from the character code before printing.
;runs during non-maskable interrupt.
PrintChar:
;a = char to print
SEC
SBC #$32 ;subtract ascii offset to map the index to the correct tile graphics data.
;everything below this comment is hardware-specific mumbo-jumbo, feel free to ignore it if you don't care.
;ideally you'd want to do this before getting here so that the only thing that happens during NMI is the write to vram.
pha
LDA Cursor_Y
ASL
ASL
ASL
ASL
ASL
STA tempY ;row * 32
LDA #$20
ADC Cursor_X
STA tempX
LDA $2002 ;reset picture processor high-low latch
LDA tempX
STA $2006 ;this register is big-endian for some reason. Which is why I had to store Cursor_Y << 5 into tempY rather than here directly.
LDA tempY
STA $2006
PLA
STA $2007(defun mapping (a1 a2 b1 b2 s)
(+ b1 (/ (* (- s a1)
(- b2 b1))
(- a2 a1))))
(defun map-each (a1 a2 b1 b2 ss)
(if (endp ss)
nil
(cons (mapping a1 a2 b1 b2 (first ss))
(map-each a1 a2 b1 b2 (rest ss)))))
(map-each 0 10 -1 0 '(0 1 2 3 4 5 6 7 8 9 10))
;; (-1 -9/10 -4/5 -7/10 -3/5 -1/2 -2/5 -3/10 -1/5 -1/10 0)
INCLUDE "D2:REAL.ACT" ;from the Action! Tool Kit
PROC Map(REAL POINTER a1,a2,b1,b2,s,res)
REAL tmp1,tmp2,tmp3
RealSub(s,a1,tmp1) ;tmp1=s-a1
RealSub(b2,b1,tmp2) ;tmp2=b2-b1
RealMult(tmp1,tmp2,tmp3) ;tmp3=(s-a1)*(b2-b1)
RealSub(a2,a1,tmp1) ;tmp1=a2-a1
RealDiv(tmp3,tmp1,tmp2) ;tmp2=(s-a1)*(b2-b1)/(a2-a1)
RealAdd(b1,tmp2,res) ;res=b1+(s-a1)*(b2-b1)/(a2-a1)
RETURN
PROC Main()
BYTE i
REAL a1,a2,b1,b2,s,res
Put(125) PutE() ;clear screen
ValR("0",a1) ValR("10",a2)
ValR("-1",b1) ValR("0",b2)
FOR i=0 TO 10
DO
IntToReal(i,s)
Map(a1,a2,b1,b2,s,res)
PrintR(s) Print(" maps to ")
PrintRE(res)
OD
RETURN- Output:
Screenshot from Atari 8-bit computer
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
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;
- 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
# maps a real s in the range [ a1, a2 ] to the range [ b1, b2 ] #
# there are no checks that s is in the range or that the ranges are valid #
PROC map range = ( REAL s, a1, a2, b1, b2 )REAL:
b1 + ( ( s - a1 ) * ( b2 - b1 ) ) / ( a2 - a1 );
# test the mapping #
FOR i FROM 0 TO 10 DO
print( ( whole( i, -2 ), " maps to ", fixed( map range( i, 0, 10, -1, 0 ), -8, 2 ), newline ) )
OD- 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
La función "Seqspaced" es una macro-sustitución de "seqsp", y está construida, internamente, como una rutina en C que realiza el siguiente cálculo (en pseudocódigo):
double inc = (nHasta - nDesde) / ( nTotal - 1);
lista[0] = nDesde;
lista[nTotal] = nHasta;
for( n=1; n<nTotal; n++){
lista[n] = lista[n-1] + inc;
}
Macro-sustitución de "Seqspaced":
#defn Seqspaced(__X__,__Y__,__Z__,_V_) #ATOM#CMPLX;#ATOM#CMPLX;#ATOM#CMPLX;keep;lthan(1);\
do{{"Seqspaced: num elements < 1"}throw(2301)},seqsp(_V_)Otras macrosustituciones:
#defn Toksep(__X__) #ATOM#CMPLX;toksep;
#defn Cat(_X_,*) #ATOM#CMPLX;#GENCODE $$$*$$$ #ATCMLIST;cat; #ENDGEN
#defn Justleft(_X_,_V_) {" "};#ATOM#CMPLX;#ATOM#CMPLX;padright;Código que resuelve la tarea:
#include <jambo.h>
Main
v=0,w=0
Seqspaced(-1,0,11,w) // [-1,0}->[0-10]=11 números
Seqspaced(0,10,11,v)
Toksep( "\n" )
Cat( Justright(5,Str(v))," => ",Justright(5,Str(w))), Prnl
End- Output:
$ hopper maprange.jambo
0 => -1
1 => -0.9
2 => -0.8
3 => -0.7
4 => -0.6
5 => -0.5
6 => -0.4
7 => -0.3
8 => -0.2
9 => -0.1
10 => 0
$
------------------------ MAP RANGE -----------------------
-- rangeMap :: (Num, Num) -> (Num, Num) -> Num -> Num
on rangeMap(a, b)
script
on |λ|(s)
set {a1, a2} to a
set {b1, b2} to b
b1 + ((s - a1) * (b2 - b1)) / (a2 - a1)
end |λ|
end script
end rangeMap
--------------------------- TEST -------------------------
on run
set mapping to rangeMap({0, 10}, {-1, 0})
set xs to enumFromTo(0, 10)
set ys to map(mapping, xs)
set zs to map(approxRatio(0), ys)
unlines(zipWith3(formatted, xs, ys, zs))
end run
------------------------- DISPLAY ------------------------
-- formatted :: Int -> Float -> Ratio -> String
on formatted(x, m, r)
set fract to showRatio(r)
set {n, d} to splitOn("/", fract)
(justifyRight(2, space, x as string) & " -> " & ¬
justifyRight(4, space, m as string)) & " = " & ¬
justifyRight(2, space, n) & "/" & d
end formatted
-------------------- GENERIC FUNCTIONS -------------------
-- Absolute value.
-- abs :: Num -> Num
on abs(x)
if 0 > x then
-x
else
x
end if
end abs
-- approxRatio :: Real -> Real -> Ratio
on approxRatio(epsilon)
script
on |λ|(n)
if {real, integer} contains (class of epsilon) and 0 < epsilon then
set e to epsilon
else
set e to 1 / 10000
end if
script gcde
on |λ|(e, x, y)
script _gcd
on |λ|(a, b)
if b < e then
a
else
|λ|(b, a mod b)
end if
end |λ|
end script
|λ|(abs(x), abs(y)) of _gcd
end |λ|
end script
set c to |λ|(e, 1, n) of gcde
ratio((n div c), (1 div c))
end |λ|
end script
end approxRatio
-- enumFromTo :: Int -> Int -> [Int]
on enumFromTo(m, n)
if m ≤ n then
set lst to {}
repeat with i from m to n
set end of lst to i
end repeat
return lst
else
return {}
end if
end enumFromTo
-- gcd :: Int -> Int -> Int
on gcd(a, b)
set x to abs(a)
set y to abs(b)
repeat until y = 0
if x > y then
set x to x - y
else
set y to y - x
end if
end repeat
return x
end gcd
-- justifyLeft :: Int -> Char -> String -> String
on justifyLeft(n, cFiller, strText)
if n > length of strText then
text 1 thru n of (strText & replicate(n, cFiller))
else
strText
end if
end justifyLeft
-- justifyRight :: Int -> Char -> String -> String
on justifyRight(n, cFiller, strText)
if n > length of strText then
text -n thru -1 of ((replicate(n, cFiller) as text) & strText)
else
strText
end if
end justifyRight
-- length :: [a] -> Int
on |length|(xs)
set c to class of xs
if list is c or string is c then
length of xs
else
(2 ^ 29 - 1) -- (maxInt - simple proxy for non-finite)
end if
end |length|
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- minimum :: Ord a => [a] -> a
on minimum(xs)
set lng to length of xs
if lng < 1 then return missing value
set m to item 1 of xs
repeat with x in xs
set v to contents of x
if v < m then set m to v
end repeat
return m
end minimum
-- ratio :: Int -> Int -> Ratio Int
on ratio(x, y)
script go
on |λ|(x, y)
if 0 ≠ y then
if 0 ≠ x then
set d to gcd(x, y)
{type:"Ratio", n:(x div d), d:(y div d)}
else
{type:"Ratio", n:0, d:1}
end if
else
missing value
end if
end |λ|
end script
go's |λ|(x * (signum(y)), abs(y))
end ratio
-- Egyptian multiplication - progressively doubling a list, appending
-- stages of doubling to an accumulator where needed for binary
-- assembly of a target length
-- replicate :: Int -> a -> [a]
on replicate(n, a)
set out to {}
if n < 1 then return out
set dbl to {a}
repeat while (n > 1)
if (n mod 2) > 0 then set out to out & dbl
set n to (n div 2)
set dbl to (dbl & dbl)
end repeat
return out & dbl
end replicate
-- showRatio :: Ratio -> String
on showRatio(r)
(n of r as string) & "/" & (d of r as string)
end showRatio
-- signum :: Num -> Num
on signum(x)
if x < 0 then
-1
else if x = 0 then
0
else
1
end if
end signum
-- splitOn :: String -> String -> [String]
on splitOn(pat, src)
set {dlm, my text item delimiters} to ¬
{my text item delimiters, pat}
set xs to text items of src
set my text item delimiters to dlm
return xs
end splitOn
-- take :: Int -> [a] -> [a]
-- take :: Int -> String -> String
on take(n, xs)
set c to class of xs
if list is c then
if 0 < n then
items 1 thru min(n, length of xs) of xs
else
{}
end if
else if string is c then
if 0 < n then
text 1 thru min(n, length of xs) of xs
else
""
end if
else if script is c then
set ys to {}
repeat with i from 1 to n
set v to xs's |λ|()
if missing value is v then
return ys
else
set end of ys to v
end if
end repeat
return ys
else
missing value
end if
end take
-- unlines :: [String] -> String
on unlines(xs)
set {dlm, my text item delimiters} to ¬
{my text item delimiters, linefeed}
set str to xs as text
set my text item delimiters to dlm
str
end unlines
-- zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
on zipWith3(f, xs, ys, zs)
set lng to minimum({length of xs, length of ys, length of zs})
if 1 > lng then return {}
set lst to {}
tell mReturn(f)
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, item i of ys, item i of zs)
end repeat
return lst
end tell
end zipWith3
- Output:
0 -> -1.0 = -1/1 1 -> -0.9 = -9/10 2 -> -0.8 = -4/5 3 -> -0.7 = -7/10 4 -> -0.6 = -3/5 5 -> -0.5 = -1/2 6 -> -0.4 = -2/5 7 -> -0.3 = -3/10 8 -> -0.2 = -1/5 9 -> -0.1 = -1/10 10 -> 0.0 = 0/1
getMapped: function [a,b,i][
round .to:1 b\0 + ((i - a\0) * (b\1 - b\0))/(a\1 - a\0)
]
rangeA: @[0.0 10.0]
rangeB: @[0-1.0 0.0]
loop 0..10 'x [
mapped: getMapped rangeA rangeB to :floating x
print [x "maps to" mapped]
]
- 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.3 8 maps to -0.2 9 maps to -0.1 10 maps to 0.0
real mapRange(int s, int a1, int a2, int b1, int b2) {
return b1 + (s - a1) * (b2 - b1) / (a2 - a1);
}
for (int i = 0; i <= 10; ++i) {
real r = mapRange(i, 0, 10, -1, 0);
write( (string)i + " maps to " + format("%0.1f", r) );
}
- 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
mapRange(a1, a2, b1, b2, s)
{
return b1 + (s-a1)*(b2-b1)/(a2-a1)
}
out := "Mapping [0,10] to [-1,0] at intervals of 1:`n"
Loop 11
out .= "f(" A_Index-1 ") = " mapRange(0,10,-1,0,A_Index-1) "`n"
MsgBox % out
# syntax: GAWK -f MAP_RANGE.AWK
BEGIN {
a1 = 0
a2 = 10
b1 = -1
b2 = 0
for (i=a1; i<=a2; i++) {
printf("%g maps to %g\n",i,map_range(a1,a2,b1,b2,i))
}
exit(0)
}
function map_range(a1,a2,b1,b2,num) {
return b1 + ((num-a1) * (b2-b1) / (a2-a1))
}
- 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
Axiom provides a Segment domain for intervals. The following uses a closure for a mapRange function over fields, which provides for some generality.
)abbrev package TESTP TestPackage
TestPackage(R:Field) : with
mapRange: (Segment(R), Segment(R)) -> (R->R)
== add
mapRange(fromRange, toRange) ==
(a1,a2,b1,b2) := (lo fromRange,hi fromRange,lo toRange,hi toRange)
(x:R):R +-> b1+(x-a1)*(b2-b1)/(a2-a1)Use:
f := mapRange(1..10,a..b)
[(xi,f xi) for xi in 1..10]- Output:
b + 8a 2b + 7a b + 2a 4b + 5a 5b + 4a
[(1,a), (2,------), (3,-------), (4,------), (5,-------), (6,-------),
9 9 3 9 9
2b + a 7b + 2a 8b + a
(7,------), (8,-------), (9,------), (10,b)]
3 9 9
Type: List(Tuple(Fraction(Polynomial(Integer))))
100 REM Map range
110 DECLARE EXTERNAL FUNCTION MapRange
120 FOR I = 0 TO 10
130 PRINT USING "## maps to ##.#": I, MapRange(I, 0, 10, -1, 0)
140 NEXT I
150 END
160 REM *****************************************
170 EXTERNAL FUNCTION MapRange(S, A1, A2, B1, B2)
180 LET MapRange = B1 + (S - A1) * (B2 - B1) / (A2 - A1)
190 END FUNCTION
- Output:
0 maps to -1.0 1 maps to -.9 2 maps to -.8 3 maps to -.7 4 maps to -.6 5 maps to -.5 6 maps to -.4 7 maps to -.3 8 maps to -.2 9 maps to -.1 10 maps to .0
function MapRange(s, a1, a2, b1, b2)
return b1+(s-a1)*(b2-b1)/(a2-a1)
end function
for i = 0 to 10
print i; " maps to "; MapRange(i,0,10,-1,0)
next i
end- Output:
Igual que la entrada de FreeBASIC.
@% = 5 : REM Column width
DIM range{l, h}
DIM A{} = range{}, B{} = range{}
A.l = 0 : A.h = 10
B.l = -1 : B.h = 0
FOR n = 0 TO 10
PRINT n " maps to " FNmaprange(A{}, B{}, n)
NEXT
END
DEF FNmaprange(a{}, b{}, s)
= b.l + (s - a.l) * (b.h - b.l) / (a.h - a.l)
- 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
10 REM MAP RANGE
20 REM COMMODORE BASIC 2.0
30 REM ================================
40 A1 = 0 : A2 = 10
50 B1 = -1 : B2 = 0
60 DEF FN MR(S)=B1+(S-A1)*(B2-B1)/(A2-A1)
70 FOR S=0 TO 10
80 PRINT S;"MAPS TO ";FN MR(S)
90 NEXT- Output:
0 MAPS TO -1 1 MAPS TO -.9 2 MAPS TO -.8 3 MAPS TO -.7 4 MAPS TO -.6 5 MAPS TO -.5 6 MAPS TO -.4 7 MAPS TO -.3 8 MAPS TO -.2 9 MAPS TO -.1 10 MAPS TO 0
100 cls
110 for i = 0 to 10
120 print using "##";i;
130 print " maps to ";
140 print using "##.#";maprange(i,0,10,-1,0)
150 next i
160 end
170 function maprange(s,a1,a2,b1,b2)
180 maprange = b1+(s-a1)*(b2-b1)/(a2-a1)
190 end function
- Output:
Same as FreeBASIC entry.
define a1 = 0, b1 = 0, a2 = 0, b2 = 0
for i = 0 to 10
let s = i
let a1 = 0
let a2 = 10
let b1 = -1
let b2 = 0
print i, " : ", b1 + ( s - a1 ) * ( b2 - b1 ) / ( a2 - a1 )
next i
- Output:
0 : -11 : -0.9000 2 : -0.8000 3 : -0.7000 4 : -0.6000 5 : -0.5000 6 : -0.4000 7 : -0.3000 8 : -0.2000 9 : -0.1000
10 : 0
Function MapRange(s As Integer, a1 As Integer, a2 As Integer, b1 As Integer, b2 As Integer) As Double
Return b1+(s-a1)*(b2-b1)/(a2-a1)
End Function
For i As Integer = 0 To 10
Print Using "## maps to ##.#"; i; MapRange(i,0,10,-1,0)
Next i
Sleep
- 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.3 8 maps to -0.2 9 maps to -0.1 10 maps to 0.0
100 REM Map range
110 DEF FN MR(S) = B1 + (S - A1) * (B2 - B1) / (A2 - A1)
120 A1 = 0: A2 = 10: B1 = -1: B2 = 0
130 FOR X = A1 TO A2 STEP 2
140 PRINT USING "## maps to ##.#"; X; FN MR(X)
150 NEXT X
160 REM Inverse mapping to illustrate change of parameters
170 PRINT: PRINT "Inverse mapping:"
180 A1 = -1: A2 = 0: B1 = 0: B2 = 10
190 FOR X = A1 TO A2 STEP .2
200 PRINT USING "##.# maps to ##"; X; FN MR(X)
210 NEXT X
220 END
- Output:
0 maps to -1.0 2 maps to -0.8 4 maps to -0.6 6 maps to -0.4 8 maps to -0.2 10 maps to 0.0 Inverse mapping: -1.0 maps to 0 -0.8 maps to 2 -0.6 maps to 4 -0.4 maps to 6 -0.2 maps to 8 -0.0 maps to 10
Function MapRange(s As Integer, a1 As Integer, a2 As Integer, b1 As Integer, b2 As Integer) As Float
Return b1 + (s - a1) * (b2 - b1) / (a2 - a1)
End Function
Public Sub Main()
Dim i As Integer
For i = 0 To 10
Print Format$(i, "##"); " maps to "; Format$(MapRange(i, 0, 10, -1, 0), "+##.0")
Next
End Sub
- Output:
Same as FreeBASIC entry.
100 PROGRAM "MapRange.bas"
110 LET A1=0:LET A2=10
120 LET B1=-1:LET B2=0
130 DEF MR(S)=B1+(S-A1)*(B2-B1)/(A2-A1)
140 FOR I=0 TO 10
150 PRINT I;"maps to ";MR(I)
160 NEXT' map range
for i = 0 to 10
print using("##.#", i); " maps to ";
print using("##.#", mapRange(i, 0, 10, -1, 0))
next i
end
function mapRange(s, a1, a2, b1, b2)
mapRange = b1 + (s - a1) * (b2 - b1) / (a2 - a1)
end function
- Output:
0.0 maps to -1.0 1.0 maps to -0.9 2.0 maps to -0.8 3.0 maps to -0.7 4.0 maps to -0.6 5.0 maps to -0.5 6.0 maps to -0.4 7.0 maps to -0.3 8.0 maps to -0.2 9.0 maps to -0.1 10.0 maps to 0.0
10 CLS
20 DEF FN MR(S) = B1 + (S - A1) * (B2 - B1) / (A2 - A1)
30 A1 = 0: A2 = 10: B1 = -1: B2 = 0
40 FOR X = A1 TO A2
50 PRINT X; "maps to "; FN MR(X)
60 NEXT X
70 END
- Output:
0 maps to -1 1 maps to -.9 2 maps to -.8 3 maps to -.7 4 maps to -.6 5 maps to -.5 6 maps to -.4 7 maps to -.3 8 maps to -.2 9 maps to -.1 10 maps to 0
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
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
REM Map range
DECLARE FUNCTION MapRange (S, A1, A2, B1, B2)
FOR I = 0 TO 10
PRINT USING "## maps to ##.#"; I; MapRange(I, 0, 10, -1, 0)
NEXT I
END
FUNCTION MapRange (S, A1, A2, B1, B2)
MapRange = B1 + (S - A1) * (B2 - B1) / (A2 - A1)
END FUNCTION
- 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.3 8 maps to -0.2 9 maps to -0.1 10 maps to 0.0
The QBasic solution works without any changes.
' Map range
DECLARE FUNCTION MapRange (S#, A1#, A2#, B1#, B2#) AS DOUBLE
FOR I = 0 TO 10
PRINT FORMAT$("%4.1f", I); " maps to ";
PRINT FORMAT$("%4.1f", MapRange(I, 0, 10, -1, 0))
NEXT I
END
FUNCTION MapRange (S#, A1#, A2#, B1#, B2#) AS DOUBLE
MapRange = B1# + (S# - A1#) * (B2# - B1#) / (A2# - A1#)
END FUNCTION
- Output:
0.0 maps to -1.0 1.0 maps to -0.9 2.0 maps to -0.8 3.0 maps to -0.7 4.0 maps to -0.6 5.0 maps to -0.5 6.0 maps to -0.4 7.0 maps to -0.3 8.0 maps to -0.2 9.0 maps to -0.1 10.0 maps to 0.0
The Liberty BASIC solution works without any changes.
sub MapRange(s, a1, a2, b1, b2)
return b1+(s-a1)*(b2-b1)/(a2-a1)
end sub
for i = 0 to 10 step 2
print i, " : ", MapRange(i,0,10,-1,0)
next/* map s from [a, b] to [c, d] */
define m(a, b, c, d, s) {
return (c + (s - a) * (d - c) / (b - a))
}
scale = 6 /* division to 6 decimal places */
"[0, 10] => [-1, 0]
"
for (i = 0; i <= 10; i += 2) {
/*
* If your bc(1) has a print statement, you can try
* print i, " => ", m(0, 10, -1, 0, i), "\n"
*/
i; " => "; m(0, 10, -1, 0, i)
}
quit
- Output:
[0, 10] => [-1, 0] 0 => -1.000000 2 => -.800000 4 => -.600000 6 => -.400000 8 => -.200000 10 => 0.000000
A direct implementation of the specification.
_map_ is a 2-modifier which returns a mapping function given two ranges.
_map_ ← {
a1‿a2 _𝕣_ b1‿b2 s:
b1 + ((s - a1) × b2 - b1) ÷ a2 - a1
}
ZeroTen ← 0‿10 _map_ ¯1‿0
•Show ZeroTen 0.1
•Show ZeroTen 8
¯0.99
¯0.19999999999999996
( ( mapRange
= a1,a2,b1,b2,s
. !arg:(?a1,?a2.?b1,?b2.?s)
& !b1+(!s+-1*!a1)*(!b2+-1*!b1)*(!a2+-1*!a1)^-1
)
& out$"Mapping [0,10] to [-1,0] at intervals of 1:"
& 0:?n
& whl
' ( !n:~>10
& out$("f(" !n ") = " flt$(mapRange$(0,10.-1,0.!n),2))
& 1+!n:?n
)
);- Output:
Mapping [0,10] to [-1,0] at intervals of 1: f( 0 ) = -1,00*10E0 f( 1 ) = -9,00*10E-1 f( 2 ) = -8,00*10E-1 f( 3 ) = -7,00*10E-1 f( 4 ) = -6,00*10E-1 f( 5 ) = -5,00*10E-1 f( 6 ) = -4,00*10E-1 f( 7 ) = -3,00*10E-1 f( 8 ) = -2,00*10E-1 f( 9 ) = -1,00*10E-1 f( 10 ) = 0
#include <stdio.h>
#define mapRange(a1,a2,b1,b2,s) (b1 + (s-a1)*(b2-b1)/(a2-a1))
int main()
{
int i;
puts("Mapping [0,10] to [-1,0] at intervals of 1:");
for(i=0;i<=10;i++)
{
printf("f(%d) = %g\n",i,mapRange(0,10,-1,0,i));
}
return 0;
}
- Output:
Mapping [0,10] to [-1,0] at intervals of 1: f(0) = -1 f(1) = -0.9 f(2) = -0.8 f(3) = -0.7 f(4) = -0.6 f(5) = -0.5 f(6) = -0.4 f(7) = -0.3 f(8) = -0.2 f(9) = -0.1 f(10) = 0
using System;
using System.Linq;
public class MapRange
{
public static void Main() {
foreach (int i in Enumerable.Range(0, 11))
Console.WriteLine($"{i} maps to {Map(0, 10, -1, 0, i)}");
}
static double Map(double a1, double a2, double b1, double b2, double s) => b1 + (s - a1) * (b2 - b1) / (a2 - a1);
}
- 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
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.
#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;
}
- 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
(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
IDENTIFICATION DIVISION.
PROGRAM-ID. demo-map-range.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 i USAGE FLOAT-LONG.
01 mapped-num USAGE FLOAT-LONG.
01 a-begin USAGE FLOAT-LONG VALUE 0.
01 a-end USAGE FLOAT-LONG VALUE 10.
01 b-begin USAGE FLOAT-LONG VALUE -1.
01 b-end USAGE FLOAT-LONG VALUE 0.
01 i-display PIC --9.9.
01 mapped-display PIC --9.9.
PROCEDURE DIVISION.
PERFORM VARYING i FROM 0 BY 1 UNTIL i > 10
CALL "map-range" USING CONTENT a-begin, a-end, b-begin,
b-end, i, REFERENCE mapped-num
COMPUTE i-display ROUNDED = i
COMPUTE mapped-display ROUNDED = mapped-num
DISPLAY FUNCTION TRIM(i-display) " maps to "
FUNCTION TRIM(mapped-display)
END-PERFORM
.
END PROGRAM demo-map-range.
IDENTIFICATION DIVISION.
PROGRAM-ID. map-range.
DATA DIVISION.
LINKAGE SECTION.
01 a-begin USAGE FLOAT-LONG.
01 a-end USAGE FLOAT-LONG.
01 b-begin USAGE FLOAT-LONG.
01 b-end USAGE FLOAT-LONG.
01 val-to-map USAGE FLOAT-LONG.
01 ret USAGE FLOAT-LONG.
PROCEDURE DIVISION USING a-begin, a-end, b-begin, b-end,
val-to-map, ret.
COMPUTE ret =
b-begin + ((val-to-map - a-begin) * (b-end - b-begin)
/ (a-end - a-begin))
.
END PROGRAM map-range.
The output is identical to the output of the Common Lisp example.
mapRange = (a1,a2,b1,b2,s) ->
t = b1 + ((s-a1)*(b2 - b1)/(a2-a1))
for s in [0..10]
console.log("#{s} maps to #{mapRange(0,10,-1,0,s)}")
- 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.30000000000000004 8 maps to -0.19999999999999996 9 maps to -0.09999999999999998 10 maps to 0
(defun map-range (a1 a2 b1 b2 s)
(+ b1
(/ (* (- s a1)
(- b2 b1))
(- a2 a1))))
(loop
for i from 0 to 10
do (format t "~F maps to ~F~C" i
(map-range 0 10 -1 0 i)
#\Newline))
- Output:
0.0 maps to -1.0 1.0 maps to -0.9 2.0 maps to -0.8 3.0 maps to -0.7 4.0 maps to -0.6 5.0 maps to -0.5 6.0 maps to -0.4 7.0 maps to -0.3 8.0 maps to -0.2 9.0 maps to -0.1 10.0 maps to 0.0
def map_range (s, from, to)
a1, a2 = from.begin, from.end
b1, b2 = to.begin, to.end
b1 + ( (s - a1) * (b2 - b1) ) / (a2 - a1)
end
(0..10).each do |i|
printf " %2d -> %g\n", i, map_range(i, 0..10, -1..0)
end
- Output:
0 -> -1 1 -> -0.9 2 -> -0.8 3 -> -0.7 4 -> -0.6 5 -> -0.5 6 -> -0.4 7 -> -0.3 8 -> -0.2 9 -> -0.1 10 -> 0
double mapRange(in double[] a, in double[] b, in double s)
pure nothrow @nogc {
return b[0] + ((s - a[0]) * (b[1] - b[0]) / (a[1] - a[0]));
}
void main() {
import std.stdio;
immutable r1 = [0.0, 10.0];
immutable r2 = [-1.0, 0.0];
foreach (immutable s; 0 .. 11)
writefln("%2d maps to %5.2f", s, mapRange(r1, r2, s));
}
- 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
double mapRange(int s, int a1, int a2, int b1, int b2) {
return b1 + (s - a1) * (b2 - b1) / (a2 - a1);
}
void main() {
for (int i = 0; i <= 10; i++) {
double r = mapRange(i, 0, 10, -1, 0);
print("${i.toString().padLeft(2)} maps to ${r.toStringAsFixed(1)}");
}
}
- 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.3 8 maps to -0.2 9 maps to -0.1 10 maps to 0.0
See #Pascal.
DuckDB has several restrictions of `-` and so in the following definition of maprange(), some commitment to types is required. DOUBLE is the least restrictive floating point type available.
# x is the value to be mapped.
# The ranges, a and b, should each be an array defining the
# left-most and right-most points of the range.
create or replace function maprange(a, b, x) as
b[1] + (((x - a[1]) * (b[2] - b[1]::DOUBLE)) / (a[2] - a[1]::DOUBLE)) ;
# Example 1: a single value
# (Conversion to floating point will be automatic)
select maprange([0,10], [-1, 0], 6) as mapped;
- Output:
┌────────┐ │ mapped │ │ double │ ├────────┤ │ -0.4 │ └────────┘
# Example 2: a range of values
select x, format('{:.2f}', maprange([0,10], [-1, 0], x)) as mapped
from range(0,11,2) _(x);
┌───────┬─────────┐
│ x │ mapped │
│ int64 │ varchar │
├───────┼─────────┤
│ 0 │ -1.00 │
│ 2 │ -0.80 │
│ 4 │ -0.60 │
│ 6 │ -0.40 │
│ 8 │ -0.20 │
│ 10 │ 0.00 │
└───────┴─────────┘
func map_range a1 a2 b1 b2 s .
return b1 + (s - a1) * (b2 - b1) / (a2 - a1)
.
for i = 0 to 10
print i & " -> " & map_range 0 10 -1 0 i
.
- Output:
0 -> -1 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
EchoLisp provides several native interpolation functions: smoothstep, s-curve, .. and linear which performs linear interpolation.
(lib 'plot) ;; interpolation functions
(lib 'compile)
;; rational version
(define (q-map-range x xmin xmax ymin ymax) (+ ymin (/ ( * (- x xmin) (- ymax ymin)) (- xmax xmin))))
;; float version
(define (map-range x xmin xmax ymin ymax) (+ ymin (// ( * (- x xmin) (- ymax ymin)) (- xmax xmin))))
; accelerate it
(compile 'map-range "-vf")
(q-map-range 4 0 10 -1 0)
→ -3/5
(map-range 4 0 10 -1 0)
→ -0.6
(linear 4 0 10 -1 0) ;; native
→ -0.6
(for [(x (in-range 0 10))] (writeln x (q-map-range x 0 10 -1 0) (map-range x 0 10 -1 0)))
0 -1 -1
1 -9/10 -0.9
2 -4/5 -0.8
3 -7/10 -0.7
4 -3/5 -0.6
5 -1/2 -0.5
6 -2/5 -0.4
7 -3/10 -0.3
8 -1/5 -0.2
9 -1/10 -0.1
defmodule RC do
def map_range(a1 .. a2, b1 .. b2, s) do
b1 + (s - a1) * (b2 - b1) / (a2 - a1)
end
end
Enum.each(0..10, fn s ->
:io.format "~2w map to ~7.3f~n", [s, RC.map_range(0..10, -1..0, s)]
end)
- Output:
0 map to -1.000 1 map to -0.900 2 map to -0.800 3 map to -0.700 4 map to -0.600 5 map to -0.500 6 map to -0.400 7 map to -0.300 8 map to -0.200 9 map to -0.100 10 map to 0.000
(defun maprange (a1 a2 b1 b2 s)
(+ b1 (/ (* (- s a1) (- b2 b1)) (- a2 a1))))
(dotimes (i 10)
(message "%s" (maprange 0.0 10.0 -1.0 0.0 i)))
-module(map_range).
-export([map_value/3]).
map_value({A1,A2},{B1,B2},S) ->
B1 + (S - A1) * (B2 - B1) / (A2 - A1).
PROGRAM RANGE
BEGIN
AL=0 AH=10
BL=-1 BH=0
FOR N=0 TO 10 DO
RANGE=BL+(N-AL)*(BH-BL)/(AH-AL)
WRITE("### maps to ##.##";N;RANGE)
! PRINT(N;" maps to ";RANGE)
END FOR
END PROGRAM- 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
function map_range(sequence a, sequence b, atom s)
return b[1]+(s-a[1])*(b[2]-b[1])/(a[2]-a[1])
end function
for i = 0 to 10 do
printf(1, "%2g maps to %4g\n", {i, map_range({0,10},{-1,0},i)})
end for- 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
let map (a1: float) (a2: float) (b1: float) (b2: float) (s: float): float =
b1 + (s - a1) * (b2 - b1) / (a2 - a1)
let xs = [| for i in 0..10 -> map 0.0 10.0 -1.0 0.0 (float i) |]
for x in xs do printfn "%f" x
USE: locals
:: map-range ( a1 a2 b1 b2 x -- y )
x a1 - b2 b1 - * a2 a1 - / b1 + ;
Or:
USING: locals infix ;
:: map-range ( a1 a2 b1 b2 x -- y )
[infix
b1 + (x - a1) * (b2 - b1) / (a2 - a1)
infix] ;
Test run:
10 iota [| x | 0 10 -1 0 x map-range ] map . ! { -1 -9/10 -4/5 -7/10 -3/5 -1/2 -2/5 -3/10 -1/5 -1/10 }
class FRange
{
const Float low
const Float high
// in constructing a range, ensure the low value is smaller than high
new make (Float low, Float high)
{
this.low = ( low <= high ? low : high )
this.high = ( low <= high ? high : low )
}
// return range as a string
override Str toStr () { "[$low,$high]" }
// return a point in given range interpolated into this range
Float remap (Float point, FRange given)
{
this.low + (point - given.low) * (this.high - this.low) / (given.high - given.low)
}
}
class Main
{
public static Void main ()
{
range1 := FRange (0f, 10f)
range2 := FRange (-1f, 0f)
11.times |Int n|
{
m := range2.remap (n.toFloat, range1)
echo ("Value $n in ${range1} maps to $m in ${range2}")
}
}
}- Output:
Value 0 in [0.0,10.0] maps to -1.0 in [-1.0,0.0] Value 1 in [0.0,10.0] maps to -0.9 in [-1.0,0.0] Value 2 in [0.0,10.0] maps to -0.8 in [-1.0,0.0] Value 3 in [0.0,10.0] maps to -0.7 in [-1.0,0.0] Value 4 in [0.0,10.0] maps to -0.6 in [-1.0,0.0] Value 5 in [0.0,10.0] maps to -0.5 in [-1.0,0.0] Value 6 in [0.0,10.0] maps to -0.4 in [-1.0,0.0] Value 7 in [0.0,10.0] maps to -0.30000000000000004 in [-1.0,0.0] Value 8 in [0.0,10.0] maps to -0.19999999999999996 in [-1.0,0.0] Value 9 in [0.0,10.0] maps to -0.09999999999999998 in [-1.0,0.0] Value 10 in [0.0,10.0] maps to 0.0 in [-1.0,0.0]
\ 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 ;
There is less stack shuffling if you use origin and range instead of endpoints for intervals. (o = a1, r = a2-a1)
: 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 ;
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
Frink can exactly map to rational numbers so the mapping is round-trippable.
mapRange[s, a1, a2, b1, b2] := b1 + (s-a1)(b2-b1)/(a2-a1)
for a = 0 to 10
println["$a\t" + mapRange[a, 0, 10, -1, 0]]- Output:
0 -1 1 -9/10 (exactly -0.9) 2 -4/5 (exactly -0.8) 3 -7/10 (exactly -0.7) 4 -3/5 (exactly -0.6) 5 -1/2 (exactly -0.5) 6 -2/5 (exactly -0.4) 7 -3/10 (exactly -0.3) 8 -1/5 (exactly -0.2) 9 -1/10 (exactly -0.1) 10 0
Much more impressive, though, is that Frink is a powerful Computer Algebra System (CAS) and can symbolically invert the function so you can map t back to s:
The resulting inverse function is:
inverseMapRange[t, a1, a2, b1, b2] := a1 + a1 b1 (-1 b1 + b2)^-1 + -1 a2 b1 (-1 b1 + b2)^-1 + -1 a1 (-1 b1 + b2)^-1 t + a2 (-1 b1 + b2)^-1 tinclude "NSLog.incl"
local fn MapRange( s as double, a1 as double, a2 as double, b1 as double, b2 as double ) as double
end fn = b1+(s-a1)*(b2-b1)/(a2-a1)
NSInteger i
for i = 0 to 10
NSLog( @"%2d maps to %5.1f", i, fn MapRange( i, 0, 10, -1, 0 ) )
next
HandleEventsOutput:
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
Fōrmulæ programs are not textual, visualization/edition of programs is done showing/manipulating structures but not text. Moreover, there can be multiple visual representations of the same program. Even though it is possible to have textual representation —i.e. XML, JSON— they are intended for storage and transfer purposes more than visualization and edition.
Programs in Fōrmulæ are created/edited online in its website.
In this page you can see and run the program(s) related to this task and their results. You can also change either the programs or the parameters they are called with, for experimentation, but remember that these programs were created with the main purpose of showing a clear solution of the task, and they generally lack any kind of validation.
Solution
Part 1
Part 2
func mapRange(s:float, a1:float, a2:float, b1:float, b2:float) -> float :
return b1 + ((b2-b1)/(a2-a1))*(s-a1)
for i in 11 :
print( "%2d %+.1f" % [i,mapRange(i,0.0,10.0,-1.0,0.0)] )
- Output:
0 -1.0 1 -0.9 2 -0.8 3 -0.7 4 -0.6 5 -0.5 6 -0.4 7 -0.3 8 -0.2 9 -0.1 10 +0.0
Basic task
package main
import "fmt"
type rangeBounds struct {
b1, b2 float64
}
func mapRange(x, y rangeBounds, n float64) float64 {
return y.b1 + (n - x.b1) * (y.b2 - y.b1) / (x.b2 - x.b1)
}
func main() {
r1 := rangeBounds{0, 10}
r2 := rangeBounds{-1, 0}
for n := float64(0); n <= 10; n += 2 {
fmt.Println(n, "maps to", mapRange(r1, r2, n))
}
}
- Output:
0 maps to -1 2 maps to -0.8 4 maps to -0.6 6 maps to -0.4 8 maps to -0.19999999999999996 10 maps to 0
Extra credit
First, a function literal replaces the mapping function specified by the basic task. This allows a simpler parameter signature and also allows things to be precomputed for efficiency. newMapRange checks the direction of the first range and if it is decreasing, reverses both ranges. This simplifies an out-of-range check in the function literal. Also, the slope and intercept of the linear function are computed. This allows the range mapping to use the slope intercept formula which is computationally more efficient that the two point formula.
Second, ", ok" is a Go idiom. It takes advantage of Go's multiple return values and multiple assignment 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.
package main
import "fmt"
type rangeBounds struct {
b1, b2 float64
}
func newRangeMap(xr, yr rangeBounds) func(float64) (float64, bool) {
// normalize direction of ranges so that out-of-range test works
if xr.b1 > xr.b2 {
xr.b1, xr.b2 = xr.b2, xr.b1
yr.b1, yr.b2 = yr.b2, yr.b1
}
// compute slope, intercept
m := (yr.b2 - yr.b1) / (xr.b2 - xr.b1)
b := yr.b1 - m*xr.b1
// return function literal
return func(x float64) (y float64, ok bool) {
if x < xr.b1 || x > xr.b2 {
return 0, false // out of range
}
return m*x + b, true
}
}
func main() {
rm := newRangeMap(rangeBounds{0, 10}, rangeBounds{-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)
}
}
}
- 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
def mapRange(a1, a2, b1, b2, s) {
b1 + ((s - a1) * (b2 - b1)) / (a2 - a1)
}
(0..10).each { s ->
println(s + " in [0, 10] maps to " + mapRange(0, 10, -1, 0, s) + " in [-1, 0].")
}
- Output:
0 in [0, 10] maps to -1 in [-1, 0]. 1 in [0, 10] maps to -0.9 in [-1, 0]. 2 in [0, 10] maps to -0.8 in [-1, 0]. 3 in [0, 10] maps to -0.7 in [-1, 0]. 4 in [0, 10] maps to -0.6 in [-1, 0]. 5 in [0, 10] maps to -0.5 in [-1, 0]. 6 in [0, 10] maps to -0.4 in [-1, 0]. 7 in [0, 10] maps to -0.3 in [-1, 0]. 8 in [0, 10] maps to -0.2 in [-1, 0]. 9 in [0, 10] maps to -0.1 in [-1, 0]. 10 in [0, 10] maps to 0 in [-1, 0].
Rather than handling only floating point numbers, the mapping function takes any number implementing the Fractional typeclass, which in our example also includes exact Rational numbers.
import Data.Ratio
import Text.Printf (PrintfType, printf)
-- Map a value from the range [a1,a2] to the range [b1,b2]. We don't check
-- for empty ranges.
mapRange
:: Fractional a
=> (a, a) -> (a, a) -> a -> a
mapRange (a1, a2) (b1, b2) s = b1 + (s - a1) * (b2 - b1) / (a2 - a1)
main :: IO ()
main
-- Perform the mapping over floating point numbers.
= do
putStrLn "---------- Floating point ----------"
mapM_ (\n -> prtD n . mapRange (0, 10) (-1, 0) $ fromIntegral n) [0 .. 10]
-- Perform the same mapping over exact rationals.
putStrLn "---------- Rationals ----------"
mapM_ (\n -> prtR n . mapRange (0, 10) (-1, 0) $ n % 1) [0 .. 10]
where
prtD
:: PrintfType r
=> Integer -> Double -> r
prtD = printf "%2d -> %6.3f\n"
prtR
:: PrintfType r
=> Integer -> Rational -> r
prtR n x = printf "%2d -> %s\n" n (show x)
- Output:
---------- Floating point ---------- 0 -> -1.000 1 -> -0.900 2 -> -0.800 3 -> -0.700 4 -> -0.600 5 -> -0.500 6 -> -0.400 7 -> -0.300 8 -> -0.200 9 -> -0.100 10 -> 0.000 ---------- Rationals ---------- 0 -> (-1) % 1 1 -> (-9) % 10 2 -> (-4) % 5 3 -> (-7) % 10 4 -> (-3) % 5 5 -> (-1) % 2 6 -> (-2) % 5 7 -> (-3) % 10 8 -> (-1) % 5 9 -> (-1) % 10 10 -> 0 % 1
record Range(a, b)
# note, we force 'n' to be real, which means recalculation will
# be using real numbers, not integers
procedure remap (range1, range2, n : real)
if n < range2.a | n > range2.b then fail # n out of given range
return range1.a + (n - range2.a) * (range1.b - range1.a) / (range2.b - range2.a)
end
procedure range_string (range)
return "[" || range.a || ", " || range.b || "]"
end
procedure main ()
range1 := Range (0, 10)
range2 := Range (-1, 0)
# if i is out of range1, then 'remap' fails, so only valid changes are written
every i := -2 to 12 do {
if m := remap (range2, range1, i)
then write ("Value " || i || " in " || range_string (range1) ||
" maps to " || m || " in " || range_string (range2))
}
end
Icon does not permit the type declaration, as Unicon does. For Icon, replace 'remap' with:
- Output:
Value 0 in [0, 10] maps to -1.0 in [-1, 0] Value 1 in [0, 10] maps to -0.9 in [-1, 0] Value 2 in [0, 10] maps to -0.8 in [-1, 0] Value 3 in [0, 10] maps to -0.7 in [-1, 0] Value 4 in [0, 10] maps to -0.6 in [-1, 0] Value 5 in [0, 10] maps to -0.5 in [-1, 0] Value 6 in [0, 10] maps to -0.4 in [-1, 0] Value 7 in [0, 10] maps to -0.3 in [-1, 0] Value 8 in [0, 10] maps to -0.2 in [-1, 0] Value 9 in [0, 10] maps to -0.1 in [-1, 0] Value 10 in [0, 10] maps to 0.0 in [-1, 0]
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
Or
maprange=:2 :0
'a1 a2'=.m
'b1 b2'=.n
b1 + ((b2-b1)%a2-a1) * -&a1
)
NB. this version precomputes the scaling ratio
Or, more concisely:
maprange=:{{ ({.n) + (n%&(-/)m) * -&({.m) }}
Example use:
2 4 maprange 5 11 (2.718282 3 3.141592)
7.15485 8 8.42478
or
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
Required example:
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
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);
}
}
- 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 come from double math. Similar issues show even when using float types.
ES5
// Javascript doesn't have built-in support for ranges
// Insted we use arrays of two elements to represent ranges
var mapRange = function(from, to, s) {
return to[0] + (s - from[0]) * (to[1] - to[0]) / (from[1] - from[0]);
};
var range = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (var i = 0; i < range.length; i++) {
range[i] = mapRange([0, 10], [-1, 0], range[i]);
}
console.log(range);
- Output:
[-1, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.30000000000000004, -0.19999999999999996, -0.09999999999999998, 0]
Extra credit
Here we will use the ECMAScript 5 support for map and the _.range function from Underscore.js.
var mapRange = function(from, to, s) {
// mapRange expects ranges generated by _.range
var a1 = from[0];
var a2 = from[from.length - 1];
var b1 = to[0];
var b2 = to[to.length - 1];
return b1 + (s - a1) * (b2 - b1) / (a2 - a1);
};
// The range function is exclusive
var fromRange = _.range(0, 11);
var toRange = _.range(-1, 1);
// .map constructs a new array
fromRange = fromRange.map(function(s) {
return mapRange(fromRange, toRange, s);
});
console.log(fromRange);
- Output:
[-1, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.30000000000000004, -0.19999999999999996, -0.09999999999999998, 0]
ES6
Composing a solution from generic abstractions:
(() => {
'use strict';
// main :: IO ()
const main = () => {
// rangeMap :: (Num, Num) -> (Num, Num) -> Num -> Num
const rangeMap = (a, b) => s => {
const [a1, a2] = a;
const [b1, b2] = b;
// Scaling up an order, and then down, to bypass a potential,
// precision issue with negative numbers.
return (((((b2 - b1) * (s - a1)) / (a2 - a1)) * 10) + (10 * b1)) / 10;
};
const
mapping = rangeMap([0, 10], [-1, 0]),
xs = enumFromTo(0, 10),
ys = map(mapping, xs),
zs = map(approxRatio(''), ys);
const formatted = (x, m, r) => {
const
fract = showRatio(r),
[n, d] = splitOn('/', fract);
return justifyRight(2, ' ', x.toString()) + ' -> ' +
justifyRight(4, ' ', m.toString()) + ' = ' +
justifyRight(2, ' ', n.toString()) + '/' + d.toString();
};
console.log(
unlines(zipWith3(formatted, xs, ys, zs))
);
};
// GENERIC FUNCTIONS ----------------------------
// abs :: Num -> Num
const abs = Math.abs;
// Epsilon - > Real - > Ratio
// approxRatio :: Real -> Real -> Ratio
const approxRatio = eps => n => {
const
gcde = (e, x, y) => {
const _gcd = (a, b) => (b < e ? a : _gcd(b, a % b));
return _gcd(abs(x), abs(y));
},
c = gcde(Boolean(eps) ? eps : (1 / 10000), 1, abs(n)),
r = ratio(quot(abs(n), c), quot(1, c));
return {
type: 'Ratio',
n: r.n * signum(n),
d: r.d
};
};
// enumFromTo :: Int -> Int -> [Int]
const enumFromTo = (m, n) =>
Array.from({
length: 1 + n - m
}, (_, i) => m + i)
// gcd :: Int -> Int -> Int
const gcd = (x, y) => {
const
_gcd = (a, b) => (0 === b ? a : _gcd(b, a % b)),
abs = Math.abs;
return _gcd(abs(x), abs(y));
};
// justifyRight :: Int -> Char -> String -> String
const justifyRight = (n, cFiller, s) =>
n > s.length ? (
s.padStart(n, cFiller)
) : s;
// Returns Infinity over objects without finite length
// this enables zip and zipWith to choose the shorter
// argument when one is non-finite, like cycle, repeat etc
// length :: [a] -> Int
const length = xs => Array.isArray(xs) ? xs.length : Infinity;
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
// quot :: Int -> Int -> Int
const quot = (n, m) => Math.floor(n / m);
// ratio :: Int -> Int -> Ratio Int
const ratio = (x, y) => {
const go = (x, y) =>
0 !== y ? (() => {
const d = gcd(x, y);
return {
type: 'Ratio',
'n': quot(x, d), // numerator
'd': quot(y, d) // denominator
};
})() : undefined;
return go(x * signum(y), abs(y));
};
// showRatio :: Ratio -> String
const showRatio = nd =>
nd.n.toString() + '/' + nd.d.toString();
// signum :: Num -> Num
const signum = n => 0 > n ? -1 : (0 < n ? 1 : 0);
// splitOn :: String -> String -> [String]
const splitOn = (pat, src) =>
src.split(pat);
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
// zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
const zipWith3 = (f, xs, ys, zs) =>
Array.from({
length: Math.min(length(xs), length(ys), length(zs))
}, (_, i) => f(xs[i], ys[i], zs[i]));
// MAIN ---
return main();
})();
- Output:
0 -> -1 = -1/1 1 -> -0.9 = -9/10 2 -> -0.8 = -4/5 3 -> -0.7 = -7/10 4 -> -0.6 = -3/5 5 -> -0.5 = -1/2 6 -> -0.4 = -2/5 7 -> -0.3 = -3/10 8 -> -0.2 = -1/5 9 -> -0.1 = -1/10 10 -> 0 = 0/1
In jq, it is generally preferable to define functions as parameterized filters. In the present case, since the task calls for defining a map, the signature maprange(a;b), where a and b are the two ranges, is appropriate.
# The input is the value to be mapped.
# The ranges, a and b, should each be an array defining the
# left-most and right-most points of the range.
def maprange(a; b):
b[0] + (((. - a[0]) * (b[1] - b[0])) / (a[1] - a[0])) ;Example 1: a single value
6 | maprange([0,10]; [-1, 0])
produces:
-0.4
Example 2: a stream of values
range(0;11) | maprange([0,10]; [-1, 0])produces:
-1 -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.30000000000000004 -0.19999999999999996 -0.09999999999999998 0
Extra credit
To avoid repeating the same arithmetic, we shall define a filter that handles an array of values all at once, using an inner function and map/1:
def maprange_array(a; b):
def _helper(a0; b0; factor): b0 + (. - a0) * factor;
a[0] as $a | b[0] as $b | ((b[1] - b[0]) / (a[1] - a[0])) as $factor
| map(_helper( $a; $b; $factor) );Example:
[range(0;11)] | maprange_array([0,10]; [-1, 0])
function maprange(s, a, b)
a₁, a₂ = minimum(a), maximum(a)
b₁, b₂ = minimum(b), maximum(b)
return b₁ + (s - a₁) * (b₂ - b₁) / (a₂ - a₁)
end
@show maprange(6, 1:10, -1:0)
@show maprange(0:10, 0:10, -1:0)
- Output:
maprange(6, 1:10, -1:0) = -0.4444444444444444 maprange(0:10, 0:10, -1:0) = -1.0:0.1:0.0
f:{[a1;a2;b1;b2;s] b1+(s-a1)*(b2-b1)%(a2-a1)}
+(a; f[0;10;-1;0]'a:!11)
((0;-1.0)
(1;-0.9)
(2;-0.8)
(3;-0.7)
(4;-0.6)
(5;-0.5)
(6;-0.4)
(7;-0.3)
(8;-0.2)
(9;-0.1)
(10;0.0))
// version 1.0.6
class FloatRange(override val start: Float, override val endInclusive: Float) : ClosedRange<Float>
fun mapRange(range1: FloatRange, range2: FloatRange, value: Float): Float {
if (value !in range1) throw IllegalArgumentException("value is not within the first range")
if (range1.endInclusive == range1.start) throw IllegalArgumentException("first range cannot be single-valued")
return range2.start + (value - range1.start) * (range2.endInclusive - range2.start) / (range1.endInclusive - range1.start)
}
fun main(args: Array<String>) {
for (i in 0..10) {
val mappedValue = mapRange(FloatRange(0.0f, 10.0f), FloatRange(-1.0f, 0.0f), i.toFloat())
println(String.format("%2d maps to %+4.2f", i, mappedValue))
}
}
- 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
{def maprange
{lambda {:a0 :a1 :b0 :b1 :s}
{+ :b0 {/ {* {- :s :a0} {- :b1 :b0}} {- :a1 :a0}}}}}
-> maprange
{maprange 0 10 -1 0 5}
-> -0.5
{S.map {maprange 0 10 -1 0} {S.serie 0 10}}
->
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.30000000000000004
8 maps to -0.19999999999999996
9 maps to -0.09999999999999998
10 maps to 0
define map_range(
a1,
a2,
b1,
b2,
number
) => (decimal(#b1) + (decimal(#number) - decimal(#a1)) * (decimal(#b2) - decimal(#b1)) / (decimal(#a2) - decimal(#a1))) -> asstring(-Precision = 1)
with number in generateSeries(1,10) do {^
#number
': '
map_range( 0, 10, -1, 0, #number)
'<br />'
^}'
- Output:
0: -1.0 1: -0.9 2: -0.8 3: -0.7 4: -0.6 5: -0.5 6: -0.4 7: -0.3 8: -0.2 9: -0.1 10: 0.0
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]function map_range( a1, a2, b1, b2, s )
return b1 + (s-a1)*(b2-b1)/(a2-a1)
end
for i = 0, 10 do
print( string.format( "f(%d) = %f", i, map_range( 0, 10, -1, 0, i ) ) )
end
Using Class
module MapRange {
class Map {
private:
a, b, f
public:
value (x){
=.b+(x-.a)*.f
}
class:
module Map (.a,a2,.b,b2) {
if a2-.a=0 then error "wrong parameters"
.f<=(b2-.b)/(a2-.a)
}
}
m1=Map(0,10, -1, 0)
for i=0 to 10
Print i," maps to ";m1(i)
next
}
MapRangeUsing Lambda
module MapRange {
Map=lambda (a,a2,b,b2) -> {
if a2-a=0 then error "wrong parameters"
f=(b2-b)/(a2-a)
=lambda a,b,f (x)->b+(x-a)*f
}
m1=Map(0,10, -1, 0)
for i=0 to 10
Print i," maps to ";m1(i)
next
}
MapRangeSame output for both versions
- 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
Map:=proc(a1,a2,b1,b2,s);
return (b1+((s-a1)*(b2-b1)/(a2-a1)));
end proc;
for i from 0 to 10 do
printf("%a maps to ",i);
printf("%a\n",Map(0,10,-1,0,i));
end do;
Such a function is already built in
Rescale[#,{0,10},{-1,0}]&/@Range[0,10]
- Output:
{-1., -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.}
maprange(a, b, c, d) := buildq([e: ratsimp(('x - a)*(d - c)/(b - a) + c)],
lambda([x], e))$
f: maprange(0, 10, -1, 0);
MODULE MapRange;
FROM STextIO IMPORT WriteLn, WriteString;
FROM SWholeIO IMPORT WriteInt;
FROM SRealIO IMPORT WriteFixed;
VAR
I: INTEGER;
PROCEDURE MapRange(S, A1, A2, B1, B2: REAL): REAL;
BEGIN
RETURN B1 + (S - A1) * (B2 - B1) / (A2 - A1)
END MapRange;
BEGIN
FOR I := 0 TO 10 DO
WriteInt(I, 2);
WriteString(" maps to ");
WriteFixed(MapRange(FLOAT(I), 0., 10., -1., 0.), 1, 4);
WriteLn;
END;
END MapRange.
- 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.3 8 maps to -0.2 9 maps to -0.1 10 maps to 0.0
using System;
using System.Console;
module Maprange
{
Maprange(a : double * double, b : double * double, s : double) : double
{
def (a1, a2) = a; def (b1, b2) = b;
b1 + (((s - a1) * (b2 - b1))/(a2 - a1))
}
Main() : void
{
foreach (i in [0 .. 10])
WriteLine("{0, 2:f0} maps to {1:f1}", i, Maprange((0.0, 10.0), (-1.0, 0.0), i));
}
}
/* NetRexx */
options replace format comments java crossref savelog symbols nobinary
A = [ 0.0, 10.0 ]
B = [ -1.0, 0.0 ]
incr = 1.0
say 'Mapping ['A[0]',' A[1]'] to ['B[0]',' B[1]'] in increments of' incr':'
loop sVal = A[0] to A[1] by incr
say ' f('sVal.format(3, 3)') =' mapRange(A, B, sVal).format(4, 3)
end sVal
return
method mapRange(a = Rexx[], b = Rexx[], s_) public static
return mapRange(a[0], a[1], b[0], b[1], s_)
method mapRange(a1, a2, b1, b2, s_) public static
t_ = b1 + ((s_ - a1) * (b2 - b1) / (a2 - a1))
return t_
- Output:
Mapping [0.0, 10.0] to [-1.0, 0.0] in increments of 1.0: f( 0.000) = -1.000 f( 1.000) = -0.900 f( 2.000) = -0.800 f( 3.000) = -0.700 f( 4.000) = -0.600 f( 5.000) = -0.500 f( 6.000) = -0.400 f( 7.000) = -0.300 f( 8.000) = -0.200 f( 9.000) = -0.100 f( 10.000) = 0.000
import strformat
type FloatRange = tuple[s, e: float]
proc mapRange(a, b: FloatRange; s: float): float =
b.s + (s - a.s) * (b.e - b.s) / (a.e - a.s)
for i in 0..10:
let m = mapRange((0.0,10.0), (-1.0, 0.0), float(i))
echo &"{i:>2} maps to {m:4.1f}"
- 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.3 8 maps to -0.2 9 maps to -0.1 10 maps to 0.0
bundle Default {
class Range {
function : MapRange(a1:Float, a2:Float, b1:Float, b2:Float, s:Float) ~ Float {
return b1 + (s-a1)*(b2-b1)/(a2-a1);
}
function : Main(args : String[]) ~ Nil {
"Mapping [0,10] to [-1,0] at intervals of 1:"->PrintLine();
for(i := 0.0; i <= 10.0; i += 1;) {
IO.Console->Print("f(")->Print(i->As(Int))->Print(") = ")->PrintLine(MapRange(0.0, 10.0, -1.0, 0.0, i));
};
}
}
}- Output:
Mapping [0,10] to [-1,0] at intervals of 1: f(0) = -1 f(1) = -0.9 f(2) = -0.8 f(3) = -0.7 f(4) = -0.6 f(5) = -0.5 f(6) = -0.4 f(7) = -0.3 f(8) = -0.2 f(9) = -0.1 f(10) = 0
let map_range (a1, a2) (b1, b2) s =
b1 +. ((s -. a1) *. (b2 -. b1) /. (a2 -. a1))
let () =
print_endline "Mapping [0,10] to [-1,0] at intervals of 1:";
for i = 0 to 10 do
Printf.printf "f(%d) = %g\n" i (map_range (0.0, 10.0) (-1.0, 0.0) (float i))
done
- Output:
Mapping [0,10] to [-1,0] at intervals of 1: f(0) = -1 f(1) = -0.9 f(2) = -0.8 f(3) = -0.7 f(4) = -0.6 f(5) = -0.5 f(6) = -0.4 f(7) = -0.3 f(8) = -0.2 f(9) = -0.1 f(10) = 0
If range mapping is used in a heavy computational task we can reduce the number of calculations made using partial application and currying:
let map_range (a1, a2) (b1, b2) =
let v = (b2 -. b1) /. (a2 -. a1) in
function s ->
b1 +. ((s -. a1) *. v)
let () =
print_endline "Mapping [0,10] to [-1,0] at intervals of 1:";
let p = (map_range (0.0, 10.0) (-1.0, 0.0)) in
for i = 0 to 10 do
Printf.printf "f(%d) = %g\n" i (p (float i))
done
: mapRange(p1, p2, s)
s p1 first - p2 second p2 first - * p1 second p1 first - asFloat /
p2 first + ;- Output:
Interval newFromToStep(0, 10, 0.5) map(#[ mapRange([0, 10], [ -1, 0 ])]) println [-1, -0.95, -0.9, -0.85, -0.8, -0.75, -0.7, -0.65, -0.6, -0.55, -0.5, -0.45, -0.4, -0.35, -0.3, -0.25, -0.2, -0.15, -0.1, -0.05, 0]
Usage (e.g.): map([1,10],[0,5],8.)
map(r1,r2,x)=r2[1]+(x-r1[1])*(r2[2]-r2[1])/(r1[2]-r1[1])Program Map(output);
function MapRange(fromRange, toRange: array of real; value: real): real;
begin
MapRange := (value - fromRange[0]) * (toRange[1] - toRange[0]) /
(fromRange[1] - fromRange[0]) + toRange[0];
end;
var
i: integer;
begin
for i := 0 to 10 do
writeln(i: 2, ' maps to: ', MapRange([0.0, 10.0], [-1.0, 0.0], i): 5: 2);
end.
- 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
improvement doing many calculations
Tested with freepascal_32 2.6.4 .Pushing all data over the stack takes quite a long time. Precaltulating the scalefactor helps too.
Time relation doing 1E7 calculations
Org/ const / tMr
double : 267/177/107 .. 25/16/10
extended: 363/193/123 .. 30/15/10
Program Map(output);
type
real = double;
tRange = array [0..1] of real;
tMapRec = record
mrFrom,
mrTo: tRange;
mrScale: real
end;
function InitRange(rfrom, rTo: real): tRange;
begin
InitRange[0] := rfrom;
InitRange[1] := rTo;
end;
function InitMapRec(const fromRange, toRange: tRange): tMapRec;
begin
with InitMapRec do
begin
mrFrom := fromRange;
mrTo := toRange;
mrScale := (toRange[1] - toRange[0]) / (fromRange[1] - fromRange[0]);
end;
end;
function MapRecRange(const value: real; var MR: tMapRec): real;
begin
with MR do
MapRecRange := (value - mrFrom[0]) * mrScale + mrTo[0];
end;
function MapRange(const value: real; const fromRange, toRange: tRange): real;
begin
MapRange := (value - fromRange[0]) * (toRange[1] - toRange[0]) /
(fromRange[1] - fromRange[0]) + toRange[0];
end;
var
value: real;
rFrom, rTo: tRange;
mr: tMapRec;
i: longint;
begin
rFrom := InitRange(0, 10);
rTo := InitRange(-1, 0);
mr := InitMapRec(rFrom, rTo);
for i := 0 to 10 do
begin
value := i;
writeln(i: 4, ' maps to: ', MapRange(value, rFrom, rTo): 10: 6,
MapRecRange(value, mr): 10: 6);
end;
end.
- Output:
0 maps to: -1.000000 -1.000000 1 maps to: -0.900000 -0.900000 2 maps to: -0.800000 -0.800000 3 maps to: -0.700000 -0.700000 4 maps to: -0.600000 -0.600000 5 maps to: -0.500000 -0.500000 6 maps to: -0.400000 -0.400000 7 maps to: -0.300000 -0.300000 8 maps to: -0.200000 -0.200000 9 maps to: -0.100000 -0.100000 10 maps to: 0.000000 0.000000
program map(output);
(* Map range *)
var
i: integer;
function maprange(a1, a2, b1, b2: real; s: real): real;
begin
maprange := (s - a1) * (b2 - b1) / (a2 - a1) + b1;
end;
begin
for i := 0 to 10 do
writeln(i: 2, ' maps to: ', maprange(0.0, 10.0, -1.0, 0.0, i): 10);
end.
- Output:
Note. In Pascal-P4, the write and writeln procedures do not allow to display values of type real in a fixed-point representation.
0 maps to: -1.000e+00 1 maps to: -9.000e-01 2 maps to: -8.000e-01 3 maps to: -7.000e-01 4 maps to: -6.000e-01 5 maps to: -5.000e-01 6 maps to: -4.000e-01 7 maps to: -3.000e-01 8 maps to: -2.000e-01 9 maps to: -1.000e-01 10 maps to: 0.000e+00
##
function maprange(a, b: (real, real); s: real) := b[0] + (s - a[0]) * (b[1] - b[0]) / (a[1] - a[0]);
for var i := 0 to 10 do
Writeln(i, ' maps to ', maprange((0, 10), (-1, 0), i));
- 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
#!/usr/bin/perl -w
use strict ;
sub mapValue {
my ( $range1 , $range2 , $number ) = @_ ;
return ( $range2->[ 0 ] +
(( $number - $range1->[ 0 ] ) * ( $range2->[ 1 ] - $range2->[ 0 ] ) ) / ( $range1->[ -1 ]
- $range1->[ 0 ] ) ) ;
}
my @numbers = 0..10 ;
my @interval = ( -1 , 0 ) ;
print "The mapped value for $_ is " . mapValue( \@numbers , \@interval , $_ ) . " !\n" foreach @numbers ;
- Output:
The mapped value for 0 is -1 ! The mapped value for 1 is -0.9 ! The mapped value for 2 is -0.8 ! The mapped value for 3 is -0.7 ! The mapped value for 4 is -0.6 ! The mapped value for 5 is -0.5 ! The mapped value for 6 is -0.4 ! The mapped value for 7 is -0.3 ! The mapped value for 8 is -0.2 ! The mapped value for 9 is -0.1 ! The mapped value for 10 is 0 !
with javascript_semantics function map_range(atom s, a1, a2, b1, b2) return b1+(s-a1)*(b2-b1)/(a2-a1) end function for i=0 to 10 by 2 do printf(1,"%2d : %g\n",{i,map_range(i,0,10,-1,0)}) end for
- Output:
0 : -1 2 : -0.8 4 : -0.6 6 : -0.4 8 : -0.2 10 : 0
<?php
// Map range
function map_range($s, $a1, $a2, $b1, $b2) {
return ($s - $a1) * ($b2 - $b1) / ($a2 - $a1) + $b1;
}
for ($i = 0; $i <= 10; $i++) {
$mr = map_range($i, 0, 10, -1, 0); // To avoid too long line.
echo(str_pad($i, 2, ' ', STR_PAD_LEFT)
.' maps to: '
.str_pad(number_format($mr, 1, '.', ''), 4, ' ', STR_PAD_LEFT)
.PHP_EOL);
}
?>
- 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.3 8 maps to: -0.2 9 maps to: -0.1 10 maps to: 0.0
(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) ) )- Output:
-1.0 -0.9 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1 0.0
map: procedure options (main); /* 24/11/2011 */
declare (a1, a2, b1, b2) float;
declare d fixed decimal (3,1);
do d = 0 to 10 by 0.9, 10;
put skip edit ( d, ' maps to ', map(0, 10, -1, 0, d) ) (f(5,1), a, f(10,6));
end;
map: procedure (a1, a2, b1, b2, s) returns (float);
declare (a1, a2, b1, b2, s) float;
return (b1 + (s - a1)*(b2 - b1) / (a2 - a1) );
end map;
end map;- Output:
0.0 maps to -1.000000 0.9 maps to -0.910000 1.8 maps to -0.820000 2.7 maps to -0.730000 3.6 maps to -0.640000 4.5 maps to -0.550000 5.4 maps to -0.460000 6.3 maps to -0.370000 7.2 maps to -0.280000 8.1 maps to -0.190000 9.0 maps to -0.100000 9.9 maps to -0.010000 10.0 maps to 0.000000
local fmt = require "fmt"
local function map_range(a, b, s)
return b[1] + (s - a[1]) * (b:back() - b[1]) / (a:back() - a[1])
end
local a = range(0, 10)
local b = range(-1, 0)
for a as s do
local t = map_range(a, b, s)
fmt.print("%2d maps to % g", s, t)
end
- 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
function Group-Range
{
[CmdletBinding()]
[OutputType([PSCustomObject])]
Param
(
[Parameter(Mandatory=$true,
Position=0)]
[ValidateCount(2,2)]
[double[]]
$Range1,
[Parameter(Mandatory=$true,
Position=1)]
[ValidateCount(2,2)]
[double[]]
$Range2,
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
Position=2)]
[double]
$Map
)
Process
{
foreach ($number in $Map)
{
[PSCustomObject]@{
Index = $number
Mapping = $Range2[0] + ($number - $Range1[0]) * ($Range2[0] - $Range2[1]) / ($Range1[0] - $Range1[1])
}
}
}
}
0..10 | Group-Range (0,10) (-1,0)
- Output:
Index Mapping
----- -------
0 -1
1 -0.9
2 -0.8
3 -0.7
4 -0.6
5 -0.5
6 -0.4
7 -0.3
8 -0.2
9 -0.1
10 0
% map_range(+S, +A1, +A2, +B1, +B2, -R)
map_range(S, A1, A2, B1, B2, R) :-
R is B1 + (S - A1) * (B2 - B1) / (A2 - A1).
% bucle principal
run :-
forall(between(0, 10, I),
( map_range(I, 0, 10, -1, 0, R),
format("~w maps to ~1f~n", [I, R])
)).
- 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.3 8 maps to -0.2 9 maps to -0.1 10 maps to 0.0
from fractions import Fraction
def map_range(a, b, s):
(a1, a2), (b1, b2) = a, b
return b1 + ((s - a1) * (b2 - b1) / (a2 - a1))
for s in range(11):
print(f"{s:2g} maps to {map_range((0, 10), (-1, 0), s):g}")
print()
# Because of Python's strict, dynamic typing rules for numbers, the same
# function can give answers as fractions.
for s in range(11):
print(f"{s:2g} maps to {map_range((0, 10), (-1, 0), Fraction(s))}")
- 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 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
As Quackery does not support reals (or floating point), the function takes the argument s as a decimal string, and returns the result, t as a rational number.
[ $ "bigrat.qky" loadfile ] now!
[ do over -
2swap
do over -
unrot
dip [ $->v drop ]
n->v v-
rot n->v v/
rot n->v v*
rot n->v v+ ] is maprange ( $ [ [ --> n/d )
$ "0 1 2 3 4 5 6 7 8 9 10"
nest$
witheach
[ dup echo$ say " maps to "
' [ 0 10 ] ' [ -1 0 ] maprange
7 point$ echo$ cr ]- 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
tRange <- function(aRange, bRange, s)
{
#Guard clauses. We could write some proper error messages, but this is all we really need.
stopifnot(length(aRange) == 2, length(bRange) == 2,
is.numeric(aRange), is.numeric(bRange), is.numeric(s),
s >= aRange[1], s <= aRange[2])
bRange[1] + ((s - aRange[1]) * (bRange[2] - bRange[1])) / (aRange[2] - aRange[1])
}
data.frame(s = 0:10, t = sapply(0:10, tRange, aRange = c(0, 10), bRange = c(-1, 0)))
- Output:
s t 1 0 -1.0 2 1 -0.9 3 2 -0.8 4 3 -0.7 5 4 -0.6 6 5 -0.5 7 6 -0.4 8 7 -0.3 9 8 -0.2 10 9 -0.1 11 10 0.0
#lang racket
(define (make-range-map a1 a2 b1 b2)
;; returns a mapping function, doing computing the differences in
;; advance so it's fast
(let ([a (- a2 a1)] [b (- b2 b1)])
(λ(s) (exact->inexact (+ b1 (/ (* (- s a1) b) a))))))
(define map (make-range-map 0 10 -1 0))
(for ([i (in-range 0 11)]) (printf "~a --> ~a\n" i (map i)))
- Output:
0 --> -1.0 1 --> -0.9 2 --> -0.8 3 --> -0.7 4 --> -0.6 5 --> -0.5 6 --> -0.4 7 --> -0.3 8 --> -0.2 9 --> -0.1 10 --> 0.0
(formerly Perl 6) Return a closure that does the mapping without have to supply the ranges every time.
sub getmapper(Range $a, Range $b) {
my ($a1, $a2) = $a.bounds;
my ($b1, $b2) = $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)"}
- 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
let map_range = ((a1, a2), (b1, b2), s) => {
b1 +. ((s -. a1) *. (b2 -. b1) /. (a2 -. a1))
}
Js.log("Mapping [0,10] to [-1,0] at intervals of 1:")
for i in 0 to 10 {
Js.log("f(" ++ Js.String.make(i) ++ ") = " ++
Js.String.make(map_range((0.0, 10.0), (-1.0, 0.0), float(i))))
}<!DOCTYPE html>
<html>
<head>
<title>ReScript: Map_range</title>
<meta charset="UTF-8"/>
<style rel="stylesheet" type="text/css">
body { color:#EEE; background-color:#888; }
</style>
<script>var exports = {};</script>
<script src="./maprange.js"></script>
</head>
<body>
</body>
</html>
- Output:
Mapping [0,10] to [-1,0] at intervals of 1: f(0) = -1 f(1) = -0.9 f(2) = -0.8 f(3) = -0.7 f(4) = -0.6 f(5) = -0.5 f(6) = -0.4 f(7) = -0.30000000000000004 f(8) = -0.19999999999999996 f(9) = -0.09999999999999998 f(10) = 0
(The first three REXX versions don't differ idiomatically that much, but differ mostly just in style.)
The first three versions support different increments (the inc variable) and an A range that is decreasing in values
(that is, the 2nd number [usually the high] in the range is less than the first number in the range [usually the low]). Also,
the BY (increment) is automatically adjusted (either upwards or downwards). Also,
both sets of numbers in the
output are aligned (vertically).
version 1
/*REXX program maps and displays a range of numbers from one range to another range.*/
rangeA = 0 10 /*or: rangeA = ' 0 10 ' */
rangeB = -1 0 /*or: rangeB = " -1 0 " */
parse var rangeA L H
inc= 1
do j=L to H by inc * (1 - 2 * sign(H<L) ) /*BY: either +inc or -inc */
say right(j, 9) ' maps to ' mapR(rangeA, rangeB, j)
end /*j*/
exit /*stick a fork in it, we're done.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
mapR: procedure; parse arg a1 a2,b1 b2,s;$=b1+(s-a1)*(b2-b1)/(a2-a1);return left('',$>=0)$
- 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
This version demonstrates an increment (inc) of 1/2 instead of the usual unity.
Note that this REXX version also uses a different rangeA numbers (they are reversed).
/*REXX program maps and displays a range of numbers from one range to another range.*/
rangeA = 10 0 /*or: rangeA = ' 0 10 ' */
rangeB = -1 0 /*or: rangeB = " -1 0 " */
parse var rangeA L H
inc= 1/2
do j=L to H by inc * (1 - 2 * sign(H<L) ) /*BY: either +inc or -inc */
say right(j, 9) ' maps to ' mapR(rangeA, rangeB, j)
end /*j*/
exit /*stick a fork in it, we're done.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
mapR: procedure; parse arg a1 a2,b1 b2,s;$=b1+(s-a1)*(b2-b1)/(a2-a1);return left('',$>=0)$
- output:
0 maps to 0
0.5 maps to -0.05
1.0 maps to -0.1
1.5 maps to -0.15
2.0 maps to -0.2
2.5 maps to -0.25
3.0 maps to -0.3
3.5 maps to -0.35
4.0 maps to -0.4
4.5 maps to -0.45
5.0 maps to -0.5
5.5 maps to -0.55
6.0 maps to -0.6
6.5 maps to -0.65
7.0 maps to -0.7
7.5 maps to -0.75
8.0 maps to -0.8
8.5 maps to -0.85
9.0 maps to -0.9
9.5 maps to -0.95
10.0 maps to -1
version 3
This REXX version used a function that calculates and also displays the range mapping.
/*REXX program maps and displays a range of numbers from one range to another range.*/
rangeA = 0 10
rangeB = -1 0
inc = 1
call mapR rangeA, rangeB, inc
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
mapR: procedure; parse arg a1 a2, b1 b2, inc /* [↓] BY is either +inc or -inc.*/
do s=a1 to a2 by inc * (1 - 2 * sign(a2 < a1) )
t= b1 + (s-a1) * (b2-b1) / (a2-a1)
say right(s, 9) ' maps to' left('', t>=0) t
end /*s*/
return /* [↑] LEFT··· aligns non─negative #'s*/
- output is identical to the 1st REXX version.
Version 4
/*REXX program maps a number from one range to another range. */
/* 31.10.2013 Walter Pachl */
/* 'translated' from an older version 1 without using Procedure */
do j=0 to 10
say right(j,3) ' maps to ' mapRange(0,10,-1,0,j)
end
exit
/*──────────────────────────────────MAPRANGE subroutine─────────────────*/
mapRange: return arg(3)+(arg(5)-arg(1))*(arg(4)-arg(3))/(arg(2)-arg(1))
/* Arguments are arg a1,a2,b1,b2,x */
- 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
# Project : Map range
decimals(1)
al = 0
ah = 10
bl = -1
bh = 0
for n = 0 to 10
see "" + n + " maps to " + maprange(al, bl, n) + nl
next
func maprange(al, bl, s)
return bl + (s - al) * (bh - bl) / (ah - al)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
Classical way
Local variables, processing the formula one step after the other
« → a b s
« b ΔLIST EVAL a ΔLIST EVAL /
s a HEAD - * b HEAD +
» » 'MAP→' STO
Using stack and list features together
« OVER 2 GET 4 ROLLD @ put b1 at stack level 4 UNROT + + ΔLIST @ get all the diffs EVAL NIP SWAP / * + @ do the math » 'MAP→' STO
Using the Calculator Algebraic System (CAS)
« 'X' STO
R→C EVAL DROITE EQ→ EVAL
NIP 'X' PURGE
» 'MAP→' STO
{ 0 10 } { -1 0 } 5 MAP→
- Output:
1: -.5
def map_range(a, b, s)
af, al, bf, bl = a.first, a.last, b.first, b.last
bf + (s - af)*(bl - bf).quo(al - af)
end
(0..10).each{|s| puts "%s maps to %g" % [s, map_range(0..10, -1..0, s)]}
Numeric#quo does floating point division.
- 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
To use rational arithmetic, delete s *= 1.0 and either require 'rational', or use Ruby 1.9 (which has Rational in the core library).
(0..10).each do |s|
puts "%s maps to %s" % [s, map_range(0..10, -1..0, s)]
end
- Output:
using rational arithmetic
0 maps to -1/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/1
use std::ops::{Add, Sub, Mul, Div};
fn map_range<T: Copy>(from_range: (T, T), to_range: (T, T), s: T) -> T
where T: Add<T, Output=T> +
Sub<T, Output=T> +
Mul<T, Output=T> +
Div<T, Output=T>
{
to_range.0 + (s - from_range.0) * (to_range.1 - to_range.0) / (from_range.1 - from_range.0)
}
fn main() {
let input: Vec<f64> = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
let result = input.into_iter()
.map(|x| map_range((0.0, 10.0), (-1.0, 0.0), x))
.collect::<Vec<f64>>();
print!("{:?}", result);
}- Output:
[-1, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.30000000000000004, -0.19999999999999996, -0.09999999999999998, 0]
def mapRange(a1:Double, a2:Double, b1:Double, b2:Double, x:Double):Double=b1+(x-a1)*(b2-b1)/(a2-a1)
for(i <- 0 to 10)
println("%2d in [0, 10] maps to %5.2f in [-1, 0]".format(i, mapRange(0,10, -1,0, i)))- Output:
0 in [0, 10] maps to -1,00 in [-1, 0] 1 in [0, 10] maps to -0,90 in [-1, 0] 2 in [0, 10] maps to -0,80 in [-1, 0] 3 in [0, 10] maps to -0,70 in [-1, 0] 4 in [0, 10] maps to -0,60 in [-1, 0] 5 in [0, 10] maps to -0,50 in [-1, 0] 6 in [0, 10] maps to -0,40 in [-1, 0] 7 in [0, 10] maps to -0,30 in [-1, 0] 8 in [0, 10] maps to -0,20 in [-1, 0] 9 in [0, 10] maps to -0,10 in [-1, 0] 10 in [0, 10] maps to 0,00 in [-1, 0]
$ include "seed7_05.s7i";
include "float.s7i";
const func float: mapRange (in float: a1, in float: a2, in float: b1, in float: b2, ref float: s) is
return b1 + (s-a1)*(b2-b1)/(a2-a1);
const proc: main is func
local
var integer: number is 0;
begin
writeln("Mapping [0,10] to [-1,0] at intervals of 1:");
for number range 0 to 10 do
writeln("f(" <& number <& ") = " <& mapRange(0.0, 10.0, -1.0, 0.0, flt(number)) digits 1);
end for;
end func;- Output:
Mapping [0,10] to [-1,0] at intervals of 1: f(0) = -1.0 f(1) = -0.9 f(2) = -0.8 f(3) = -0.7 f(4) = -0.6 f(5) = -0.5 f(6) = -0.4 f(7) = -0.3 f(8) = -0.2 f(9) = -0.1 f(10) = 0.0
func map_range(a, b, x) {
var (a1, a2, b1, b2) = (a.bounds, b.bounds);
x-a1 * b2-b1 / a2-a1 + b1;
}
var a = 0..10;
var b = -1..0;
for x in a {
say "#{x} maps to #{map_range(a, b, x)}";
}- 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
As a structured script.
#!/usr/local/bin/spar
pragma annotate( summary, "mapping" )
@( description, "The task is to write a function/subroutine/... that takes" )
@( description, "two ranges and a real number, and returns the mapping of" )
@( description, "the real number from the first to the second range. Use" )
@( description, "this function to map values from the range [0, 10] to the" )
@( description, "range [-1, 0]." )
@( see_also, "http://rosettacode.org/wiki/Map_range" )
@( author, "Ken O. Burtch" );
pragma license( unrestricted );
pragma restriction( no_external_commands );
procedure mapping is
type first_range is new float;
type second_range is new float;
-- Spar doesn't implement ranges so we'll use constants
first_range_first : constant first_range := 0.0;
first_range_last : constant first_range := 10.0;
second_range_first : constant second_range := -1.0;
second_range_last : constant second_range := 0.0;
function translate (first_range_value : first_range) return second_range is
b1 : constant float := float( second_range_first );
b2 : constant float := float( second_range_last );
a1 : constant float := float( first_range_first );
a2 : constant float := float( first_range_last );
result : float;
begin
result := b1 + (float (first_range_value) - a1) * (b2 - b1) / (a2 - a1);
return second_range(result);
end translate;
function translate_back (second_range_value : second_range) return first_range is
b1 : constant float := float (first_range_first);
b2 : constant float := float (first_range_last);
a1 : constant float := float (second_range_first);
a2 : constant float := float (second_range_last);
result : float;
begin
result := b1 + (float (second_range_value) - a1) * (b2 - b1) / (a2 - a1);
return first_range (result);
end translate_back;
test_value : first_range := first_range_first;
translated_value : second_range;
translated_back_value : first_range;
begin
loop
translated_value := translate( test_value );
translated_back_value := translate_back( translated_value );
? strings.image(test_value) & " maps to: "
& strings.image (translated_value);
? strings.image(translated_value) & " maps back to: "
& strings.image (translated_back_value);
exit when test_value = first_range_last;
test_value := @ + 1.0;
end loop;
end mapping;- Output:
$ spar mapping.sp 0.0 maps to: -1.00000000000000E+00 -1.00000000000000E+00 maps back to: 0.00000000000000E+00 1.00000000000000E+00 maps to: -9.00000000000000E-01 -9.00000000000000E-01 maps back to: 1.00000000000000E+00 2.00000000000000E+00 maps to: -8.00000000000000E-01 -8.00000000000000E-01 maps back to: 2.00000000000000E+00 3.00000000000000E+00 maps to: -7.00000000000000E-01 -7.00000000000000E-01 maps back to: 3.00000000000000E+00 4.00000000000000E+00 maps to: -6.00000000000000E-01 -6.00000000000000E-01 maps back to: 4.00000000000000E+00 5.00000000000000E+00 maps to: -5.00000000000000E-01 -5.00000000000000E-01 maps back to: 5.00000000000000E+00 6.00000000000000E+00 maps to: -4.00000000000000E-01 -4.00000000000000E-01 maps back to: 6.00000000000000E+00 7.00000000000000E+00 maps to: -3.00000000000000E-01 -3.00000000000000E-01 maps back to: 7.00000000000000E+00 8.00000000000000E+00 maps to: -2.00000000000000E-01 -2.00000000000000E-01 maps back to: 8.00000000000000E+00 9.00000000000000E+00 maps to: -1.00000000000000E-01 -1.00000000000000E-01 maps back to: 9.00000000000000E+00 1.00000000000000E+01 maps to: 0.00000000000000E+00 0.00000000000000E+00 maps back to: 1.00000000000000E+01
The following program will map a variable to a new variable. It accepts if and in conditions.
program define maprange
version 15.1
syntax varname(numeric) [if] [in], ///
from(numlist min=2 max=2) to(numlist min=2 max=2) ///
GENerate(name) [REPLACE]
tempname a b c d h
sca `a'=`:word 1 of `from''
sca `b'=`:word 2 of `from''
sca `c'=`:word 1 of `to''
sca `d'=`:word 2 of `to''
sca `h'=(`d'-`c')/(`b'-`a')
cap confirm variable `generate'
if "`replace'"=="replace" & !_rc {
qui replace `generate'=(`varlist'-`a')*`h'+`c' `if' `in'
}
else {
if "`replace'"=="replace" {
di in gr `"(note: variable `generate' not found)"'
}
qui gen `generate'=(`varlist'-`a')*`h'+`c' `if' `in'
}
endExample
clear
set obs 11
gen x=_n-1
maprange x if mod(x,2)==0, gen(y) from(0 10) to(-10 10)
maprange x if mod(x,2)!=0, gen(y) from(0 10) to(-100 100) replace
listOutput
+----------+
| x y |
|----------|
1. | 0 -10 |
2. | 1 -80 |
3. | 2 -6 |
4. | 3 -40 |
5. | 4 -2 |
|----------|
6. | 5 0 |
7. | 6 2 |
8. | 7 40 |
9. | 8 6 |
10. | 9 80 |
|----------|
11. | 10 10 |
+----------+
import Foundation
func mapRanges(_ r1: ClosedRange<Double>, _ r2: ClosedRange<Double>, to: Double) -> Double {
let num = (to - r1.lowerBound) * (r2.upperBound - r2.lowerBound)
let denom = r1.upperBound - r1.lowerBound
return r2.lowerBound + num / denom
}
for i in 0...10 {
print(String(format: "%2d maps to %5.2f", i, mapRanges(0...10, -1...0, to: Double(i))))
}- 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
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)}
}Demonstration (using a curried alias to bind the ranges mapped from and to):
interp alias {} demomap {} rangemap {0 10} {-1 0}
for {set i 0} {$i <= 10} {incr i} {
puts [format "%2d -> %5.2f" $i [demomap $i]]
}- 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
As expected, we get floating-point rounding errors, but that's a topic for another task.
function mapRange(
a: number[],
b: number[],
s: number
): number {
return b[0] + (s-a[0]) * (b[1]-b[0]) / (a[1]-a[0]);
}
const a = [0, 10];
const b = [-1, 0];
for (let s = 0; s < 11; s++) {
console.log(`${s} -> ${mapRange(a, b, s)}`);
}- Output:
0 -> -1 1 -> -0.9 2 -> -0.8 3 -> -0.7 4 -> -0.6 5 -> -0.5 6 -> -0.4 7 -> -0.30000000000000004 8 -> -0.19999999999999996 9 -> -0.09999999999999998 10 -> 0
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.
#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.- 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
f(("a1","a2"),("b1","b2")) "s" = ...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.
double map_range(double s, int a1, int a2, int b1, int b2) {
return b1+(s-a1)*(b2-b1)/(a2-a1);
}
void main() {
for (int s = 0; s < 11; s++){
print("%2d maps to %5.2f\n", s, map_range(s, 0, 10, -1, 0));
}
}- 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
struct Range_Bounds {
b1 f64
b2 f64
}
fn map_range(x Range_Bounds, y Range_Bounds, n f64) f64 {
return y.b1 + (n - x.b1) * (y.b2 - y.b1) / (x.b2 - x.b1)
}
fn main() {
r1 := Range_Bounds{0, 10}
r2 := Range_Bounds{-1, 0}
for n := 0; n <= 10; n += 2 {
println("${n} maps to ${map_range(r1, r2, n):.2}")
}
}- Output:
0 maps to -1 2 maps to -0.8 4 maps to -0.6 6 maps to -0.4 8 maps to -0.2 10 maps to 0
let mapRange r1 r2 s =>
+
(at r2 0)
(/
(*
(-
s
(at r1 0)
)
(-
(at r2 1)
(at r2 0)
)
)
(-
(at r1 1)
(at r1 0)
)
)
;
let s => import 'stream';
let str => import 'strings';
s.range 10
-> s.map (@ enum v => [v; mapRange [0; 10] [-1; 0] v])
-> s.map (@ print v => str.format '{} -> {}' (at v 0) (at v 1) -- io.writeln io.stdout)
-> s.drain
;- Output:
0 -> -1 1 -> -0.9 2 -> -0.8 3 -> -0.7 4 -> -0.6 5 -> -0.5 6 -> -0.4 7 -> -0.3 8 -> -0.2 9 -> -0.1
import "./fmt" for Fmt
var mapRange = Fn.new { |a, b, s| b.from + (s - a.from) * (b.to - b.from) / (a.to - a.from) }
var a = 0..10
var b = -1..0
for (s in a) {
var t = mapRange.call(a, b, s)
Fmt.print("$2d maps to $ h", s, t)
}- 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
include c:\cxpl\codes;
func real Map(A1, A2, B1, B2, S);
real A1, A2, B1, B2, S;
return B1 + (S-A1)*(B2-B1)/(A2-A1);
int I;
[for I:= 0 to 10 do
[if I<10 then ChOut(0, ^ ); IntOut(0, I);
RlOut(0, Map(0., 10., -1., 0., float(I)));
CrLf(0);
];
]- Output:
0 -1.00000 1 -0.90000 2 -0.80000 3 -0.70000 4 -0.60000 5 -0.50000 6 -0.40000 7 -0.30000 8 -0.20000 9 -0.10000 10 0.00000
!ys-0
defn main():
each s (0 .. 10):
t =: map-range(0 10 -1 0 s)
say: "$s -> $t"
defn map-range(a1 a2 b1 b2 s):
num =: (s - a1) * (b2 - b1)
b1 +: num / (a2 - a1)- Output:
$ ys map-range.ys 0 -> -1 1 -> -0.9 2 -> -0.8 3 -> -0.7 4 -> -0.6 5 -> -0.5 6 -> -0.4 7 -> -0.30000000000000004 8 -> -0.19999999999999996 9 -> -0.09999999999999998 10 -> 0
fcn mapRange([(a1,a2)], [(b1,b2)], s) // a1a2 is List(a1,a2)
{ b1 + ((s - a1) * (b2 - b1) / (a2 - a1)) }
r1:=T(0.0, 10.0); r2:=T(-1.0, 0.0);
foreach s in ([0.0 .. 10]){
"%2d maps to %5.2f".fmt(s,mapRange(r1,r2, s)).println();
}- 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
- Programming Tasks
- Solutions by Programming Task
- 11l
- 6502 Assembly
- ACL2
- Action!
- Action! Tool Kit
- Ada
- ALGOL 68
- Amazing Hopper
- AppleScript
- Arturo
- Asymptote
- AutoHotkey
- AWK
- Axiom
- BASIC
- ANSI BASIC
- BASIC256
- BBC BASIC
- Commodore BASIC
- Chipmunk Basic
- Craft Basic
- FreeBASIC
- GW-BASIC
- Gambas
- IS-BASIC
- Liberty BASIC
- MSX Basic
- PureBasic
- QBasic
- QuickBASIC
- QB64
- RapidQ
- Run BASIC
- Yabasic
- Bc
- BQN
- Bracmat
- C
- C sharp
- C++
- Clojure
- COBOL
- CoffeeScript
- Common Lisp
- Crystal
- D
- Dart
- Delphi
- DuckDB
- EasyLang
- EchoLisp
- Elixir
- Emacs Lisp
- Erlang
- ERRE
- Euphoria
- F Sharp
- Factor
- Fantom
- Forth
- Fortran
- Frink
- FutureBasic
- Fōrmulæ
- GDScript
- Go
- Groovy
- Haskell
- Icon
- Unicon
- J
- Java
- JavaScript
- Underscore.js
- Jq
- Julia
- K
- Kotlin
- Lambdatalk
- Lasso
- Logo
- Lua
- M2000 Interpreter
- Maple
- Mathematica
- Wolfram Language
- Maxima
- Modula-2
- Nemerle
- NetRexx
- Nim
- Objeck
- OCaml
- Oforth
- PARI/GP
- Pascal
- Free Pascal
- Pascal-P
- PascalABC.NET
- Perl
- Phix
- PHP
- PicoLisp
- PL/I
- Pluto
- Pluto-fmt
- PowerShell
- Prolog
- Python
- Quackery
- R
- Racket
- Raku
- ReScript
- REXX
- Ring
- RPL
- Ruby
- Rust
- Scala
- Seed7
- Sidef
- SparForte
- Stata
- Swift
- Tcl
- TypeScript
- Ursala
- Vala
- V (Vlang)
- WDTE
- Wren
- Wren-fmt
- XPL0
- YAMLScript
- Zkl
- Pages with too many expensive parser function calls


