struct Tuple(*T)

Overview

A tuple is a fixed-size, immutable, stack-allocated sequence of values of possibly different types.

You can think of a Tuple as an immutable Array whose types for each position are known at compile time.

A tuple can be created with the usual .new method or with a tuple literal:

tuple = {1, "hello", 'x'} # Tuple(Int32, String, Char)
tuple[0]                  # => 1
tuple[1]                  # => "hello"
tuple[2]                  # => 'x'

The compiler knows what types are in each position, so when indexing a tuple with an integer literal the compiler will return the value in that index and with the expected type, like in the above snippet. Indexing with an integer literal outside the bounds of the tuple will give a compile-time error.

Indexing with an integer value that is only known at runtime will return a value whose type is the union of all the types in the tuple, and might raise IndexError.

Tuples are the preferred way to return fixed-size multiple return values because no memory is needed to be allocated for them:

def one_and_hello
  {1, "hello"}
end

one, hello = one_and_hello
one   # => 1
hello # => "hello"

Good examples of the above are Number#divmod and Enumerable#minmax.

Tuples can be splat with the * operator and passed to methods:

def multiply(string, value)
  string * value
end

tuple = {"hey", 2}
value = multiply(*tuple) # same as multiply tuple[0], tuple[1]
value                    # => "heyhey"

Finally, when using a splat argument in a method definition its type will be a tuple of the call arguments:

def splat_test(*args)
  args
end

tuple = splat_test 1, "hello", 'x'
tuple.class # => Tuple(Int32, String, Char)
tuple       # => {1, "hello", 'x'}

Included Modules

Defined in:

json/to_json.cr
tuple.cr
yaml/to_yaml.cr

Constructors

Class Method Summary

Instance Method Summary

Instance methods inherited from module Comparable(Tuple(*T))

<(other : T) <, <=(other : T) <=, <=>(other : T) <=>, ==(other : T) ==, >(other : T) >, >=(other : T) >=, clamp(min, max)
clamp(range : Range)
clamp

Instance methods inherited from module Indexable(Union(*T))

[](index : Int) [], []?(index : Int) []?, bsearch(&block : Union(T) -> Bool) bsearch, bsearch_index(&block : Union(T), Int32 -> Bool) bsearch_index, combinations(size : Int = self.size) combinations, dig(index : Int, *subindexes) dig, dig?(index : Int, *subindexes) dig?, each(*, within range : Range, &)
each
each(&)
each(*, start : Int, count : Int, &)
each
, each_combination(size : Int = self.size, reuse = false, &) : Nil
each_combination(size : Int = self.size, reuse = false)
each_combination
, each_index(&) : Nil
each_index
each_index(*, start : Int, count : Int, &)
each_index
, each_permutation(size : Int = self.size, reuse = false)
each_permutation(size : Int = self.size, reuse = false, &) : Nil
each_permutation
, each_repeated_combination(size : Int = self.size, reuse = false, &) : Nil
each_repeated_combination(size : Int = self.size, reuse = false)
each_repeated_combination
, empty? empty?, equals?(other : Indexable, &)
equals?(other, &)
equals?
, fetch(index, default)
fetch(index : Int, &)
fetch
, first(&) first, hash(hasher) hash, index(offset : Int = 0, &)
index(object, offset : Int = 0)
index
, join(separator : String | Char | Number = "") join, last
last(&)
last
, last? last?, permutations(size : Int = self.size) permutations, repeated_combinations(size : Int = self.size) repeated_combinations, reverse_each(&) : Nil
reverse_each
reverse_each
, rindex(value, offset = size - 1)
rindex(offset = size - 1, &)
rindex
, sample(random = Random::DEFAULT) sample, size size, to_a to_a, unsafe_fetch(index : Int) unsafe_fetch, values_at(*indexes : Int) values_at

Instance methods inherited from module Enumerable(Union(*T))

