22
22
package cats
23
23
package kernel
24
24
25
- import scala .collection .immutable .LazyList
26
25
import scala .annotation .tailrec
27
26
import scala .{specialized => sp }
27
+ import cats .kernel .{ScalaVersionSpecificLazyListCompat => LazyListLike }
28
28
29
29
/** A typeclass for types which are countable. Formally this means that values
30
30
* can be mapped on to the natural numbers.
31
31
*
32
- * Because Countable types may be mapped to the natural numbers, being an
33
- * instance of `Countable ` implies having a total ordering, e.g. an `Order`
32
+ * Because Enumerable types may be mapped to the natural numbers, being an
33
+ * instance of `Enumerable ` implies having a total ordering, e.g. an `Order`
34
34
* instance. It also implies having a `PartialNext` and `PartialPrevious` as
35
35
* all representations of the countable numbers, or a subset there of, have
36
36
* `PartialNext` and `PartialPrevious`.
@@ -42,52 +42,65 @@ import scala.{specialized => sp}
42
42
* @see [[https://hackage.haskell.org/package/base-4.15.0.0/docs/GHC-Enum.html ]]
43
43
* @see [[https://en.wikipedia.org/wiki/Countable_set ]]
44
44
*/
45
- trait Countable [@ sp A ] extends PartialNext [A ] with PartialPrevious [A ]{
45
+ trait Enumerable [@ sp A ] extends PartialNext [A ] with PartialPrevious [A ]{
46
46
def order : Order [A ]
47
47
def fromEnum (a : A ): BigInt
48
48
def toEnum (i : BigInt ): Option [A ]
49
49
50
- /** The fundamental function in the `Countable ` class. Given a start position,
51
- * an offset , and an optional last position , enumerate the values between
52
- * `first` and `last` (or `MaxValue` or infinity), using a step of `by -
53
- * first` .
50
+ /** The fundamental function in the `Enumerable ` class. Given a `first`
51
+ * element, a second element , and an optional ` last` element , enumerate the
52
+ * values between `first` and `last` (or `MaxValue` or infinity), the step
53
+ * between the first and second element as the step between all elements .
54
54
*
55
55
* {{{
56
- * scala> Countable [Int].enumFromThenToOpt(1, 3, Some(11)).toList
56
+ * scala> Enumerable [Int].enumFromThenToOpt(1, 3, Some(11)).toList
57
57
* val res0: List[Int] = List(1, 3, 5, 7, 9, 11)
58
58
* }}}
59
59
*
60
+ * @note If the last element is defined, and the second element is less
61
+ * than the last element, then the last element will not be part of
62
+ * the result.
63
+ *
64
+ * @note The last element will only be included in the enumerated result if
65
+ * it aligns with the step. For example, `enumFromThenToOpt(1, 3,
66
+ * 6).toList`, would be `List(1, 3, 5)`.
67
+ *
68
+ * {{{
69
+ * scala> Enumerable[Int].enumFromThenToOpt(1, 2, Some(1)).toList
70
+ * val res0: List[Int] = List(1)
71
+ * }}}
72
+ *
60
73
* All other enum like functions can be expressed in terms of this
61
74
* function.
62
75
*/
63
- def enumFromThenToOpt (first : A , by : A , last : Option [A ]): LazyList [A ] = {
76
+ def enumFromThenToOpt (first : A , second : A , last : Option [A ]): LazyListLike . T [A ] = {
64
77
val Zero : BigInt = BigInt (0 )
65
- val increment : BigInt = fromEnum(by ) - fromEnum(first)
78
+ val increment : BigInt = fromEnum(second ) - fromEnum(first)
66
79
67
- def loop (i : A ): LazyList [A ] =
80
+ def loop (i : A ): LazyListLike . T [A ] =
68
81
if (increment > Zero ) {
69
82
// forwards
70
83
partialNextByN(i, increment) match {
71
84
case Some (next) =>
72
85
if (last.fold(false )(order.gt(next, _))) {
73
- LazyList .empty[A ]
86
+ LazyListLike .empty[A ]
74
87
} else {
75
88
next #:: loop(next)
76
89
}
77
90
case _ =>
78
- LazyList .empty[A ]
91
+ LazyListLike .empty[A ]
79
92
}
80
93
} else {
81
94
// backwards or zero
82
95
partialPreviousByN(i, increment.abs) match {
83
96
case Some (next) =>
84
97
if (last.fold(false )(order.lt(next, _))) {
85
- LazyList .empty
98
+ LazyListLike .empty
86
99
} else {
87
100
next #:: loop(next)
88
101
}
89
102
case _ =>
90
- LazyList .empty
103
+ LazyListLike .empty
91
104
}
92
105
}
93
106
@@ -96,13 +109,13 @@ trait Countable[@sp A] extends PartialNext[A] with PartialPrevious[A]{
96
109
order.compare(first, last) match {
97
110
case result if result < Zero =>
98
111
if (increment < Zero ) {
99
- LazyList .empty[A ]
112
+ LazyListLike .empty[A ]
100
113
} else {
101
114
first #:: loop(first)
102
115
}
103
116
case result if result > Zero =>
104
117
if (increment > Zero ) {
105
- LazyList .empty[A ]
118
+ LazyListLike .empty[A ]
106
119
} else {
107
120
first #:: loop(first)
108
121
}
@@ -114,69 +127,88 @@ trait Countable[@sp A] extends PartialNext[A] with PartialPrevious[A]{
114
127
}
115
128
}
116
129
117
- /** Given a start position, an offset, and a last position, enumerate the
118
- * values between `first` and `last`, using a step of `by - first`.
130
+ def enumFromByToOpt (first : A , step : BigInt , last : Option [A ]): LazyListLike .T [A ] =
131
+ toEnum(step).fold(
132
+ LazyListLike .empty[A ]
133
+ )(second =>
134
+ enumFromThenToOpt(first, second, last)
135
+ )
136
+
137
+ /** Given a `first` element, a second element, and a last, enumerate the
138
+ * values between `first` and `last`, using a step between the `first` and
139
+ * the `second` element as step between all elements.
119
140
*
120
141
* {{{
121
- * scala> Countable [Int].enumFromThenTo(1, 3, 11).toList
142
+ * scala> Enumerable [Int].enumFromThenTo(1, 3, 11).toList
122
143
* val res0: List[Int] = List(1, 3, 5, 7, 9, 11)
123
144
* }}}
145
+ *
146
+ * @see [[#enumFromThenToOpt ]]
124
147
*/
125
- def enumFromThenTo (first : A , by : A , last : A ): LazyList [A ] =
126
- enumFromThenToOpt(first, by , Some (last))
148
+ def enumFromThenTo (first : A , second : A , last : A ): LazyListLike . T [A ] =
149
+ enumFromThenToOpt(first, second , Some (last))
127
150
128
- /** Given a start position and a last position, enumerate the
151
+ def enumFromByTo (first : A , step : BigInt , last : A ): LazyListLike .T [A ] =
152
+ enumFromByToOpt(first, step, Some (last))
153
+
154
+ /** Given a first element and a last element, enumerate the
129
155
* values between `first` and `last`, using a step of 1.
130
156
*
131
157
* {{{
132
- * scala> Countable [Int].enumFromTo(1, 5).toList
158
+ * scala> Enumerable [Int].enumFromTo(1, 5).toList
133
159
* val res0: List[Int] = List(1, 2, 3, 4, 5)
134
160
* }}}
135
161
*/
136
- def enumFromTo (first : A , last : A ): LazyList [A ] =
162
+ def enumFromTo (first : A , last : A ): LazyListLike . T [A ] =
137
163
partialNext(first) match {
138
164
case Some (by) =>
139
165
enumFromThenTo(first, by, last)
140
166
case _ =>
141
167
if (order.lteqv(first, last)) {
142
- LazyList (first)
168
+ LazyListLike (first)
143
169
} else {
144
- LazyList .empty
170
+ LazyListLike .empty
145
171
}
146
172
}
147
173
148
- /** Given a start position and a increment, enumerate the values starting at
149
- * `first` until `MaxValue` or infinity if the type is unbounded.
174
+ /** Given a first element and second element, enumerate all values in the
175
+ * domain starting at first using the step between first and second as the
176
+ * step between all elements. If the domain is infinite, e.g. natural
177
+ * numbers or `BigInt`, then this will be an infinite result.
150
178
*
151
179
* {{{
152
- * scala> Countable [Int].enumFromThen(Int.MaxValue - 5, Int.MaxValue - 4).toList
180
+ * scala> Enumerable [Int].enumFromThen(Int.MaxValue - 5, Int.MaxValue - 4).toList
153
181
* val res0: List[Int] = List(2147483642, 2147483643, 2147483644, 2147483645, 2147483646, 2147483647)
154
182
* }}}
155
183
*/
156
- def enumFromThen (first : A , by : A ): LazyList [A ] =
157
- enumFromThenToOpt(first, by, None )
184
+ def enumFromThen (first : A , second : A ): LazyListLike .T [A ] =
185
+ enumFromThenToOpt(first, second, None )
186
+
187
+ def enumFromBy (first : A , by : BigInt ): LazyListLike .T [A ] =
188
+ enumFromByToOpt(first, by, None )
158
189
159
- /** Given a start position, enumerate the values starting at `first` by 1,
160
- * until `MaxValue` or infinity if the type is unbounded.
190
+ /** Given a first element, enumerate all values in the domain starting at
191
+ * first using a step of 1 between all elements. If the domain is infinite,
192
+ * e.g. natural numbers or `BigInt`, then this will be an infinite result.
161
193
*
162
194
* {{{
163
- * scala> Countable [Int].enumFrom(Int.MaxValue - 5).toList
195
+ * scala> Enumerable [Int].enumFrom(Int.MaxValue - 5).toList
164
196
* val res0: List[Int] = List(2147483642, 2147483643, 2147483644, 2147483645, 2147483646, 2147483647)
165
197
* }}}
166
198
*/
167
- def enumFrom (first : A ): LazyList [A ] =
199
+ def enumFrom (first : A ): LazyListLike . T [A ] =
168
200
partialNext(first) match {
169
201
case Some (by) =>
170
202
enumFromThen(first, by)
171
203
case _ =>
172
- LazyList (first)
204
+ LazyListLike (first)
173
205
}
174
206
175
207
override final def partialOrder : PartialOrder [A ] = order
176
208
}
177
209
178
- object Countable {
179
- def apply [A ](implicit A : Countable [A ]): Countable [A ] = A
210
+ object Enumerable {
211
+ def apply [A ](implicit A : Enumerable [A ]): Enumerable [A ] = A
180
212
}
181
213
182
214
/**
0 commit comments