Packages

Elixir-inspired standard library modules for Erlang

Current section

Files

Jump to
ex_stdlib src tuple.erl
Raw

src/tuple.erl

%%% @doc
%%% Tuple operations.
%%%
%%% This module provides functions for working with tuples, including creation,
%%% modification, and conversion operations. Tuples are intended as fixed-size
%%% containers for multiple elements with constant-time access but linear-time
%%% modification.
%%%
%%% Note: The following functions for tuples are found in Erlang BIFs:
%%% - element/2 - accesses a tuple by index (1-based)
%%% - setelement/3 - updates a value in a tuple by index (1-based)
%%% - tuple_size/1 - gets the number of elements in a tuple
%%%
%%% Examples:
%%% ```
%%% % Create a tuple with duplicated elements
%%% {hello, hello, hello} = tuple:duplicate(hello, 3),
%%%
%%% % Insert element at specific position (0-based indexing)
%%% {foo, bar, baz} = tuple:insert_at({bar, baz}, 0, foo),
%%%
%%% % Convert tuple to list
%%% [foo, bar, baz] = tuple:to_list({foo, bar, baz}),
%%%
%%% % Compute sum and product
%%% 6 = tuple:sum({1, 2, 3}),
%%% 24 = tuple:product({2, 3, 4}).
%%% '''
%%% @end
-module(tuple).
%% API
-export([duplicate/2, insert_at/3, delete_at/2, append/2]).
-export([to_list/1, from_list/1]).
-export([sum/1, product/1]).
-export([reverse/1, zip/2, unzip/1]).
-export([map/2, filter/2, all/2, any/2]).
-export([elem/2, put_elem/3, size/1]).
%% Type specifications
-type tuple_index() :: non_neg_integer().
-export_type([tuple_index/0]).
%%% @doc
%%% Creates a new tuple.
%%%
%%% Creates a tuple of Size containing the given Data at every position.
%%% Equivalent to Erlang's make_tuple/2.
%%%
%%% Examples:
%%% ```
%%% {hello, hello, hello} = tuple:duplicate(hello, 3),
%%% {} = tuple:duplicate(anything, 0),
%%% {42} = tuple:duplicate(42, 1).
%%% '''
%%% @end
-spec duplicate(term(), non_neg_integer()) -> tuple().
duplicate(Data, Size) when is_integer(Size), Size >= 0 ->
erlang:make_tuple(Size, Data);
duplicate(_, Size) when is_integer(Size), Size < 0 ->
error(badarg);
duplicate(_, _) ->
error(badarg).
%%% @doc
%%% Inserts an element into a tuple.
%%%
%%% Inserts Value into Tuple at the given Index (0-based).
%%% Raises badarg if Index is negative or greater than the length of Tuple.
%%%
%%% Examples:
%%% ```
%%% {foo, bar, baz} = tuple:insert_at({bar, baz}, 0, foo),
%%% {bar, baz, bong} = tuple:insert_at({bar, baz}, 2, bong).
%%% '''
%%% @end
-spec insert_at(tuple(), tuple_index(), term()) -> tuple().
insert_at(Tuple, Index, Value) when is_tuple(Tuple), is_integer(Index), Index >= 0 ->
Size = tuple_size(Tuple),
if
Index =< Size ->
erlang:insert_element(Index + 1, Tuple, Value);
true ->
error(badarg)
end;
insert_at(_, _, _) ->
error(badarg).
%%% @doc
%%% Removes an element from a tuple.
%%%
%%% Deletes the element at the given Index (0-based) from Tuple.
%%% Raises badarg if Index is negative or greater than or equal to the length of Tuple.
%%%
%%% Examples:
%%% ```
%%% {bar, baz} = tuple:delete_at({foo, bar, baz}, 0),
%%% {foo, baz} = tuple:delete_at({foo, bar, baz}, 1).
%%% '''
%%% @end
-spec delete_at(tuple(), tuple_index()) -> tuple().
delete_at(Tuple, Index) when is_tuple(Tuple), is_integer(Index), Index >= 0 ->
Size = tuple_size(Tuple),
if
Index < Size ->
erlang:delete_element(Index + 1, Tuple);
true ->
error(badarg)
end;
delete_at(_, _) ->
error(badarg).
%%% @doc
%%% Appends an element to the end of a tuple.
%%%
%%% Adds Value as the last element of Tuple, creating a new tuple.
%%% Equivalent to insert_at(Tuple, tuple_size(Tuple), Value).
%%%
%%% Examples:
%%% ```
%%% {foo, bar, baz} = tuple:append({foo, bar}, baz),
%%% {hello} = tuple:append({}, hello).
%%% '''
%%% @end
-spec append(tuple(), term()) -> tuple().
append(Tuple, Value) when is_tuple(Tuple) ->
erlang:append_element(Tuple, Value);
append(_, _) ->
error(badarg).
%%% @doc
%%% Converts a tuple to a list.
%%%
%%% Returns a new list with all the tuple elements in the same order.
%%% Equivalent to Erlang's tuple_to_list/1.
%%%
%%% Examples:
%%% ```
%%% [foo, bar, baz] = tuple:to_list({foo, bar, baz}),
%%% [] = tuple:to_list({}).
%%% '''
%%% @end
-spec to_list(tuple()) -> list().
to_list(Tuple) when is_tuple(Tuple) ->
erlang:tuple_to_list(Tuple);
to_list(_) ->
error(badarg).
%%% @doc
%%% Converts a list to a tuple.
%%%
%%% Returns a new tuple with all the list elements in the same order.
%%% Equivalent to Erlang's list_to_tuple/1.
%%%
%%% Examples:
%%% ```
%%% {foo, bar, baz} = tuple:from_list([foo, bar, baz]),
%%% {} = tuple:from_list([]).
%%% '''
%%% @end
-spec from_list(list()) -> tuple().
from_list(List) when is_list(List) ->
erlang:list_to_tuple(List);
from_list(_) ->
error(badarg).
%%% @doc
%%% Computes the sum of tuple elements.
%%%
%%% Returns the sum of all numeric elements in the tuple.
%%% Returns 0 for an empty tuple.
%%%
%%% Examples:
%%% ```
%%% 510 = tuple:sum({255, 255}),
%%% 255.0 = tuple:sum({255, 0.0}),
%%% 0 = tuple:sum({}).
%%% '''
%%% @end
-spec sum(tuple()) -> number().
sum(Tuple) when is_tuple(Tuple) ->
sum_impl(Tuple, tuple_size(Tuple));
sum(_) ->
error(badarg).
%%% @doc
%%% Computes the product of tuple elements.
%%%
%%% Returns the product of all numeric elements in the tuple.
%%% Returns 1 for an empty tuple.
%%%
%%% Examples:
%%% ```
%%% 65025 = tuple:product({255, 255}),
%%% 255.0 = tuple:product({255, 1.0}),
%%% 1 = tuple:product({}).
%%% '''
%%% @end
-spec product(tuple()) -> number().
product(Tuple) when is_tuple(Tuple) ->
product_impl(Tuple, tuple_size(Tuple));
product(_) ->
error(badarg).
%%% @doc
%%% Reverses the elements of a tuple.
%%%
%%% Returns a new tuple with elements in reverse order.
%%%
%%% Examples:
%%% ```
%%% {c, b, a} = tuple:reverse({a, b, c}),
%%% {} = tuple:reverse({}).
%%% '''
%%% @end
-spec reverse(tuple()) -> tuple().
reverse(Tuple) when is_tuple(Tuple) ->
List = erlang:tuple_to_list(Tuple),
erlang:list_to_tuple(lists:reverse(List));
reverse(_) ->
error(badarg).
%%% @doc
%%% Zips two tuples into a tuple of pairs.
%%%
%%% Combines two tuples element-wise into pairs. If the tuples have different
%%% sizes, the result will have the size of the smaller tuple.
%%%
%%% Examples:
%%% ```
%%% {{a, 1}, {b, 2}, {c, 3}} = tuple:zip({a, b, c}, {1, 2, 3}),
%%% {{a, 1}, {b, 2}} = tuple:zip({a, b}, {1, 2, 3}).
%%% '''
%%% @end
-spec zip(tuple(), tuple()) -> tuple().
zip(Tuple1, Tuple2) when is_tuple(Tuple1), is_tuple(Tuple2) ->
Size1 = tuple_size(Tuple1),
Size2 = tuple_size(Tuple2),
MinSize = min(Size1, Size2),
zip_impl(Tuple1, Tuple2, 1, MinSize, []);
zip(_, _) ->
error(badarg).
%%% @doc
%%% Unzips a tuple of pairs into two tuples.
%%%
%%% Separates a tuple of pairs into two tuples.
%%%
%%% Examples:
%%% ```
%%% {{a, b, c}, {1, 2, 3}} = tuple:unzip({{a, 1}, {b, 2}, {c, 3}}),
%%% {{}, {}} = tuple:unzip({}).
%%% '''
%%% @end
-spec unzip(tuple()) -> {tuple(), tuple()}.
unzip(Tuple) when is_tuple(Tuple) ->
unzip_impl(Tuple, 1, tuple_size(Tuple), [], []);
unzip(_) ->
error(badarg).
%%% @doc
%%% Maps a function over all elements of a tuple.
%%%
%%% Applies Fun to each element of Tuple and returns a new tuple with the results.
%%%
%%% Examples:
%%% ```
%%% {2, 4, 6} = tuple:map(fun(X) -> X * 2 end, {1, 2, 3}),
%%% {} = tuple:map(fun(X) -> X end, {}).
%%% '''
%%% @end
-spec map(fun((term()) -> term()), tuple()) -> tuple().
map(Fun, Tuple) when is_function(Fun, 1), is_tuple(Tuple) ->
List = erlang:tuple_to_list(Tuple),
MappedList = lists:map(Fun, List),
erlang:list_to_tuple(MappedList);
map(_, _) ->
error(badarg).
%%% @doc
%%% Filters elements of a tuple based on a predicate.
%%%
%%% Returns a new tuple containing only elements for which Fun returns true.
%%%
%%% Examples:
%%% ```
%%% {2, 4} = tuple:filter(fun(X) -> X rem 2 =:= 0 end, {1, 2, 3, 4}),
%%% {} = tuple:filter(fun(_) -> false end, {1, 2, 3}).
%%% '''
%%% @end
-spec filter(fun((term()) -> boolean()), tuple()) -> tuple().
filter(Fun, Tuple) when is_function(Fun, 1), is_tuple(Tuple) ->
List = erlang:tuple_to_list(Tuple),
FilteredList = lists:filter(Fun, List),
erlang:list_to_tuple(FilteredList);
filter(_, _) ->
error(badarg).
%%% @doc
%%% Tests whether all elements of a tuple satisfy a predicate.
%%%
%%% Returns true if Fun returns true for all elements, false otherwise.
%%% Returns true for an empty tuple.
%%%
%%% Examples:
%%% ```
%%% true = tuple:all(fun(X) -> X > 0 end, {1, 2, 3}),
%%% false = tuple:all(fun(X) -> X > 2 end, {1, 2, 3}),
%%% true = tuple:all(fun(_) -> false end, {}).
%%% '''
%%% @end
-spec all(fun((term()) -> boolean()), tuple()) -> boolean().
all(Fun, Tuple) when is_function(Fun, 1), is_tuple(Tuple) ->
all_impl(Fun, Tuple, 1, tuple_size(Tuple));
all(_, _) ->
error(badarg).
%%% @doc
%%% Tests whether any element of a tuple satisfies a predicate.
%%%
%%% Returns true if Fun returns true for at least one element, false otherwise.
%%% Returns false for an empty tuple.
%%%
%%% Examples:
%%% ```
%%% true = tuple:any(fun(X) -> X > 2 end, {1, 2, 3}),
%%% false = tuple:any(fun(X) -> X > 5 end, {1, 2, 3}),
%%% false = tuple:any(fun(_) -> true end, {}).
%%% '''
%%% @end
-spec any(fun((term()) -> boolean()), tuple()) -> boolean().
any(Fun, Tuple) when is_function(Fun, 1), is_tuple(Tuple) ->
any_impl(Fun, Tuple, 1, tuple_size(Tuple));
any(_, _) ->
error(badarg).
%%% @doc
%%% Gets an element from a tuple by index.
%%%
%%% Returns the element at the given Index (0-based) in Tuple.
%%% Raises badarg if Index is out of bounds.
%%%
%%% Examples:
%%% ```
%%% foo = tuple:elem({foo, bar, baz}, 0),
%%% baz = tuple:elem({foo, bar, baz}, 2).
%%% '''
%%% @end
-spec elem(tuple(), tuple_index()) -> term().
elem(Tuple, Index) when is_tuple(Tuple), is_integer(Index), Index >= 0 ->
Size = tuple_size(Tuple),
if
Index < Size ->
erlang:element(Index + 1, Tuple);
true ->
error(badarg)
end;
elem(_, _) ->
error(badarg).
%%% @doc
%%% Puts an element in a tuple by index.
%%%
%%% Returns a new tuple with Value at the given Index (0-based).
%%% Raises badarg if Index is out of bounds.
%%%
%%% Examples:
%%% ```
%%% {hello, bar, baz} = tuple:put_elem({foo, bar, baz}, 0, hello),
%%% {foo, bar, world} = tuple:put_elem({foo, bar, baz}, 2, world).
%%% '''
%%% @end
-spec put_elem(tuple(), tuple_index(), term()) -> tuple().
put_elem(Tuple, Index, Value) when is_tuple(Tuple), is_integer(Index), Index >= 0 ->
Size = tuple_size(Tuple),
if
Index < Size ->
erlang:setelement(Index + 1, Tuple, Value);
true ->
error(badarg)
end;
put_elem(_, _, _) ->
error(badarg).
%%% @doc
%%% Gets the size of a tuple.
%%%
%%% Returns the number of elements in Tuple.
%%% Equivalent to Erlang's tuple_size/1.
%%%
%%% Examples:
%%% ```
%%% 3 = tuple:size({foo, bar, baz}),
%%% 0 = tuple:size({}).
%%% '''
%%% @end
-spec size(tuple()) -> non_neg_integer().
size(Tuple) when is_tuple(Tuple) ->
tuple_size(Tuple);
size(_) ->
error(badarg).
%%%=============================================================================
%%% Internal functions
%%%=============================================================================
%% @private
sum_impl(_Tuple, 0) -> 0;
sum_impl(Tuple, Index) ->
erlang:element(Index, Tuple) + sum_impl(Tuple, Index - 1).
%% @private
product_impl(_Tuple, 0) -> 1;
product_impl(Tuple, Index) ->
erlang:element(Index, Tuple) * product_impl(Tuple, Index - 1).
%% @private
zip_impl(_Tuple1, _Tuple2, Index, MaxIndex, Acc) when Index > MaxIndex ->
erlang:list_to_tuple(lists:reverse(Acc));
zip_impl(Tuple1, Tuple2, Index, MaxIndex, Acc) ->
Elem1 = erlang:element(Index, Tuple1),
Elem2 = erlang:element(Index, Tuple2),
zip_impl(Tuple1, Tuple2, Index + 1, MaxIndex, [{Elem1, Elem2} | Acc]).
%% @private
unzip_impl(_Tuple, Index, MaxIndex, Acc1, Acc2) when Index > MaxIndex ->
{erlang:list_to_tuple(lists:reverse(Acc1)),
erlang:list_to_tuple(lists:reverse(Acc2))};
unzip_impl(Tuple, Index, MaxIndex, Acc1, Acc2) ->
{Elem1, Elem2} = erlang:element(Index, Tuple),
unzip_impl(Tuple, Index + 1, MaxIndex, [Elem1 | Acc1], [Elem2 | Acc2]).
%% @private
all_impl(_Fun, _Tuple, Index, Size) when Index > Size -> true;
all_impl(Fun, Tuple, Index, Size) ->
case Fun(erlang:element(Index, Tuple)) of
true -> all_impl(Fun, Tuple, Index + 1, Size);
false -> false
end.
%% @private
any_impl(_Fun, _Tuple, Index, Size) when Index > Size -> false;
any_impl(Fun, Tuple, Index, Size) ->
case Fun(erlang:element(Index, Tuple)) of
true -> true;
false -> any_impl(Fun, Tuple, Index + 1, Size)
end.