all?(&)
all?(pattern)
all?
all?
, any?(&)
any?(pattern)
any?
any?
, chunks(&block : Union(T) -> U) forall U chunks, compact_map(&) compact_map, count(&)
count(item)
count
, cycle(&)
cycle(n, &)
cycle
, each(&block : Union(T) -> _) each, each_cons(count : Int, reuse = false, &) each_cons, each_cons_pair(& : Union(T), Union(T) -> _) : Nil each_cons_pair, each_slice(count : Int, reuse = false, &) each_slice, each_with_index(offset = 0, &) each_with_index, each_with_object(obj, &) each_with_object, empty? empty?, find(if_none = nil, &) find, first(&)
first(count : Int)
first
first
, first? first?, flat_map(&block : Union(T) -> Array(U) | Iterator(U) | U) forall U flat_map, group_by(&block : Union(T) -> U) forall U group_by, in_groups_of(size : Int, filled_up_with : U = nil) forall U
in_groups_of(size : Int, filled_up_with : U = nil, reuse = false, &) forall U
in_groups_of
, includes?(obj) includes?, index(&)
index(obj)
index
, index_by(&block : Union(T) -> U) forall U index_by, join(separator = "", & : Union(T) -> )
join(separator, io : IO, &)
join(io : IO, separator = "", & : Union(T), IO -> )
join(separator = "")
join(separator, io : IO)
join(io : IO, separator = "")
join
, map(&block : Union(T) -> U) forall U map, map_with_index(offset = 0, &block : Union(T), Int32 -> U) forall U map_with_index, max max, max? max?, max_by(&block : Union(T) -> U) forall U max_by, max_by?(&block : Union(T) -> U) forall U max_by?, max_of(&block : Union(T) -> U) forall U max_of, max_of?(&block : Union(T) -> U) forall U max_of?, min min, min? min?, min_by(&block : Union(T) -> U) forall U min_by, min_by?(&block : Union(T) -> U) forall U min_by?, min_of(&block : Union(T) -> U) forall U min_of, min_of?(&block : Union(T) -> U) forall U min_of?, minmax minmax, minmax? minmax?, minmax_by(&block : Union(T) -> U) forall U minmax_by, minmax_by?(&block : Union(T) -> U) forall U minmax_by?, minmax_of(&block : Union(T) -> U) forall U minmax_of, minmax_of?(&block : Union(T) -> U) forall U minmax_of?, none?(pattern)
none?(&)
none?
none?
, one?(&)
one?(pattern)
one?
one?
, partition(&) partition, product(initial : Number)
product
product(initial : Number, &)
product(&)
product
, reduce(memo, &)
reduce(&)
reduce
, reduce?(&) reduce?, reject(pattern)
reject(type : U.class) forall U
reject(&block : Union(T) -> )
reject
, sample(n : Int, random = Random::DEFAULT)
sample(random = Random::DEFAULT)
sample
, select(pattern)
select(type : U.class) forall U
select(&block : Union(T) -> )
select
, size size, skip(count : Int) skip, skip_while(&) skip_while, sum(initial)
sum
sum(initial, &)
sum(&)
sum
, take_while(&) take_while, tally : Hash(Union(T), Int32) tally, to_a to_a, to_h
to_h(&block : Union(T) -> Tuple(K, V)) forall K, V
to_h
, to_set to_set, zip(*others : Indexable | Iterable | Iterator, &)
zip(*others : Indexable | Iterable | Iterator)
zip
, zip?(*others : Indexable | Iterable | Iterator, &)
zip?(*others : Indexable | Iterable | Iterator)
zip?

Instance methods inherited from module Iterable(Union(*T))

chunk(reuse = false, &block : Union(T) -> U) forall U chunk, chunk_while(reuse : Bool | Array(Union(T)) = false, &block : Union(T), Union(T) -> B) forall B chunk_while, cycle(n)
cycle
cycle
, each each, each_cons(count : Int, reuse = false) each_cons, each_slice(count : Int, reuse = false) each_slice, each_with_index(offset = 0) each_with_index, each_with_object(obj) each_with_object, slice_after(reuse : Bool | Array(Union(T)) = false, &block : Union(T) -> B) forall B
slice_after(pattern, reuse : Bool | Array(Union(T)) = false)
slice_after
, slice_before(reuse : Bool | Array(Union(T)) = false, &block : Union(T) -> B) forall B
slice_before(pattern, reuse : Bool | Array(Union(T)) = false)
slice_before
, slice_when(reuse : Bool | Array(Union(T)) = false, &block : Union(T), Union(T) -> B) forall B slice_when

Instance methods inherited from struct Value

==(other : JSON::Any)
==(other : YAML::Any)
==(other)
==
, dup dup

Instance methods inherited from class Object

! : Bool !, !=(other) !=, !~(other) !~, ==(other) ==, ===(other : JSON::Any)
===(other : YAML::Any)
===(other)
===
, =~(other) =~, as(type : Class) as, as?(type : Class) as?, class class, dup dup, hash(hasher)
hash
hash
, in?(*values : Object) : Bool
in?(collection) : Bool
in?
, inspect : String
inspect(io : IO) : Nil
inspect
, is_a?(type : Class) : Bool is_a?, itself itself, nil? : Bool nil?, not_nil! not_nil!, pretty_inspect(width = 79, newline = "\n", indent = 0) : String pretty_inspect, pretty_print(pp : PrettyPrint) : Nil pretty_print, responds_to?(name : Symbol) : Bool responds_to?, tap(&) tap, to_json(io : IO)
to_json
to_json
, to_pretty_json(io : IO, indent : String = " ")
to_pretty_json(indent : String = " ")
to_pretty_json
, to_s : String
to_s(io : IO) : Nil
to_s
, to_yaml(io : IO)
to_yaml
to_yaml
, try(&) try, unsafe_as(type : T.class) forall T unsafe_as

