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

Anthony Ramine <>
Wed Nov 13 21:25:39 CET 2013


For fun and bonus points, you could inline these two functions with +inline_list_funcs.

See sys_core_fold:call_1/5.

-- 
Anthony Ramine

Le 13 nov. 2013 à 20:35, Sean Cribbs <> a écrit :

> 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/
> _______________________________________________
> erlang-patches mailing list
> 
> http://erlang.org/mailman/listinfo/erlang-patches



More information about the erlang-patches mailing list