diff options
Diffstat (limited to 'Data/Libraries/Penlight/tests/test-__vector.lua')
-rw-r--r-- | Data/Libraries/Penlight/tests/test-__vector.lua | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/Data/Libraries/Penlight/tests/test-__vector.lua b/Data/Libraries/Penlight/tests/test-__vector.lua new file mode 100644 index 0000000..e040564 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-__vector.lua @@ -0,0 +1,123 @@ +---- deriving specialized classes from List +-- illustrating covariance of List methods +local test = require 'pl.test' +local class = require 'pl.class' +local types = require 'pl.types' +local operator = require 'pl.operator' +local List = require 'pl.List' + +local asserteq = test.asserteq + +class.Vector(List) + + +function Vector.range (x1,x2,delta) + return Vector(List.range(x1,x2,delta)) +end + +local function vbinop (op,v1,v2,scalar) + if not Vector:class_of(v1) then + v2, v1 = v1, v2 + end + if type(v2) ~= 'table' then + return v1:map(op,v2) + else + if scalar then error("operation not permitted on two vectors",3) end + if #v1 ~= #v2 then error("vectors have different lengths",3) end + return v1:map2(op,v2) + end +end + +function Vector.__add (v1,v2) + return vbinop(operator.add,v1,v2) +end + +function Vector.__sub (v1,v2) + return vbinop(operator.sub,v1,v2) +end + +function Vector.__mul (v1,v2) + return vbinop(operator.mul,v1,v2,true) +end + +function Vector.__div (v1,v2) + return vbinop(operator.div,v1,v2,true) +end + +function Vector.__unm (v) + return v:map(operator.unm) +end + +Vector:catch(List.default_map_with(math)) + +v = Vector() + +assert(v:is_a(Vector)) +assert(Vector:class_of(v)) + +v:append(10) +v:append(20) +asserteq(1+v,v+1) + +-- covariance: the inherited Vector.map returns a Vector +asserteq(List{1,2} + v:map '2*_',{21,42}) + +u = Vector{1,2} + +asserteq(v + u,{11,22}) +asserteq(v - u,{9,18}) +asserteq (v - 1, {9,19}) +asserteq(2 * v, {20,40}) +-- print(v * v) -- throws error: not permitted +-- print(v + Vector{1,2,3}) -- throws error: different lengths +asserteq(2*v + u, {21,42}) +asserteq(-v, {-10,-20}) + +-- Vector.slice returns the Right Thing due to covariance +asserteq( + Vector.range(0,1,0.1):slice(1,3)+1, + {1,1.1,1.2}, + 1e-8) + +u:transform '_+1' +asserteq(u,{2,3}) + +u = Vector.range(0,1,0.1) +asserteq( + u:map(math.sin), + {0,0.0998,0.1986,0.2955,0.3894,0.4794,0.5646,0.6442,0.7173,0.7833,0.8414}, +0.001) + +-- unknown Vector methods are assumed to be math.* functions +asserteq(Vector{-1,2,3,-4}:abs(),Vector.range(1,4)) + +local R = Vector.range + +-- concatenating two Vectors returns another vector (covariance again) +-- note the operator precedence here... +asserteq(( + R(0,1,0.1)..R(1.2,2,0.2)) + 1, + {1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,2.2,2.4,2.6,2.8,3}, + 1e-8) + + +class.Strings(List) + +Strings:catch(List.default_map_with(string)) + +ls = Strings{'one','two','three'} +asserteq(ls:upper(),{'ONE','TWO','THREE'}) +asserteq(ls:sub(1,2),{'on','tw','th'}) + +-- all map operations on specialized lists +-- results in another list of that type! This isn't necessarily +-- what you want. +local sizes = ls:map '#' +asserteq(sizes, {3,3,5}) +asserteq(types.type(sizes),'Strings') +asserteq(sizes:is_a(Strings),true) +sizes = Vector:cast(sizes) +asserteq(types.type(sizes),'Vector') +asserteq(sizes+1,{4,4,6}) + + |