Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional pipe methods #71

Merged
merged 1 commit into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading