Skip to content

Commit

Permalink
Additional pipe methods (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
degory committed May 24, 2024
1 parent 10c4370 commit dc39377
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 1 deletion.
14 changes: 14 additions & 0 deletions src/box.ghul
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Ghul is
class BOX[T] is
value: T public;

init() is
si

init(value: T) is
self.value = value;
si

to_string() -> string => "{value}";
si
si
42 changes: 42 additions & 0 deletions src/flat-map-pipe.ghul
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace Ghul.Pipes is
use Collections;

class FLAT_MAP_PIPE[TFrom,TTo]: Pipe[TTo] is
current: TTo;

_iterator: Iterator[TFrom];
_flat_iterator: Iterator[TTo];

_mapper: (TFrom) -> Iterable[TTo];

init(iterator: Iterator[TFrom], mapper: (TFrom) -> Iterable[TTo]) is
_iterator = iterator;
_mapper = mapper;
si

move_next() -> bool is
do
if _flat_iterator? /\ _flat_iterator.move_next() then
current = _flat_iterator.current;

return true;
fi

if !_iterator.move_next() then
reset();

return false;
fi

let flat_iterable = _mapper(_iterator.current);

_flat_iterator = flat_iterable.iterator;
od
si

reset() is
_iterator.reset();
_flat_iterator = null;
si
si
si
34 changes: 34 additions & 0 deletions src/list-reverse-pipe.ghul
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace Ghul.Pipes is
use Collections;

class LIST_REVERSE_PIPE[T]: Pipe[T] is
_list: List[T];
_index: int;

current: T => _list[_index];

init(list: List[T]) is
super.init(list);

_list = list;
reset();
si

move_next() -> bool is
let result = _index > 0;

if result then
_index = _index - 1;
fi

return result;
si

reset() is
_index = _list.count;
si

dispose() is
si
si
si
41 changes: 40 additions & 1 deletion src/pipe.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ namespace Ghul.Pipes is
map[U](mapper: (T) -> U) -> Pipe[U] =>
MAP_PIPE[T,U](iterator, mapper);

flat_map[U](mapper: (T) -> Iterable[U]) -> Pipe[U] =>
FLAT_MAP_PIPE[T,U](iterator, mapper);

skip(count: int) -> Pipe[T] =>
SKIP_PIPE[T](iterator, count);

Expand Down Expand Up @@ -167,6 +170,19 @@ namespace Ghul.Pipes is

throw InvalidOperationException("no matching element found");
si

find_map_or_throw[U](mapper: (T) -> MAYBE[U]) -> U is
while move_next() do
let element = current;
let maybe_result = mapper(element);

if maybe_result? then
return maybe_result!;
fi
od

throw InvalidOperationException("no matching element found");
si

first() -> MAYBE[T] =>
if move_next() then
Expand All @@ -185,7 +201,7 @@ namespace Ghul.Pipes is
fi
fi

MAYBE[U]();
return MAYBE[U]();
si

first_or_throw() -> T =>
Expand All @@ -194,7 +210,20 @@ namespace Ghul.Pipes is
else
throw InvalidOperationException("no element found")
fi;

first_map_or_throw[U](mapper: (T) -> MAYBE[U]) -> U is
if move_next() then
let element = current;
let maybe_result = mapper(element);

if maybe_result? then
return maybe_result!
fi
fi

throw InvalidOperationException("no matching element found");
si

only() -> T is
if move_next() then
let result = current;
Expand Down Expand Up @@ -250,6 +279,16 @@ namespace Ghul.Pipes is
return true;
si

for_each(action: T -> void) is
while move_next() do
action(current);
od
si

reverse() -> Pipe[T] =>
let list = collect_list() in
LIST_REVERSE_PIPE(list);

sort() -> Pipe[T] is
let list = collect_list();

Expand Down
136 changes: 136 additions & 0 deletions tests/flat-map-pipe-tests.ghul
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
namespace Tests is

use Collections;
use NSubstitute;

use NSubstitute.SubstituteExtensions.returns;
use NSubstitute.SubstituteExtensions.received;

use Microsoft.VisualStudio.TestTools.UnitTesting;

use Ghul.Pipes;

class FlatMapPipeShould is
@test()

init() is
si

FlatMap_EmptyArray_ReturnsEmptySequence() is
@test()

let pipe =
Ghul.Pipes.Pipe`[int]
.from(empty`[int]());

