diff --git a/iterator/stateless/compress.go b/iterator/stateless/compress.go new file mode 100644 index 0000000..3506629 --- /dev/null +++ b/iterator/stateless/compress.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/iterator/stateless/generic" + T "github.com/IBM/fp-go/tuple" +) + +// Compress returns an [Iterator] that filters elements from a data [Iterator] returning only those that have a corresponding element in selector [Iterator] that evaluates to `true`. +// Stops when either the data or selectors iterator has been exhausted. +func Compress[U any](sel Iterator[bool]) func(Iterator[U]) Iterator[U] { + return G.Compress[Iterator[U], Iterator[bool], Iterator[T.Tuple2[U, bool]]](sel) +} diff --git a/iterator/stateless/compress_test.go b/iterator/stateless/compress_test.go new file mode 100644 index 0000000..6700242 --- /dev/null +++ b/iterator/stateless/compress_test.go @@ -0,0 +1,35 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + A "github.com/IBM/fp-go/array" + "github.com/stretchr/testify/assert" +) + +func TestCompress(t *testing.T) { + // sequence of 5 items + data := From(0, 1, 2, 3) + // select some of these items + selector := From(true, false, false, true) + // compressed result + compressed := Compress[int](selector)(data) + + assert.Equal(t, A.From(0, 3), ToArray(compressed)) + +} diff --git a/iterator/stateless/cycle.go b/iterator/stateless/cycle.go new file mode 100644 index 0000000..4528d16 --- /dev/null +++ b/iterator/stateless/cycle.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/iterator/stateless/generic" +) + +// DropWhile creates an [Iterator] that drops elements from the [Iterator] as long as the predicate is true; afterwards, returns every element. +// Note, the [Iterator] does not produce any output until the predicate first becomes false +func Cycle[U any](ma Iterator[U]) Iterator[U] { + return G.Cycle[Iterator[U]](ma) +} diff --git a/iterator/stateless/cycle_test.go b/iterator/stateless/cycle_test.go new file mode 100644 index 0000000..e460c0e --- /dev/null +++ b/iterator/stateless/cycle_test.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + A "github.com/IBM/fp-go/array" + "github.com/stretchr/testify/assert" +) + +func TestCycle(t *testing.T) { + // sequence of 5 items + items := Take[int](5)(Count(0)) + // repeat + repeated := Take[int](17)(Cycle(items)) + + assert.Equal(t, A.From(0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1), ToArray(repeated)) + +} diff --git a/iterator/stateless/dropwhile.go b/iterator/stateless/dropwhile.go new file mode 100644 index 0000000..b4a1445 --- /dev/null +++ b/iterator/stateless/dropwhile.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + G "github.com/IBM/fp-go/iterator/stateless/generic" +) + +// DropWhile creates an [Iterator] that drops elements from the [Iterator] as long as the predicate is true; afterwards, returns every element. +// Note, the [Iterator] does not produce any output until the predicate first becomes false +func DropWhile[U any](pred func(U) bool) func(Iterator[U]) Iterator[U] { + return G.DropWhile[Iterator[U]](pred) +} diff --git a/iterator/stateless/dropwhile_test.go b/iterator/stateless/dropwhile_test.go new file mode 100644 index 0000000..c284292 --- /dev/null +++ b/iterator/stateless/dropwhile_test.go @@ -0,0 +1,35 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package stateless + +import ( + "testing" + + A "github.com/IBM/fp-go/array" + "github.com/stretchr/testify/assert" +) + +func TestDropWhile(t *testing.T) { + // sequence of 5 items + data := Take[int](10)(Cycle(From(0, 1, 2, 3))) + + total := DropWhile(func(data int) bool { + return data <= 2 + })(data) + + assert.Equal(t, A.From(3, 0, 1, 2, 3, 0, 1), ToArray(total)) + +} diff --git a/iterator/stateless/generic/compress.go b/iterator/stateless/generic/compress.go new file mode 100644 index 0000000..a3642bc --- /dev/null +++ b/iterator/stateless/generic/compress.go @@ -0,0 +1,34 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/function" + O "github.com/IBM/fp-go/option" + T "github.com/IBM/fp-go/tuple" +) + +// Compress returns an [Iterator] that filters elements from a data [Iterator] returning only those that have a corresponding element in selector [Iterator] that evaluates to `true`. +// Stops when either the data or selectors iterator has been exhausted. +func Compress[GU ~func() O.Option[T.Tuple2[GU, U]], GB ~func() O.Option[T.Tuple2[GB, bool]], CS ~func() O.Option[T.Tuple2[CS, T.Tuple2[U, bool]]], U any](sel GB) func(GU) GU { + return F.Flow2( + Zip[GU, GB, CS](sel), + FilterMap[GU, CS](F.Flow2( + O.FromPredicate(T.Second[U, bool]), + O.Map(T.First[U, bool]), + )), + ) +} diff --git a/iterator/stateless/generic/cycle.go b/iterator/stateless/generic/cycle.go new file mode 100644 index 0000000..31ce5b0 --- /dev/null +++ b/iterator/stateless/generic/cycle.go @@ -0,0 +1,43 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/function" + O "github.com/IBM/fp-go/option" + T "github.com/IBM/fp-go/tuple" +) + +func Cycle[GU ~func() O.Option[T.Tuple2[GU, U]], U any](ma GU) GU { + // avoid cyclic references + var m func(O.Option[T.Tuple2[GU, U]]) O.Option[T.Tuple2[GU, U]] + + recurse := func(mu GU) GU { + return F.Nullary2( + mu, + m, + ) + } + + m = O.Fold(func() O.Option[T.Tuple2[GU, U]] { + return recurse(ma)() + }, F.Flow2( + T.Map2(recurse, F.Identity[U]), + O.Of[T.Tuple2[GU, U]], + )) + + return recurse(ma) +} diff --git a/iterator/stateless/generic/dropwhile.go b/iterator/stateless/generic/dropwhile.go new file mode 100644 index 0000000..28d0c1b --- /dev/null +++ b/iterator/stateless/generic/dropwhile.go @@ -0,0 +1,49 @@ +// Copyright (c) 2023 IBM Corp. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package generic + +import ( + F "github.com/IBM/fp-go/function" + O "github.com/IBM/fp-go/option" + P "github.com/IBM/fp-go/predicate" + T "github.com/IBM/fp-go/tuple" +) + +// DropWhile creates an [Iterator] that drops elements from the [Iterator] as long as the predicate is true; afterwards, returns every element. +// Note, the [Iterator] does not produce any output until the predicate first becomes false +func DropWhile[GU ~func() O.Option[T.Tuple2[GU, U]], U any](pred func(U) bool) func(GU) GU { + // avoid cyclic references + var m func(O.Option[T.Tuple2[GU, U]]) O.Option[T.Tuple2[GU, U]] + + fromPred := O.FromPredicate(P.Not(P.ContraMap(T.Second[GU, U])(pred))) + + recurse := func(mu GU) GU { + return F.Nullary2( + mu, + m, + ) + } + + m = O.Chain(func(t T.Tuple2[GU, U]) O.Option[T.Tuple2[GU, U]] { + return F.Pipe2( + t, + fromPred, + O.Fold(recurse(t.F1), O.Of[T.Tuple2[GU, U]]), + ) + }) + + return recurse +} diff --git a/iterator/stateless/generic/iterator.go b/iterator/stateless/generic/iterator.go index 6190632..e614ba8 100644 --- a/iterator/stateless/generic/iterator.go +++ b/iterator/stateless/generic/iterator.go @@ -20,7 +20,7 @@ import ( F "github.com/IBM/fp-go/function" "github.com/IBM/fp-go/internal/utils" IO "github.com/IBM/fp-go/iooption/generic" - N "github.com/IBM/fp-go/number/integer" + N "github.com/IBM/fp-go/number" O "github.com/IBM/fp-go/option" T "github.com/IBM/fp-go/tuple" ) @@ -135,38 +135,49 @@ func Flatten[GV ~func() O.Option[T.Tuple2[GV, GU]], GU ~func() O.Option[T.Tuple2 return MonadChain(ma, F.Identity[GU]) } -// MakeBy returns an [Iterator] with `n` elements initialized with `f(i)` -func MakeBy[GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(int) U, U any](n int, f FCT) GU { +// MakeBy returns an [Iterator] with an infinite number of elements initialized with `f(i)` +func MakeBy[GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(int) U, U any](f FCT) GU { var m func(int) O.Option[T.Tuple2[GU, U]] recurse := func(i int) GU { - return func() O.Option[T.Tuple2[GU, U]] { - return F.Pipe1( - i, - m, - ) - } + return F.Nullary2( + F.Constant(i), + m, + ) } - m = F.Flow2( - O.FromPredicate(N.Between(0, n)), - O.Map(F.Flow2( - T.Replicate2[int], - T.Map2(F.Flow2( - utils.Inc, - recurse), - f), - )), + m = F.Flow3( + T.Replicate2[int], + T.Map2(F.Flow2( + utils.Inc, + recurse), + f), + O.Of[T.Tuple2[GU, U]], ) // bootstrap return recurse(0) } -// Replicate creates an [Iterator] containing a value repeated the specified number of times. -func Replicate[GU ~func() O.Option[T.Tuple2[GU, U]], U any](n int, a U) GU { - return MakeBy[GU](n, F.Constant1[int](a)) +// Replicate creates an infinite [Iterator] containing a value. +func Replicate[GU ~func() O.Option[T.Tuple2[GU, U]], U any](a U) GU { + return MakeBy[GU](F.Constant1[int](a)) +} + +// Repeat creates an [Iterator] containing a value repeated the specified number of times. +// Alias of [Replicate] combined with [Take] +func Repeat[GU ~func() O.Option[T.Tuple2[GU, U]], U any](n int, a U) GU { + return F.Pipe2( + a, + Replicate[GU], + Take[GU](n), + ) +} + +// Count creates an [Iterator] containing a consecutive sequence of integers starting with the provided start value +func Count[GU ~func() O.Option[T.Tuple2[GU, int]]](start int) GU { + return MakeBy[GU](N.Add(start)) } func FilterMap[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(U) O.Option[V], U, V any](f FCT) func(ma GU) GV { diff --git a/iterator/stateless/generic/scan.go b/iterator/stateless/generic/scan.go index a930a72..58385f0 100644 --- a/iterator/stateless/generic/scan.go +++ b/iterator/stateless/generic/scan.go @@ -21,19 +21,25 @@ import ( T "github.com/IBM/fp-go/tuple" ) +func apTuple[A, B any](t T.Tuple2[func(A) B, A]) T.Tuple2[B, A] { + return T.MakeTuple2(t.F1(t.F2), t.F2) +} + func Scan[GV ~func() O.Option[T.Tuple2[GV, V]], GU ~func() O.Option[T.Tuple2[GU, U]], FCT ~func(V, U) V, U, V any](f FCT, initial V) func(ma GU) GV { // pre-declare to avoid cyclic reference - var recurse func(ma GU, v V) GV + var m func(GU) func(V) GV - recurse = func(ma GU, current V) GV { + recurse := func(ma GU, current V) GV { return F.Nullary2( ma, - O.Map(func(t T.Tuple2[GU, U]) T.Tuple2[GV, V] { - v := f(current, t.F2) - return T.MakeTuple2(recurse(t.F1, v), v) - }), + O.Map(F.Flow2( + T.Map2(m, F.Bind1st(f, current)), + apTuple[V, GV], + )), ) } + m = F.Curry2(recurse) + return F.Bind2nd(recurse, initial) } diff --git a/iterator/stateless/generic/take.go b/iterator/stateless/generic/take.go index 5e0a1f8..166dc33 100644 --- a/iterator/stateless/generic/take.go +++ b/iterator/stateless/generic/take.go @@ -29,18 +29,14 @@ func Take[GU ~func() O.Option[T.Tuple2[GU, U]], U any](n int) func(ma GU) GU { fromPred := O.FromPredicate(N.Between(0, n)) recurse = func(ma GU, idx int) GU { - return func() O.Option[T.Tuple2[GU, U]] { - return F.Pipe2( - idx, - fromPred, - O.Chain(F.Ignore1of1[int](F.Nullary2( - ma, - O.Map(func(t T.Tuple2[GU, U]) T.Tuple2[GU, U] { - return T.MakeTuple2(recurse(t.F1, idx+1), t.F2) - }), - ))), - ) - } + return F.Nullary3( + F.Constant(idx), + fromPred, + O.Chain(F.Ignore1of1[int](F.Nullary2( + ma, + O.Map(T.Map2(F.Bind2nd(recurse, idx+1), F.Identity[U])), + ))), + ) } return F.Bind2nd(recurse, 0) diff --git a/iterator/stateless/iterator.go b/iterator/stateless/iterator.go index 5bfc788..985aedf 100644 --- a/iterator/stateless/iterator.go +++ b/iterator/stateless/iterator.go @@ -78,14 +78,14 @@ func From[U any](data ...U) Iterator[U] { return G.From[Iterator[U]](data...) } -// MakeBy returns an [Iterator] with `n` elements initialized with `f(i)` -func MakeBy[FCT ~func(int) U, U any](n int, f FCT) Iterator[U] { - return G.MakeBy[Iterator[U]](n, f) +// MakeBy returns an [Iterator] with an infinite number of elements initialized with `f(i)` +func MakeBy[FCT ~func(int) U, U any](f FCT) Iterator[U] { + return G.MakeBy[Iterator[U]](f) } -// Replicate creates an [Iterator] containing a value repeated the specified number of times. -func Replicate[U any](n int, a U) Iterator[U] { - return G.Replicate[Iterator[U]](n, a) +// Replicate creates an [Iterator] containing a value repeated an infinite number of times. +func Replicate[U any](a U) Iterator[U] { + return G.Replicate[Iterator[U]](a) } // FilterMap filters and transforms the content of an iterator @@ -107,3 +107,14 @@ func Ap[V, U any](ma Iterator[U]) func(Iterator[func(U) V]) Iterator[V] { func MonadAp[V, U any](fab Iterator[func(U) V], ma Iterator[U]) Iterator[V] { return G.MonadAp[Iterator[func(U) V], Iterator[V]](fab, ma) } + +// Repeat creates an [Iterator] containing a value repeated the specified number of times. +// Alias of [Replicate] +func Repeat[U any](n int, a U) Iterator[U] { + return G.Repeat[Iterator[U]](n, a) +} + +// Count creates an [Iterator] containing a consecutive sequence of integers starting with the provided start value +func Count(start int) Iterator[int] { + return G.Count[Iterator[int]](start) +} diff --git a/iterator/stateless/iterator_test.go b/iterator/stateless/iterator_test.go index 909d10f..3eee813 100644 --- a/iterator/stateless/iterator_test.go +++ b/iterator/stateless/iterator_test.go @@ -76,8 +76,9 @@ func isPrimeNumber(num int) bool { func TestFilterMap(t *testing.T) { - it := F.Pipe2( - MakeBy(100, utils.Inc), + it := F.Pipe3( + MakeBy(utils.Inc), + Take[int](100), FilterMap(O.FromPredicate(isPrimeNumber)), ToArray[int], ) diff --git a/iterator/stateless/take_test.go b/iterator/stateless/take_test.go index 70dbf2b..c2bda95 100644 --- a/iterator/stateless/take_test.go +++ b/iterator/stateless/take_test.go @@ -18,19 +18,20 @@ package stateless import ( "testing" + A "github.com/IBM/fp-go/array" F "github.com/IBM/fp-go/function" "github.com/stretchr/testify/assert" ) func TestTake(t *testing.T) { - total := MakeBy(100, F.Identity[int]) + total := MakeBy(F.Identity[int]) trimmed := F.Pipe1( total, Take[int](10), ) - assert.Equal(t, ToArray(MakeBy(10, F.Identity[int])), ToArray(trimmed)) + assert.Equal(t, A.MakeBy(10, F.Identity[int]), ToArray(trimmed)) }