[erlang-patches] Add lists:find/2,3

Sean Cribbs <>
Wed Nov 13 20:35:54 CET 2013


I've changed and force-pushed my pull-request to adjust the return values.
Instead of the bare element being returned, it is now wrapped in a tuple as
{value, Elem}. If nothing passes the predicate, 'false' is the new default
return value rather than 'undefined'. I believe this makes it more
consistent with other functions in the lists module, e.g. lists:keyfind/3.

Please refetch: https://github.com/erlang/otp/pull/102


On Wed, Nov 13, 2013 at 9:51 AM, Serge Aleynikov <>wrote:

> Thanks Steve for clarification. Though for that type of matching there's
> lists:keymember/3 (*) that covers a good portion of suggested use cases, I
> can see that in more complex scenarios find/2 could be useful, but is it
> generic enough to warrant an addition to the lists module? If find/2 is
> accepted, it seems to me that the index_of/2 prototyped in my prior email
> could be another valuable candidate, as that is currently not doable via a
> combination of functions in the lists module (other than by means of
> lists:foldl/3 and throw, which is a bit ugly).
>
> (*) 1> lists:keymember(a, 1, [{c, 2}, d, {e, 3}, [a,b,c], {a, 4}]).
> true
>
>
>
>
> On Wed, Nov 13, 2013 at 9:23 AM, Steve Vinoski <> wrote:
>
>> lists:member/2 matches (using =:=) entire elements to determine whether
>> or not they are present in the list, whereas the proposed lists:find/2,3
>> allows the caller to pass a fun to test for element presence. The fun
>> allows the caller to ignore fields of an element that's a tuple, record
>> etc. for purposes of determining whether something is or is not present in
>> the list. Therefore lists:member/2 can't do what the proposed
>> lists:find/2,3 can do.
>>
>> --steve
>>
>>
>>
>> On Wed, Nov 13, 2013 at 9:08 AM, Serge Aleynikov <>wrote:
>>
>>> Since finding an element in the list is already doable via
>>> lists:member/2, it seems to me that there's very little benefit in that
>>> find/2 function.  Whereas a more valuable addition would be to find an
>>> index of the element in the given list.  An example of a common use case
>>> for that would be to locate a field's position in a mnesia table by name
>>> (e.g. index_of):
>>>
>>> update_table(Table, FieldName, Value) ->
>>>   Attrs = mnesia:table_info(Table, attributes),
>>>   {ok, Pos} = index_of(FieldName, Attrs),
>>>   R = mnesia:dirty_read(Table, Key),
>>>   R1 = setelement(Pos, R, NewValue),
>>>   ok = mnesia:dirty_write(Table, R1).
>>>
>>> index_of(Value, List) ->
>>>   index_of(Value, List, 1).
>>> index_of(V, [V|T], N) ->
>>>   {ok, N};
>>> index_of(V, [_|T], N) ->
>>>   index_of(V, T, N+1);
>>> index_of(_, [], _) ->
>>>   false.
>>>
>>>
>>> On Wed, Nov 13, 2013 at 5:48 AM, Thomas Järvstrand <
>>> > wrote:
>>>
>>>> A more generic version would be to return the first result of applying
>>>> the fun to an element that does not return false. That way you can choose
>>>> whether to return the actual element, the result of the computation you
>>>> just or even something completely different. The user is also free to wrap
>>>> the result in case he/she is worried that the return value might be `false`.
>>>>
>>>> Example implementation:
>>>>
>>>> *find(_MatchF, []) ->*
>>>>
>>>> *  false;*
>>>>
>>>>
>>>>
>>>>
>>>> *find(MatchF, [X|Xs]) ->   case MatchF(X) of    false -> find(MatchF,
>>>> Xs);    Val   -> Val  end.*
>>>>
>>>> Thomas
>>>>
>>>>
>>>> 2013/10/22 Anthony Ramine <>
>>>>
>>>>> Forwarding to list.
>>>>>
>>>>> --
>>>>> Anthony Ramine
>>>>>
>>>>> Le 22 oct. 2013 à 11:56, Anthony Ramine <> a écrit :
>>>>>
>>>>> > Maybe we could just have dropuntil(F, L) -> dropwhile(fun (X) -> not
>>>>> F(X) end, L).
>>>>> >
>>>>> > Of course, the function would be written less naively to avoid
>>>>> constructing a new fun.
>>>>> >
>>>>> > --
>>>>> > Anthony Ramine
>>>>> >
>>>>> > Le 14 oct. 2013 à 15:52, Sean Cribbs <> a écrit :
>>>>> >
>>>>> >> #1: Would tagging the return value address like Masklinn mentions
>>>>> address your concern? {ok, Term} | error (instead of a default value)?
>>>>> >>
>>>>> >> #2: Also, I considered dropwhile but it involves introducing
>>>>> another fun, whereas the version I wrote is direct.
>>>>> >>
>>>>> >>
>>>>> >> On Mon, Oct 14, 2013 at 5:28 AM, Anthony Ramine <>
>>>>> wrote:
>>>>> >> Hello Sean,
>>>>> >>
>>>>> >> I don't like this patch.
>>>>> >>
>>>>> >> 1/ You can't know whether an element was found or not if the list
>>>>> elements are arbitrary terms.
>>>>> >> 2/ I don't see why you would say people hack either foreach/2 or
>>>>> foldl/3 to do that when they should just use dropwhile/2.
>>>>> >>
>>>>> >> A naive implementation of find/2 using dropwhile/2:
>>>>> >>
>>>>> >> find(F, L) ->
>>>>> >>        case dropwhile(fun (E) -> not F(E) end, L) of
>>>>> >>                [] -> undefined;
>>>>> >>                [E|_] -> E
>>>>> >>        end.
>>>>> >>
>>>>> >> Regards,
>>>>> >>
>>>>> >> --
>>>>> >> Anthony Ramine
>>>>> >>
>>>>> >> Le 14 oct. 2013 à 02:44, Sean Cribbs <> a écrit :
>>>>> >>
>>>>> >>> `lists:find/2,3` returns the first element of the passed list for
>>>>> which the predicate fun returns `true`. If no elements result in the
>>>>> predicate being true, `undefined` (/2) or the given default value (/3) is
>>>>> returned.
>>>>> >>>
>>>>> >>> ## Why this new feature?
>>>>> >>>
>>>>> >>> A common task is to select the first element from a list that
>>>>> matches a condition, but there is no existing lists function or language
>>>>> feature that avoids traversing the entire list, while still returning a
>>>>> "safe" value. `lists:find/2,3` codifies the pattern of a tail-recursive
>>>>> search for the matching item without resorting to exceptions (used to abort
>>>>> `foreach/2` or `foldl/3`) and always returns either the first matching
>>>>> item, or an otherwise safe value.
>>>>> >>>
>>>>> >>> ## Risks / uncertain artifacts
>>>>> >>>
>>>>> >>> It is unclear the desired order of arguments for the 3-arity
>>>>> version. I have made the default value the final argument which is
>>>>> consistent with `application:get_env/3` and `proplists:get_value/3`, but
>>>>> most functions in lists place the `List` argument last.
>>>>> >>>
>>>>> >>> ## How did you solve it?
>>>>> >>>
>>>>> >>> Following the patterns of other functions in the lists module,
>>>>> `lists:find/3` tests the predicate function against the head of the list,
>>>>> returning the head if the predicate passes, or recursing over the tail if
>>>>> it does not.
>>>>> >>>
>>>>> >>> https://github.com/erlang/otp/pull/102
>>>>> >>>
>>>>> >>> --
>>>>> >>> Sean Cribbs <>
>>>>> >>> Software Engineer
>>>>> >>> Basho Technologies, Inc.
>>>>> >>> http://basho.com/
>>>>> >>> _______________________________________________
>>>>> >>> erlang-patches mailing list
>>>>> >>> 
>>>>> >>> http://erlang.org/mailman/listinfo/erlang-patches
>>>>> >>
>>>>> >>
>>>>> >>
>>>>> >>
>>>>> >> --
>>>>> >> Sean Cribbs <>
>>>>> >> Software Engineer
>>>>> >> Basho Technologies, Inc.
>>>>> >> http://basho.com/
>>>>> >
>>>>>
>>>>> _______________________________________________
>>>>> erlang-patches mailing list
>>>>> 
>>>>> http://erlang.org/mailman/listinfo/erlang-patches
>>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> erlang-patches mailing list
>>>> 
>>>> http://erlang.org/mailman/listinfo/erlang-patches
>>>>
>>>>
>>>
>>> _______________________________________________
>>> erlang-patches mailing list
>>> 
>>> http://erlang.org/mailman/listinfo/erlang-patches
>>>
>>>
>>
>
> _______________________________________________
> erlang-patches mailing list
> 
> http://erlang.org/mailman/listinfo/erlang-patches
>
>


-- 
Sean Cribbs <>
Software Engineer
Basho Technologies, Inc.
http://basho.com/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-patches/attachments/20131113/959de6d0/attachment.html>


More information about the erlang-patches mailing list