+
+-spec first_match([{Tag, fun((A) -> boolean())}], A) ->
+ hope_option:t(Tag).
+first_match([], _) ->
+ none;
+first_match([{Tag, F} | Tests], X) ->
+ case F(X)
+ of true -> {some, Tag}
+ ; false -> first_match(Tests, X)
+ end.
+
+%% @doc Divide list into sublists of up to a requested size + a remainder.
+%% Order unspecified. Size < 1 raises an error:
+%% `hope_list__divide__size_must_be_a_positive_integer'
+%% @end
+-spec divide([A], pos_integer()) ->
+ [[A]].
+divide(_, Size) when Size < 1 orelse not is_integer(Size) ->
+ % Q: Why?
+ % A: For N < 0, what does it mean to have a negative-sized chunk?
+ % For N = 0, we can imagine that a single chunk is an empty list, but,
+ % how many such chunks should we produce?
+ % This is pretty-much equivalnet to the problem of deviding something by 0.
+ error(hope_list__divide__size_must_be_a_positive_integer);
+divide([], _) ->
+ [];
+divide([X1 | Xs], MaxChunkSize) ->
+ MoveIntoChunks =
+ fun (X2, {Chunk, Chunks, ChunkSize}) when ChunkSize >= MaxChunkSize ->
+ {[X2], [Chunk | Chunks], 1}
+ ; (X2, {Chunk, Chunks, ChunkSize}) ->
+ {[X2 | Chunk], Chunks, ChunkSize + 1}
+ end,
+ {Chunk, Chunks, _} = lists:foldl(MoveIntoChunks, {[X1], [], 1}, Xs),
+ [Chunk | Chunks].