Class methods inherited from class Object

from_json(string_or_io, root : String)
from_json(string_or_io)
from_json
, from_yaml(string_or_io : String | IO) from_yaml

Constructor Detail

def self.from(array : Array) : self #

Creates a tuple from the given array, with elements casted to the given types.

Tuple(String, Int64).from(["world", 2_i64])       # => {"world", 2_i64}
Tuple(String, Int64).from(["world", 2_i64]).class # => Tuple(String, Int64)

See also: #from.


[View source]
def self.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node) #

[View source]
def self.new(pull : JSON::PullParser) #

[View source]
def self.new(*args : *T) #

Creates a tuple that will contain the given values.

This method is useful in macros and generic code because with it you can create empty tuples, something that you can't do with a tuple literal.

Tuple.new(1, "hello", 'x') #=> {1, "hello", 'x'}
Tuple.new                  #=> {}

{}                         # syntax error

[View source]

Class Method Detail

def self.types #

Returns the types of this tuple type.

tuple = {1, "hello", 'x'}
tuple.class.types # => {Int32, String, Char}

[View source]

Instance Method Detail

def +(other : Tuple) #

Returns a tuple that contains self's elements followed by other's elements.

t1 = {1, 2}
t2 = {"foo", "bar"}
t3 = t1 + t2
t3         # => {1, 2, "foo", "bar"}
typeof(t3) # => Tuple(Int32, Int32, String, String)

[View source]
def <=>(other : self) #

The comparison operator.

Each object in each tuple is compared using the #<=> operator.

Tuples are compared in an "element-wise" manner; the first element of this tuple is compared with the first one of other using the #<=> operator, then each of the second elements, etc. As soon as the result of any such comparison is non-zero (i.e. the two corresponding elements are not equal), that result is returned for the whole tuple comparison.

If all the elements are equal, then the result is based on a comparison of the tuple sizes. Thus, two tuples are "equal" according to #<=> if, and only if, they have the same size and the value of each element is equal to the value of the corresponding element in the other tuple.

{"a", "a", "c"} <=> {"a", "b", "c"} # => -1
{1, 2, 3, 4, 5, 6} <=> {1, 2}       # => 1
{1, 2} <=> {1, 2.0}                 # => 0

[View source]
def <=>(other : Tuple) #

The comparison operator.

Each object in each tuple is compared using the #<=> operator.

Tuples are compared in an "element-wise" manner; the first element of this tuple is compared with the first one of other using the #<=> operator, then each of the second elements, etc. As soon as the result of any such comparison is non-zero (i.e. the two corresponding elements are not equal), that result is returned for the whole tuple comparison.

If all the elements are equal, then the result is based on a comparison of the tuple sizes. Thus, two tuples are "equal" according to #<=> if, and only if, they have the same size and the value of each element is equal to the value of the corresponding element in the other tuple.

{"a", "a", "c"} <=> {"a", "b", "c"} # => -1
{1, 2, 3, 4, 5, 6} <=> {1, 2}       # => 1
{1, 2} <=> {1, 2.0}                 # => 0

[View source]
def ==(other : self) #

Returns true if this tuple has the same size as the other tuple and their elements are equal to each other when compared with #==.

t1 = {1, "hello"}
t2 = {1.0, "hello"}
t3 = {2, "hello"}

t1 == t2 # => true
t1 == t3 # => false

[View source]
def ==(other : Tuple) #

Returns true if this tuple has the same size as the other tuple and their elements are equal to each other when compared with #==.

t1 = {1, "hello"}
t2 = {1.0, "hello"}
t3 = {2, "hello"}

t1 == t2 # => true
t1 == t3 # => false

[View source]
def ==(other) #
Description copied from struct Value

Returns false.


[View source]
def ===(other : self) #

Returns true if case equality holds for the elements in self and other.

{1, 2} === {1, 2} # => true
{1, 2} === {1, 3} # => false

See also: Object#===.


[View source]
def ===(other : Tuple) #

Returns true if self and other have the same size and case equality holds for the elements in self and other.

{1, 2} === {1, 2, 3}             # => false
{/o+/, "bar"} === {"foo", "bar"} # => true

See also: Object#===.


[View source]
def [](index : Int) #

