تابعها، نوعها و enumها
با fn تابع خودتان را بنویسید، مقدارها را زودتر یا چندتا با هم برگردانید، به یک نوع متد اضافه کنید و بدانید state در تابع چطور کار میکند.
تابع یک محاسبه را زیر یک نام بستهبندی میکند. آن را با fn بنویسید، بالای کدی بگذارید که از آن استفاده میکند، و مثل یک تابع آماده صدایش بزنید.
یک تابع#
تابعی که بلاک دارد آخرین عبارتش یا مقدار return را برمیگرداند. تابعی که در یک خط نوشته میشود از = استفاده میکند.
indicator "Two functions"
edition = 1
fn range_percent(length: int): float
let top = tech.highest(high, length)
let bottom = tech.lowest(low, length)
return (top - bottom) / bottom * 100
fn middle(a: float, b: float): float = (a + b) / 2
plot(range_percent(20), name = "20-bar range, %")
plot(middle(high, low), name = "Middle of the bar")نوعهایی که بعد از نامها میآیند مستندات اختیاری برای خوانندهاند. در نسخهٔ زبان (edition) 1 موتور آنها را بررسی نمیکند: (length: int): float به خواننده میگوید تابع یک عدد صحیح میگیرد و یک عدد میدهد، ولی اگر متن به آن بدهید رد نمیشود.
یک پارامتر میتواند مقدار پیشفرض داشته باشد، و آرگومان را میشود با نام داد:
indicator "Defaults and names"
edition = 1
fn scaled(x: float, factor: float = 2.0): float = x * factor
plot(scaled(close), name = "Doubled")
plot(scaled(close, factor = 0.5), name = "Halved")خروج زودهنگام#
return تابع را همان لحظه و هر جا که باشد تمام میکند. تابع میتواند از آن بهعنوان نگهبان استفاده کند:
indicator "A guard"
edition = 1
fn safe_ratio(a: float, b: float): float
if b == 0
return na
return a / b
plot(safe_ratio(close - open, high - low), name = "Body as a share of the range")indicator "Absolute value"
edition = 1
fn absolute(x: float): float
return if x >= 0 then x else -x
plot(absolute(close - open), name = "Body size")تابع نمیتواند خودش را صدا بزند#
تابع میتواند تابعهای دیگر را صدا بزند، اما نه خودش را، و نه از راه زنجیرهای از فراخوانیها که دوباره به خودش برگردد. بررسیکننده آن را با E3310 رد میکند و زنجیره را نام میبرد. دلیلش این است که عمق چنین زنجیرهای را همان دستگاهی تعیین میکرد که اسکریپت را اجرا میکند؛ پس همان اسکریپت میتوانست روی یک دستگاه کار کند و روی دستگاه دیگر متوقف شود. حلقه بنویسید، یا مقدار در حال جمع شدن را در یک state نگه دارید (بخش *state داخل تابع*، پایینتر).
indicator "A function that calls itself"
edition = 1
fn total(n: int): int = if n < 1 then 0 else n + total(n - 1)
plot(total(5), name = "Sum")نسخهٔ اصلاحشده:
indicator "A loop instead"
edition = 1
fn total(n: int): int
mut sum = 0
for i in 1 to n
sum += i
return sum
plot(total(5), name = "Sum")تابعهایی که یکدیگر را صدا میزنند بیآنکه به اولی برگردند مشکلی ندارند. موتور همچنین میشمارد که چند تابع از تابعهای شما همزمان در جریاناند، یکی درون دیگری، و کندلی را که از 64 بگذرد با E-LIMIT-DEPTH متوقف میکند؛ سقفها و بودجهٔ اجرا را ببینید.
چند مقدار با هم#
تابع میتواند یک تاپل (چند مقدار با هم) برگرداند و کدی که تابع را صدا میزند آن را از هم باز میکند. تعداد نامها باید با تعداد مقدارها برابر باشد.
indicator "Channel"
edition = 1
display = price
fn channel(length: int): (float, float, float)
let middle = tech.sma(close, length)
let spread = tech.stdev(close, length) * 2
return (middle + spread, middle, middle - spread)
let (upper, middle, lower) = channel(20)
plot(upper, name = "Upper", color = color.red)
plot(middle, name = "Middle", color = color.gray)
plot(lower, name = "Lower", color = color.green)indicator "Too many names"
edition = 1
fn pair(): (float, float) = (1.0, 2.0)
let (a, b, c) = pair()
plot(a)برای مقداری که لازمش ندارید از _ استفاده کنید: let (upper, _, lower) = channel(20). بهعنوان متغیر حلقه هم کار میکند: for _ in 0 to 4.
state داخل تابع#
تابعی که از tech.* یا state استفاده میکند برای هر جایی که از آن صدا زده میشود یک حافظهٔ جداگانه نگه میدارد. دو فراخوانی، دو حافظه:
indicator "Two calls, two memories"
edition = 1
fn smoothed(x: float): float = tech.ema(x, 5)
plot(smoothed(close), name = "Smoothed close")
plot(smoothed(open), name = "Smoothed open")دو خط روی هم اثر نمیگذارند: هر فراخوانی سری خودش را به یاد دارد. تابع نمیتواند mut یا stateای را که بیرون از آن تعریف شده تغییر دهد (E3104)؛ مقدار را بهصورت آرگومان به آن بدهید و مقدار جدید را از نتیجهاش بگیرید. متغیرها این خطا را نشان میدهد.
نوعها با متد#
یک type فیلد دارد. تابعی که به شکل fn TypeName.method(self, ...) نوشته شود متدی از آن نوع است و با نقطه صدا زده میشود.
indicator "A type with a method"
edition = 1
type Level
price: float
width: float = 1.0
fn Level.top(self): float = self.price + self.width
fn Level.bottom(self): float = self.price - self.width
let level_ = Level(price = close, width = 2.0)
plot(level_.top(), name = "Top", color = color.red)
plot(level_.bottom(), name = "Bottom", color = color.green)رکورد را با نام بردن از فیلدهایش میسازید. میتوانید به یک نوع آماده هم متدی از خودتان بدهید، مثل fn array.size(self). آنوقت متد شما بر متد آمادهٔ همنام برتری پیدا میکند و بررسیکننده دربارهٔ آن اخطار میدهد (W-METHOD-SHADOWS-BUILTIN) تا بدانید چنین چیزی رخ داده است.
همین شکل نقطهدار، تابع متن را روی یک متن صدا میزند: "10:30".split(":") همان text.split("10:30", ":") است.
Enumها#
enum فهرستی بسته از نامهاست که هر کدام در یک خط نوشته میشود. یک عضو میتواند برچسب داشته باشد:
indicator "An enum input"
edition = 1
enum Speed
fast = "Fast"
normal = "Normal"
slow = "Slow"
input speed: Speed = Speed.normal, name = "Speed"
let length = match speed
Speed.fast => 8
Speed.normal => 21
Speed.slow => 55
plot(tech.ema(close, length), name = "Average")پنل تنظیمات ورودیِ enum را بهصورت یک فهرست کشویی با برچسبها نشان میدهد. match روی enum راه عمل کردن بر اساس آن است. انتخابها و حلقهها دربارهٔ match توضیح میدهد.
نگاه کنید به: مقدارها و نوعها، ماژولها و کتابخانهها، ورودیها.