یک Tuple از بسیاری جهات کاملاً شبیه به یک Vector (یک array یکبعدی) است. با این حال، چند تفاوت مهم وجود دارد.
( ) نوشته میشوند نه با کروشه [ ]، هرچند در مواردی که معنی مبهم نباشد میتوان پرانتزها را حذف کرد.تغییرناپذیری امکان بهینهسازیهای بیشتری را برای کامپایلر فراهم میکند و باعث میشود تاپلها از Vector کارآمدتر باشند؛ بنابراین در مواردی که نیازی به تغییرپذیری نیست، باید تاپل را ترجیح داد.
julia> t = (3, 5.2, "xyz")
(3, 5.2, "xyz")
# Non-homogeneous:
julia> dump(t)
Tuple{Int64, Float64, String}
1: Int64 3
2: Float64 5.2
3: String "xyz"
julia> t[2]
5.2
# Immutable:
julia> t[2] = 7.3
ERROR: MethodError: no method matching setindex!(::Tuple{Int64, Float64, String}
همچنین سازندهی Tuple() وجود دارد که هر مجموعهی تکرارپذیر را به یک تاپل تبدیل میکند. نکتهی مفید این است که این سازنده با یک رشته مثل یک مجموعهی تکرارپذیر از کاراکترها رفتار میکند.
julia> Tuple("Julia")
('J', 'u', 'l', 'i', 'a')
برای هر تاپل میتوان با اندیس به عناصر آن دسترسی داشت یا روی آنها حلقه زد.
هر تاپل کاملاً عددی را هم میتوان دقیقاً مثل آرایهها در توابع ریاضی مانند sum() به کار برد.
میتوان یک تاپل را با انتساب چندگانه به عناصر جداگانهاش باز کرد. برای عناصری که لازم نیستند، از زیرخط _ به عنوان متغیر ساختگی استفاده کنید.
julia> tup = (1, "Julia", true)
(1, "Julia", true)
julia> num, name, _ = tup
(1, "Julia", true)
julia> uppercase(name), 2num
("JULIA", 2)
برای کپی کردن همهی عناصر یک تاپل در تاپل دیگری (یا بهطور کلی در یک مجموعه)، عملگر splat یعنی ... وجود دارد که آن را باز میکند. این موضوع در مفهوم توابع با جزئیات بیشتری بررسی میشود.
julia> new_tup = (tup..., π, '😊')
(1, "Julia", true, π, '😊')
یک Tuple فقط مقدارها را در خود دارد. یک NamedTuple هر مقدار را با یک اسم جفت میکند.
سپس میتوان به هر فیلد بهطور جداگانه با نماد «نقطه» دسترسی داشت:
julia> nt = (a = 1, b = 7.3)
(a = 1, b = 7.3)
julia> dump(nt)
@NamedTuple{a::Int64, b::Float64}
a: Int64 1
b: Float64 7.3
julia> nt.b
7.3
اسم در داخل بهصورت یک Symbol ذخیره میشود (نوعی تغییرناپذیر، و اسم با دونقطه : پیشوند میگیرد)، بنابراین یک نحوهی نگارش جایگزین هم در دسترس است:
julia> nt[:b]
7.3
گاهی مفید است که ساخت یک تاپل نامدار را به دو مرحله تقسیم کنید:
# Step 1
julia> Student = NamedTuple{(:name, :grade), Tuple{String, Int}}
@NamedTuple{name::String, grade::Int64}
julia> typeof(Student)
DataType
# Step 2
julia> s = Student( ("Helen", 3) )
(name = "Helen", grade = 3)
julia> dump(s)
@NamedTuple{name::String, grade::Int64}
name: String "Helen"
grade: Int64 3
راههای دیگری هم به شکلی غافلگیرکننده برای ساخت تاپل نامدار وجود دارد؛ اگر علاقهمندید، مستندات را ببینید.
بهسادگی میتوان اسمها (که «کلید» هم نامیده میشوند) یا مقدارها را جداگانه به دست آورد:
julia> keys(s)
(:name, :grade)
julia> values(s)
("Helen", 3)
توجه کنید که تکرار روی یک تاپل نامدار فقط مقدارها را تولید میکند و اسمها نادیده گرفته میشوند.
اگر اسمها در یک تکرار هم به کار بیایند، مراحل بیشتری لازم است. میتوان NamedTuple را به یک Vector از Pairs تبدیل کرد (موضوع Pairها در مفهوم دیگری بررسی میشود).
julia> pairs(s)
pairs(::NamedTuple) with 2 entries:
:name => "Helen"
:grade => 3
julia> collect(pairs(s))
2-element Vector{Pair{Symbol, Any}}:
:name => "Helen"
:grade => 3
بنابراین تاپل نامدار تا حد زیادی شبیه یک دیکشنری است. اما در چند مورد مهم با [Dict][dict] در Julia تفاوت دارد:
Dict تغییرپذیر است؛ یک NamedTuple، مثل هر تاپل دیگری، تغییرناپذیر است.Dict در نوع کلیدهایی که میپذیرد انعطافپذیر است؛ یک NamedTuple فقط میتواند از Symbolها به عنوان کلید استفاده کند.تاپلها (نامدار یا بینام) روشی بسیار رایج برای انتقال دادهی ساختاریافته به توابع و از توابع هستند. تغییرناپذیری در این کاربرد مفید است، چون امنیت داده را تضمین میکند و امکان بهینهسازیهای تهاجمیتر را برای کامپایلر فراهم میآورد.