Returns the element at the given index. Read the type docs to understand the difference between indexing with a number literal or a variable.

tuple = {1, "hello", 'x'}
tuple[0] # => 1 (Int32)
tuple[3] # compile error: index out of bounds for tuple {Int32, String, Char}

i = 0
tuple[i] # => 1 (Int32 | String | Char)

i = 3
tuple[i] # raises IndexError

[View source]
def []?(index : Int) #

Returns the element at the given index or nil if out of bounds.

tuple = {1, "hello", 'x'}
tuple[0]? # => 1
tuple[3]? # => nil

[View source]
def at(index : Int) #

Returns the element at the given index or raises IndexError if out of bounds.

tuple = {1, "hello", 'x'}
tuple.at(0) # => 1
tuple.at(3) # raises IndexError

[View source]
def at(index : Int, &) #

Returns the element at the given index or the value returned by the block if out of bounds.

tuple = {1, "hello", 'x'}
tuple.at(0) { 10 } # => 1
tuple.at(3) { 10 } # => 10

[View source]
def clone #

Returns a tuple containing cloned elements of this tuple using the #clone method.


[View source]
def each(&) : Nil #

Yields each of the elements in this tuple.

tuple = {1, "hello", 'x'}
tuple.each do |value|
  puts value
end

Output:

1
"hello"
'x'

[View source]
def first #

Returns the first element of this tuple. Doesn't compile if the tuple is empty.

tuple = {1, 2.5}
tuple.first # => 1

[View source]
def first? #

Returns the first element of this tuple, or nil if this is the empty tuple.

tuple = {1, 2.5}
tuple.first? # => 1

empty = Tuple.new
empty.first? # => nil

[View source]
def from(array : Array) #

Expects to be called on a tuple of types, creates a tuple from the given array, with types casted appropriately.

This allows you to easily pass an array as individual arguments to a method.

require "json"

def speak_about(thing : String, n : Int64)
  "I see #{n} #{thing}s"
end

data = JSON.parse(%(["world", 2])).as_a.map(&.raw)
speak_about(*{String, Int64}.from(data)) # => "I see 2 worlds"

[View source]
def hash(hasher) #

[View source]
def inspect : String #

Same as #to_s.


[View source]
def last #

Returns the last element of this tuple. Doesn't compile if the tuple is empty.

tuple = {1, 2.5}
tuple.last # => 2.5

[View source]
def last? #

Returns the last element of this tuple, or nil if this is the empty tuple.

tuple = {1, 2.5}
tuple.last? # => 2.5

empty = Tuple.new
empty.last? # => nil

[View source]
def map(&) #

Returns a new tuple where elements are mapped by the given block.

tuple = {1, 2.5, "a"}
tuple.map &.to_s # => {"1", "2.5", "a"}

[View source]
def map_with_index(offset = 0, &) #

Like #map, but the block gets passed both the element and its index.

tuple = {1, 2.5, "a"}
tuple.map_with_index { |e, i| "tuple[#{i}]: #{e}" } # => {"tuple[0]: 1", "tuple[1]: 2.5", "tuple[2]: a"}

Accepts an optional offset parameter, which tells it to start counting from there.


[View source]
def pretty_print(pp) : Nil #

[View source]
def reverse #

Returns a new tuple where the elements are in reverse order.

tuple = {1, 2.5, "a"}
tuple.reverse # => {"a", 2.5, 1}

[View source]
def reverse_each(&) #

Yields each of the elements in this tuple in reverse order.

tuple = {1, "hello", 'x'}
tuple.reverse_each do |value|
  puts value
end

Output:

'x'
"hello"
1

[View source]
def size #

Returns the number of elements in this tuple.

{'a', 'b'}.size # => 2

[View source]
def to_a #
Description copied from module Indexable(Union(*T))

Returns an Array with all the elements in the collection.

{1, 2, 3}.to_a # => [1, 2, 3]

[View source]
def to_json(json : JSON::Builder) #

[View source]
def to_s(io : IO) : Nil #

Appends a string representation of this tuple to the given IO.

tuple = {1, "hello"}
tuple.to_s # => "{1, \"hello\"}"

[View source]
def to_yaml(yaml : YAML::Nodes::Builder) #

[View source]
def unsafe_fetch(index : Int) #
Description copied from module Indexable(Union(*T))

Returns the element at the given index, without doing any bounds check.

Indexable makes sure to invoke this method with index in 0...size, so converting negative indices to positive ones is not needed here.

Clients never invoke this method directly. Instead, they access elements with #[](index) and #[]?(index).

This method should only be directly invoked if you are absolutely sure the index is in bounds, to avoid a bounds check for a small boost of performance.


[View source]