Add std.Vector wrapper class (by onox).
This commit is contained in:
parent
7192bd1feb
commit
33a9b73b5c
2 changed files with 305 additions and 0 deletions
130
Nasal/std/Vector.nas
Normal file
130
Nasal/std/Vector.nas
Normal file
|
@ -0,0 +1,130 @@
|
|||
# Copyright (C) 2014 onox
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
var min = func(a, b) { a < b ? a : b }
|
||||
var max = func(a, b) { a > b ? a : b }
|
||||
|
||||
var Vector = {
|
||||
|
||||
new: func (vector=nil) {
|
||||
var m = {
|
||||
parents: [Vector]
|
||||
};
|
||||
if (vector == nil) {
|
||||
vector = [];
|
||||
}
|
||||
m.vector = vector;
|
||||
return m;
|
||||
},
|
||||
|
||||
size: func {
|
||||
# Return the number of items in the vector
|
||||
|
||||
return size(me.vector);
|
||||
},
|
||||
|
||||
clear: func {
|
||||
# Remove all items from the vector, resulting in an empty vector
|
||||
|
||||
me.vector = [];
|
||||
},
|
||||
|
||||
append: func (item) {
|
||||
# Append the given item at the end of the vector
|
||||
|
||||
append(me.vector, item);
|
||||
},
|
||||
|
||||
extend: func (other_vector) {
|
||||
# Extend the vector with another vector, appending the items of
|
||||
# the other vector to this vector
|
||||
|
||||
me.vector = me.vector ~ other_vector;
|
||||
},
|
||||
|
||||
insert: func (index, item) {
|
||||
# Insert the given item at the given index before the old item
|
||||
# at that index. Any index greater than n-1 where n is the size of
|
||||
# the vector will append the given item at the end. Any index smaller
|
||||
# or equal to -n will insert the item at the beginning of the vector.
|
||||
#
|
||||
# For example, if the vector contains ["a", "b", "c"], then the
|
||||
# following operations can happen:
|
||||
#
|
||||
# insert(0, "d") => ["d", "a", "b", "c"]
|
||||
# insert(2, "e") => ["a", "b", "e", "c"]
|
||||
# insert(3, "f") => ["a", "b", "c", "f"]
|
||||
# insert(-3, "g") => ["f", "a", "b", "c"]
|
||||
|
||||
index = min(index, me.size());
|
||||
if (index < 0) {
|
||||
index = max(0, me.size() + index);
|
||||
}
|
||||
me.vector = subvec(me.vector, 0, index) ~ [item] ~ subvec(me.vector, index);
|
||||
},
|
||||
|
||||
pop: func (index=nil) {
|
||||
# Remove and return the item at the given index. If index
|
||||
# is not given, then it will remove and return the last item.
|
||||
# A negative index represents the position from the end.
|
||||
#
|
||||
# For example, if the vector contains ["a", "b", "c"], then the
|
||||
# following operations can happen:
|
||||
#
|
||||
# pop(0) => "a"
|
||||
# pop(2) => "c"
|
||||
# pop(-1) => "c"
|
||||
# pop(-3) => "a"
|
||||
#
|
||||
# Thus the range is -n .. n-1 where n is the size of the vector.
|
||||
# IndexError is raised if the index is out of range.
|
||||
|
||||
if (index != nil) {
|
||||
if (index < -me.size() or index >= me.size()) {
|
||||
die("IndexError: index out of range");
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
index = me.size() + index;
|
||||
}
|
||||
var item = me.vector[index];
|
||||
me.vector = subvec(me.vector, 0, index) ~ subvec(me.vector, index + 1);
|
||||
return item;
|
||||
}
|
||||
else {
|
||||
return pop(me.vector);
|
||||
}
|
||||
},
|
||||
|
||||
index: func (item) {
|
||||
# Return first index of the given item. Raises a ValueError
|
||||
# if the item is not in the vector.
|
||||
|
||||
forindex (var index; me.vector) {
|
||||
if (me.vector[index] == item) {
|
||||
return index;
|
||||
}
|
||||
};
|
||||
die("ValueError: item not in the vector");
|
||||
},
|
||||
|
||||
remove: func (item) {
|
||||
# Remove the first occurrence of the given item. Raises a
|
||||
# ValueError if the item is not present in the vector.
|
||||
|
||||
me.pop(me.index(item));
|
||||
}
|
||||
|
||||
};
|
175
Nasal/std/Vector.nas.test
Normal file
175
Nasal/std/Vector.nas.test
Normal file
|
@ -0,0 +1,175 @@
|
|||
# Unit tests helpers
|
||||
# TODO generalize (eg. move comparison operators somewhere global)
|
||||
|
||||
var eq = func(lhs, rhs)
|
||||
{
|
||||
if( typeof(lhs) != typeof(rhs) )
|
||||
return 0;
|
||||
|
||||
if( typeof(lhs) == "scalar" )
|
||||
return lhs == rhs;
|
||||
if( typeof(lhs) == "vector" )
|
||||
{
|
||||
if( size(lhs) != size(rhs) )
|
||||
return 0;
|
||||
|
||||
forindex(var i; lhs)
|
||||
{
|
||||
if( !eq(lhs[i], rhs[i]) )
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
var print_fail = func(msg)
|
||||
{
|
||||
var c = caller(1); # [namespace, func, file, line]
|
||||
die(c[2] ~ "(" ~ c[3] ~ "): error: " ~ msg);
|
||||
}
|
||||
|
||||
var REQUIRE_EQUAL = func(lhs, rhs)
|
||||
{
|
||||
if( !eq(lhs, rhs) )
|
||||
print_fail( "check failed [" ~ debug.string(lhs, 0)
|
||||
~ " != "
|
||||
~ debug.string(rhs, 0)
|
||||
~ "]" );
|
||||
}
|
||||
|
||||
# @param fn Function for testing
|
||||
# @param ex (Part of) the expection thrown (with die())
|
||||
var REQUIRE_THROW = func(fn, ex)
|
||||
{
|
||||
call(fn, nil, var err = []);
|
||||
|
||||
var err_msg = "exception " ~ ex ~ " expected";
|
||||
|
||||
if( size(err) )
|
||||
{
|
||||
if( err[0].starts_with(ex ~ ": ") )
|
||||
return;
|
||||
|
||||
err_msg ~= " (got '" ~ err[0] ~ "')";
|
||||
}
|
||||
|
||||
print_fail(err_msg);
|
||||
}
|
||||
|
||||
var x = std.Vector.new();
|
||||
REQUIRE_EQUAL(x.size(), 0);
|
||||
|
||||
x = std.Vector.new(["x", "y"]);
|
||||
REQUIRE_EQUAL(x.vector, ["x", "y"]);
|
||||
REQUIRE_EQUAL(x.size(), 2);
|
||||
x.clear();
|
||||
REQUIRE_EQUAL(x.vector, []);
|
||||
REQUIRE_EQUAL(x.size(), 0);
|
||||
|
||||
# append():
|
||||
x.append("a");
|
||||
x.append("b");
|
||||
x.append("c");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "c"]);
|
||||
REQUIRE_EQUAL(x.size(), 3);
|
||||
|
||||
# extend():
|
||||
x.extend(["d", "e"]);
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "c", "d", "e"]);
|
||||
REQUIRE_EQUAL(x.size(), 5);
|
||||
|
||||
# insert():
|
||||
x.insert(2, "cc");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "cc", "c", "d", "e"]);
|
||||
REQUIRE_EQUAL(x.size(), 6);
|
||||
|
||||
# pop():
|
||||
REQUIRE_EQUAL(x.pop(3), "c");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "cc", "d", "e"]);
|
||||
REQUIRE_EQUAL(x.size(), 5);
|
||||
|
||||
REQUIRE_EQUAL(x.pop(), "e");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "cc", "d"]);
|
||||
REQUIRE_EQUAL(x.size(), 4);
|
||||
|
||||
# clear():
|
||||
x.clear();
|
||||
REQUIRE_EQUAL(x.vector, []);
|
||||
REQUIRE_EQUAL(x.size(), 0);
|
||||
|
||||
# extend():
|
||||
x.extend(["a", "b", "c", "d"]);
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "c", "d"]);
|
||||
REQUIRE_EQUAL(x.size(), 4);
|
||||
|
||||
# index():
|
||||
REQUIRE_EQUAL(x.index("c"), 2);
|
||||
|
||||
# remove():
|
||||
x.remove("c");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "d"]);
|
||||
REQUIRE_EQUAL(x.size(), 3);
|
||||
|
||||
# insert():
|
||||
x.insert(0, "f");
|
||||
REQUIRE_EQUAL(x.vector, ["f", "a", "b", "d"]);
|
||||
REQUIRE_EQUAL(x.size(), 4);
|
||||
x.remove("f");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "d"]);
|
||||
|
||||
x.insert(1, "f");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "f", "b", "d"]);
|
||||
REQUIRE_EQUAL(x.size(), 4);
|
||||
x.remove("f");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "d"]);
|
||||
|
||||
x.insert(2, "f");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "f", "d"]);
|
||||
x.remove("f");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "d"]);
|
||||
|
||||
x.insert(3, "g");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "d", "g"]);
|
||||
x.remove("g");
|
||||
|
||||
x.insert(4, "g");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "d", "g"]);
|
||||
x.remove("g");
|
||||
|
||||
x.insert(-1, "h");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "h", "d"]);
|
||||
x.remove("h");
|
||||
|
||||
x.insert(-2, "h");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "h", "b", "d"]);
|
||||
x.remove("h");
|
||||
|
||||
x.insert(-3, "h");
|
||||
REQUIRE_EQUAL(x.vector, ["h", "a", "b", "d"]);
|
||||
x.remove("h");
|
||||
|
||||
x.insert(-4, "h");
|
||||
REQUIRE_EQUAL(x.vector, ["h", "a", "b", "d"]);
|
||||
x.remove("h");
|
||||
|
||||
# pop():
|
||||
REQUIRE_EQUAL(x.pop(-1), "d");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b"]);
|
||||
x.append("d");
|
||||
|
||||
REQUIRE_EQUAL(x.pop(-2), "b");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "d"]);
|
||||
x.insert(1, "b");
|
||||
|
||||
REQUIRE_EQUAL(x.pop(-3), "a");
|
||||
REQUIRE_EQUAL(x.vector, ["b", "d"]);
|
||||
x.insert(0, "a");
|
||||
REQUIRE_EQUAL(x.vector, ["a", "b", "d"]);
|
||||
|
||||
# Exceptions (should fail)
|
||||
REQUIRE_THROW(func x.pop(-4), "IndexError");
|
||||
REQUIRE_THROW(func x.pop(3), "IndexError");
|
||||
REQUIRE_THROW(func x.remove("e"), "ValueError");
|
Loading…
Add table
Reference in a new issue