use slice assign and add List.*using_test functions (#967)

* std/collections: use slice assignment
* std/collections: add List.remove_using_test and List.retain_using_test
This commit is contained in:
Pierre Curto
2023-09-01 12:16:15 +02:00
committed by GitHub
parent 70b9e811bd
commit e56313a204
2 changed files with 77 additions and 8 deletions

View File

@@ -6,6 +6,7 @@ import std::io;
import std::math;
def ElementPredicate = fn bool(Type *type);
def ElementTest = fn bool(Type *type, any context);
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
const ELEMENT_IS_POINTER = Type.kindof == POINTER;
@@ -251,6 +252,15 @@ fn usz List.remove_if(&self, ElementPredicate filter)
return self._remove_if(filter, false);
}
/**
* @param selection "The function to determine if it should be kept or not"
* @return "the number of deleted elements"
**/
fn usz List.retain_if(&self, ElementPredicate selection)
{
return self._remove_if(selection, true);
}
macro usz List._remove_if(&self, ElementPredicate filter, bool $invert) @local
{
usz size = self.size;
@@ -264,8 +274,7 @@ macro usz List._remove_if(&self, ElementPredicate filter, bool $invert) @local
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
// Do explicit copy - copying between the same slice is not well defined.
for (usz j = 0; j < n; j++) self.entries[i + j] = self.entries[k + j];
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
@@ -277,13 +286,39 @@ macro usz List._remove_if(&self, ElementPredicate filter, bool $invert) @local
return size - self.size;
}
/**
* @param selection "The function to determine if it should be kept or not"
* @return "the number of deleted elements"
**/
fn usz List.retain_if(&self, ElementPredicate selection)
fn usz List.remove_using_test(&self, ElementTest filter, any context)
{
return self._remove_if(selection, true);
return self._remove_using_test(filter, false, context);
}
fn usz List.retain_using_test(&self, ElementTest filter, any context)
{
return self._remove_using_test(filter, true, context);
}
macro usz List._remove_using_test(&self, ElementTest filter, bool $invert, ctx) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$endif
}
return size - self.size;
}
/**

View File

@@ -78,6 +78,23 @@ fn void! remove_if()
assert(test.array_view() == int[]{11, 10, 20});
}
fn void! remove_using_test()
{
IntList test;
usz removed;
test.add_array({ 1, 11, 2, 10, 20 });
removed = test.remove_using_test(fn bool(i, ctx) => *i >= *(int*)ctx, &&10);
assert(removed == 3);
assert(test.array_view() == int[]{1, 2});
test.clear();
test.add_array({ 1, 11, 2, 10, 20 });
removed = test.remove_using_test(fn bool(i, ctx) => *i < *(int*)ctx, &&10);
assert(removed == 2);
assert(test.array_view() == int[]{11, 10, 20});
}
fn void! retain_if()
{
IntList test;
@@ -95,6 +112,23 @@ fn void! retain_if()
assert(test.array_view() == int[]{11, 10, 20});
}
fn void! retain_using_test()
{
IntList test;
usz removed;
test.add_array({ 1, 11, 2, 10, 20 });
removed = test.remove_using_test(fn bool(i, ctx) => *i >= *(int*)ctx, &&10);
assert(removed == 3);
assert(test.array_view() == int[]{1, 2});
test.clear();
test.add_array({ 1, 11, 2, 10, 20 });
removed = test.remove_using_test(fn bool(i, ctx) => *i < *(int*)ctx, &&10);
assert(removed == 2);
assert(test.array_view() == int[]{11, 10, 20});
}
module list_test;
fn bool filter(int* i)