@@ -111,15 +111,20 @@ defprotocol Enumerable do
111111 @ doc """
112112 Checks if a value exists within the collection.
113113
114- Membership should be tested with the match (`===`) operator.
114+ It should return `{ :ok, boolean }` if membership can be tested
115+ faster than linear time with the match (`===`) operator, otherwise
116+ should return `{ :error, __MODULE__ }`.
115117 """
116- @ spec member? ( t , term ) :: boolean
118+ @ spec member? ( t , term ) :: { :ok , boolean } | { :error , module }
117119 def member? ( collection , value )
118120
119121 @ doc """
120122 Retrieves the collection's size.
123+
124+ Should return `{ :ok, size }` if the size is pre-calculated,
125+ `{ :error, __MODULE__ }` otherwise.
121126 """
122- @ spec count ( t ) :: non_neg_integer
127+ @ spec count ( t ) :: { :ok , non_neg_integer } | { :error , module }
123128 def count ( collection )
124129end
125130
@@ -160,56 +165,6 @@ defmodule Enum do
160165 @ type index :: non_neg_integer
161166 @ type default :: any
162167
163- @ doc """
164- Checks if `value` exists within the `collection`.
165-
166- Membership is tested with the match (`===`) operator, although
167- enumerables like ranges may include floats inside the given
168- range.
169-
170- ## Examples
171-
172- iex> Enum.member?(1..10, 5)
173- true
174- iex> Enum.member?([:a, :b, :c], :d)
175- false
176-
177- """
178- @ spec member? ( t , element ) :: boolean
179- def member? ( collection , value ) do
180- Enumerable . member? ( collection , value )
181- end
182-
183- @ doc """
184- Returns the collection's size.
185-
186- ## Examples
187-
188- iex> Enum.count([1, 2, 3])
189- 3
190-
191- """
192- @ spec count ( t ) :: non_neg_integer
193- def count ( collection ) do
194- Enumerable . count ( collection )
195- end
196-
197- @ doc """
198- Returns the count of items in the collection for which
199- `fun` returns `true`.
200-
201- ## Examples
202- iex> Enum.count([1, 2, 3, 4, 5], fn(x) -> rem(x, 2) == 0 end)
203- 2
204-
205- """
206- @ spec count ( t , ( element -> as_boolean ( term ) ) ) :: non_neg_integer
207- def count ( collection , fun ) do
208- Enumerable . reduce ( collection , { :cont , 0 } , fn ( entry , acc ) ->
209- { :cont , if ( fun . ( entry ) , do: acc + 1 , else: acc ) }
210- end ) |> elem ( 1 )
211- end
212-
213168 @ doc """
214169 Invokes the given `fun` for each item in the `collection` and returns `false`
215170 if at least one invocation returns `false`. Otherwise returns `true`.
@@ -352,6 +307,52 @@ defmodule Enum do
352307 reduce ( enumerable , [ ] , & reduce ( & 1 , & 2 , fun ) ) |> :lists . reverse
353308 end
354309
310+ @ doc """
311+ Returns the collection's size.
312+
313+ ## Examples
314+
315+ iex> Enum.count([1, 2, 3])
316+ 3
317+
318+ """
319+ @ spec count ( t ) :: non_neg_integer
320+ def count ( collection ) when is_list ( collection ) do
321+ :erlang . length ( collection )
322+ end
323+
324+ def count ( collection ) do
325+ case Enumerable . count ( collection ) do
326+ value when is_integer ( value ) ->
327+ IO . write "Expected #{ inspect Enumerable . impl_for ( collection ) } .count/1 to return " <>
328+ "{ :ok, boolean } if pre-calculated, otherwise { :error, module }, got " <>
329+ "an integer\n #{ Exception . format_stacktrace } "
330+ value
331+ { :ok , value } when is_integer ( value ) ->
332+ value
333+ { :error , module } ->
334+ module . reduce ( collection , { :cont , 0 } , fn
335+ _ , acc -> { :cont , acc + 1 }
336+ end ) |> elem ( 1 )
337+ end
338+ end
339+
340+ @ doc """
341+ Returns the count of items in the collection for which
342+ `fun` returns `true`.
343+
344+ ## Examples
345+ iex> Enum.count([1, 2, 3, 4, 5], fn(x) -> rem(x, 2) == 0 end)
346+ 2
347+
348+ """
349+ @ spec count ( t , ( element -> as_boolean ( term ) ) ) :: non_neg_integer
350+ def count ( collection , fun ) do
351+ Enumerable . reduce ( collection , { :cont , 0 } , fn ( entry , acc ) ->
352+ { :cont , if ( fun . ( entry ) , do: acc + 1 , else: acc ) }
353+ end ) |> elem ( 1 )
354+ end
355+
355356 @ doc """
356357 Drops the first `count` items from `collection`.
357358
@@ -841,6 +842,43 @@ defmodule Enum do
841842 { :lists . reverse ( list ) , acc }
842843 end
843844
845+ @ doc """
846+ Checks if `value` exists within the `collection`.
847+
848+ Membership is tested with the match (`===`) operator, although
849+ enumerables like ranges may include floats inside the given
850+ range.
851+
852+ ## Examples
853+
854+ iex> Enum.member?(1..10, 5)
855+ true
856+ iex> Enum.member?([:a, :b, :c], :d)
857+ false
858+
859+ """
860+ @ spec member? ( t , element ) :: boolean
861+ def member? ( collection , value ) when is_list ( collection ) do
862+ :lists . member ( value , collection )
863+ end
864+
865+ def member? ( collection , value ) do
866+ case Enumerable . member? ( collection , value ) do
867+ value when is_boolean ( value ) ->
868+ IO . write "Expected #{ inspect Enumerable . impl_for ( collection ) } .member?/2 to return " <>
869+ "{ :ok, boolean } if faster than linear, otherwise { :error, __MODULE__ }, " <>
870+ "got a boolean\n #{ Exception . format_stacktrace } "
871+ value
872+ { :ok , value } when is_boolean ( value ) ->
873+ value
874+ { :error , module } ->
875+ module . reduce ( collection , { :cont , false } , fn
876+ ^ value , _ -> { :halt , true }
877+ _ , _ -> { :cont , false }
878+ end ) |> elem ( 1 )
879+ end
880+ end
881+
844882 @ doc """
845883 Partitions `collection` into two collections, where the first one contains elements
846884 for which `fun` returns a truthy value, and the second one -- for which `fun`
@@ -1952,25 +1990,17 @@ defimpl Enumerable, for: List do
19521990 def reduce ( [ ] , { :cont , acc } , _fun ) , do: { :done , acc }
19531991 def reduce ( [ h | t ] , { :cont , acc } , fun ) , do: reduce ( t , fun . ( h , acc ) , fun )
19541992
1955- def member? ( [ ] , _ ) , do: false
1956- def member? ( list , value ) , do: :lists . member ( value , list )
1957-
1958- def count ( list ) , do: length ( list )
1993+ def member? ( _list , _value ) ,
1994+ do: { :error , __MODULE__ }
1995+ def count ( _list ) ,
1996+ do: { :error , __MODULE__ }
19591997end
19601998
19611999defimpl Enumerable , for: Function do
1962- def reduce ( function , acc , fun ) do
1963- function . ( acc , fun )
1964- end
1965-
1966- def member? ( function , value ) do
1967- function . ( { :cont , false } , fn
1968- ^ value , _ -> { :halt , true }
1969- _ , _ -> { :cont , false }
1970- end ) |> elem ( 1 )
1971- end
1972-
1973- def count ( function ) do
1974- function . ( { :cont , 0 } , fn ( _ , acc ) -> { :cont , acc + 1 } end ) |> elem ( 1 )
1975- end
2000+ def reduce ( function , acc , fun ) ,
2001+ do: function . ( acc , fun )
2002+ def member? ( _function , _value ) ,
2003+ do: { :error , __MODULE__ }
2004+ def count ( _function ) ,
2005+ do: { :error , __MODULE__ }
19762006end
0 commit comments