مفهوم Basics دو روش برای تعریف یک تابع معرفی کرد.
بهطور کلی، شکل چندخطی:
function muladd(x, y, z)
x * y + z
end
شکل «انتساب» یا «تکخطی» برای تعریفهای کوتاه:
muladd(x, y, z) = x * y + z
در شکل سوم و حتی کوتاهتر، میتوان یک تابع کوتاه و یکبارمصرف را بدون اسم ساخت:
julia> map(x -> 2x, 1:3)
4-element Vector{Int64}:
2
4
6
julia> map((x, y) -> x * y, 1:3, 4:6)
3-element Vector{Int64}:
4
10
18
در این حالت، x -> 2x یک «تابع بینام» است.
این معادل چیزی است که برخی زبانهای دیگر آن را «تابع لامبدا» مینامند.
توجه داشته باشید که چند آرگومان به پرانتز نیاز دارند، مانند (x, y) -> x * y.
توابع بینام در کد Julia رایجاند، بهویژه وقتی با توابع مرتبهبالا مانند map() و filter() ترکیب شوند.
تا اینجای سرفصل، فقط توابعی را دیدهایم که تعداد مشخصی آرگومان دارند و لازم است فراخوانی تابع همهی آنها را با ترتیب درست ارائه کند. این محدودکننده و ناراحتکننده بود، بنابراین گزینههای دیگری هم وجود دارد.
مانند بسیاری از زبانها، Julia اجازه میدهد تعریف تابع مقادیر پیشفرضی برای هر آرگومان مشخص کند.
در این صورت، فراخوانی تابع میتواند یا مقداری برای آن آرگومان بدهد، یا آن را حذف کند و به مقدار پیشفرض تکیه کند.
julia> f(x, y=10) = x * y
f (generic function with 2 methods)
julia> f(2, 3)
6
julia> f(2)
20
همهی آرگومانهای بدون پیشفرض باید پیش از آرگومانهای دارای پیشفرض بیایند، یعنی f(x=2, y) نامعتبر خواهد بود.
همهی مثالهای تا اینجا از positional arguments استفاده میکنند، جایی که مقادیر ارائهشده در فراخوانی تابع باید با ترتیب آرگومانهای متناظر در تعریف تابع مطابقت داشته باشند.
مانند بسیاری از زبانها، Julia هم keyword arguments را مجاز میداند.
فراخوانی تابع باید اسم آرگومان را مشخص کند، اما پس از آن میتوان چند آرگومان کلیدواژهای را به هر ترتیبی مشخص کرد.
ویژگی متمایز Julia این است که آرگومانهای کلیدواژهای (اگر وجود داشته باشند) در تعریف تابع باید یک نقطهویرگول ; پیش از آنها داشته باشند تا از هر آرگومان مکانی جدا شوند.
فراخوانی تابع میتواند بین آخرین آرگومان مکانی و اولین آرگومان کلیدواژهای از ; یا , استفاده کند.
julia> b(x; y) = x + y
b (generic function with 1 method)
julia> b(2, y=3)
5
# keyword is required when calling
julia> b(2, 3)
ERROR: MethodError: no method matching b(::Int64, ::Int64)
The function `b` exists, but no method is defined for this combination of argument types.
میتوان بهصورت اختیاری مقادیر پیشفرض را مشخص کرد، دقیقاً مانند آرگومانهای مکانی.
وقتی متغیری همنام با پارامتر از قبل محاسبه شده باشد، اغلب در فراخوانی تابع به نحوهی نگارشی مثل myarg=myarg میرسیم.
در این وضعیت، یک نحوهی نگارش کوتاه هم مجاز است:
julia> width = 4.0
4.0
julia> height = √ width
2.0
julia> area(; width, height) = width * height
area (generic function with 1 method)
# repetition
julia> area(; width=width, height=height)
8.0
# shorthand form
julia> area(; width, height)
8.0
در صورتی که کنجکاو بودهاید، اینها نامهای استاندارد یکی از جنبههای کاربردی نحوهی نگارش Julia هستند.
هر دو به عملگر ... اشاره دارند.
Splat کردن در فراخوانیهای تابع به کار میرود تا مجموعهها را به مقادیر جداگانهای که تابع نیاز دارد باز کند.
نمایش این موضوع ممکن است از توضیح دادنش راحتتر باشد:
julia> fxyz(x, y, z) = x * y * z
fxyz (generic function with 1 method)
julia> xyz = [2, 3, 4]
3-element Vector{Int64}:
2
3
4
# Using the vector directly in a function call is invalid
julia> fxyz(xyz)
ERROR: MethodError: no method matching fxyz(::Vector{Int64})
The function `fxyz` exists, but no method is defined for this combination of argument types.
# splatting converts the vector to 3 numbers, used as positional arguments
julia> fxyz(xyz...)
24
برخی «فراخوانیهای تابع» پشت شکر نحوی پنهان شدهاند، بنابراین Splat کردن میتواند به روشهای کمآشکارتری هم به کار رود.
برای مثال، انتساب چندگانه در باطن از یک تابع سازندهی چندتایی استفاده میکند:
julia> first, rest... = [1, 2, 3, 4]
4-element Vector{Int64}:
1
2
3
4
julia> first
1
julia> rest
3-element Vector{Int64}:
2
3
4
آرگومانهای کلیدواژهای را هم میتوان با Splat کردن ارائه داد، معمولاً با استفاده از یک named tuple.
یک Dict هم کار میکند، اما کلیدها باید symbol باشند (رشتهها اینجا کار نمیکنند).
# function with 3 keyword arguments
julia> fabc(; a, b, c) = a + b + c
fabc (generic function with 1 method)
# named tuple
julia> abc_nt = (a=2, b=3, c=4)
(a = 2, b = 3, c = 4)
# there are no positional arguments, so need to use ; before kw argument
julia> fabc(;abc_nt...)
9
# Dict
julia> abc_dict = Dict(:a=>2, :b=>3, :c=>4)
Dict{Symbol, Int64} with 3 entries:
:a => 2
:b => 3
:c => 4
julia> fabc(;abc_dict...)
9
Slurp کردن در تعریف تابع به کار میرود تا تعداد دلخواهی از مقادیر جداگانه را در یک مجموعه جمع کند.
julia> f_more(i, j, more...) = i + j + sum(more)
f_more (generic function with 1 method)
julia> f_more(1, 3, 5, 7, 9, 11)
36
اسم آرگومان Slurp شده (در اینجا more) اهمیت ندارد.
نوع این متغیر را کامپایلر انتخاب میکند، اما برای آرگومانهای مکانی احتمالاً tuple یا مشابه آن است.
آرگومانهای کلیدواژهای را هم میتوان Slurp کرد که یک Dict (یا مشابه آن) به دست میدهد.
julia> f_kwslurp(x, y; switches...) = :mult in keys(switches) ? x * y : x + y
f_kwslurp (generic function with 1 method)
julia> f_kwslurp(5, 6; mult=true)
30
julia> f_kwslurp(5, 6)
11
هر آرگومان کلیدواژهای را میتوان در فراخوانی به کار برد. این تعریف تابع است که تصمیم میگیرد به کدام کلیدواژهها پاسخ دهد و کدامها را نادیده بگیرد.