let result =
pipe
.flat_map(i => i.to_string());

assert_are_equal(empty`[char](), result);
si

FlatMap_IntsToStrings_ReturnsExpectedSequence() is
@test()

let pipe =
Ghul.Pipes.Pipe`[int]
.from([3, 4, 1, 5, 2, 3, 1, 4, 5]);

let result =
pipe
.flat_map(i => i.to_string());

assert_are_equal(['3', '4', '1', '5', '2', '3', '1', '4', '5'], result);
si

FlatMap_ArraysIdentity_ReturnsConcatArrays() is
@test()

let pipe =
Ghul.Pipes.Pipe[int[]]
.from([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);

let result =
pipe
.flat_map(a => a);

assert_are_equal([1, 2, 3, 4, 5, 6, 7, 8, 9], result);
si

FlatMap_ResetBeforeIteration_HasNoEffect() is
@test()

let pipe =
Ghul.Pipes.Pipe[int[]]
.from([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);

let result =
pipe
.flat_map(a => a);

result.reset();

assert_are_equal([1, 2, 3, 4, 5, 6, 7, 8, 9], result);
si

FlatMap_ResetAfterIncompleteIteration_StartsAgainFromBeginning() is
@test()

let pipe =
Ghul.Pipes.Pipe[int[]]
.from([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);

let result =
pipe
.flat_map(a => a);

result.move_next();
result.move_next();
result.move_next();
result.move_next();

result.reset();

assert_are_equal([1, 2, 3, 4, 5, 6, 7, 8, 9], result);
si

FlatMap_CompleteIteration_ResetsPipe() is
@test()

let pipe =
Ghul.Pipes.Pipe[int[]]
.from([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);

let result =
pipe
.flat_map(a => a);

// count consumes the whole pipe and then resets it:
let count = result.count();

assert_are_equal([1, 2, 3, 4, 5, 6, 7, 8, 9], result);
si


FlatMap_ResetAfterIteration_HasNoEffect() is
@test()

let pipe =
Ghul.Pipes.Pipe[int[]]
.from([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);

let result =
pipe
.flat_map(a => a);

// count consumes the whole pipe and then resets it:
let count = result.count();

// this reset is a no-op:
result.reset();

assert_are_equal([1, 2, 3, 4, 5, 6, 7, 8, 9], result);
si
si
si
48 changes: 48 additions & 0 deletions tests/pipe-tests.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,54 @@ namespace Tests is
Assert.are_equal("total: " + (1 + 2 + 3 + 4 + 5 + 6), result);
si

Reverse_EmptySequence_GivesEmptyResult() is
@test()

let pipe = Pipe.from(empty[int]());

let result = pipe.reverse();

assert_are_equal(empty[int](), result);
si

Reverse_NonEmptySequence_GivesExpectedResult() is
@test()

let pipe = Pipe.from([1, 2, 3, 4, 5, 6]);

let result = pipe.reverse();

assert_are_equal([6, 5, 4, 3, 2, 1], result);
si

ForEach_EmptySequence_DoesNotCallAction() is
@test()

let pipe = Pipe.from(empty[int]());

let action_was_called = BOX(false);

pipe.for_each(element is
action_was_called.value = true;
si);

Assert.is_false(action_was_called.value);
si

ForEach_NonEmptySequence_CallsActionOnceInOrderForEveryElement() is
@test()

let pipe = Pipe.from([1, 2, 3, 4, 5, 6]);

let result = LIST[int]();

pipe.for_each(element is
result.add(element)
si);

assert_are_equal([1, 2, 3, 4, 5, 6], Pipe.from(result));
si

Sort_NoComparer_SortsInDefaultOrder() is
@test()

Expand Down

0 comments on commit dc39377

Please sign in to comment.