millionchart
English

تابع‌ها، نوع‌ها و enum‌ها

با fn تابع خودتان را بنویسید، مقدارها را زودتر یا چندتا با هم برگردانید، به یک نوع متد اضافه کنید و بدانید state در تابع چطور کار می‌کند.

تابع یک محاسبه را زیر یک نام بسته‌بندی می‌کند. آن را با fn بنویسید، بالای کدی بگذارید که از آن استفاده می‌کند، و مثل یک تابع آماده صدایش بزنید.

یک تابع#

تابعی که بلاک دارد آخرین عبارتش یا مقدار return را برمی‌گرداند. تابعی که در یک خط نوشته می‌شود از = استفاده می‌کند.

Cuprel
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 به خواننده می‌گوید تابع یک عدد صحیح می‌گیرد و یک عدد می‌دهد، ولی اگر متن به آن بدهید رد نمی‌شود.

یک پارامتر می‌تواند مقدار پیش‌فرض داشته باشد، و آرگومان را می‌شود با نام داد:

Cuprel
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 تابع را همان لحظه و هر جا که باشد تمام می‌کند. تابع می‌تواند از آن به‌عنوان نگهبان استفاده کند:

Cuprel
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")
Cuprel
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 داخل تابع*، پایین‌تر).

Cuprel · خطا E3310
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")

نسخهٔ اصلاح‌شده:

Cuprel
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 متوقف می‌کند؛ سقف‌ها و بودجهٔ اجرا را ببینید.

چند مقدار با هم#

تابع می‌تواند یک تاپل (چند مقدار با هم) برگرداند و کدی که تابع را صدا می‌زند آن را از هم باز می‌کند. تعداد نام‌ها باید با تعداد مقدارها برابر باشد.

Cuprel
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)
Cuprel · خطا E3143
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 استفاده می‌کند برای هر جایی که از آن صدا زده می‌شود یک حافظهٔ جداگانه نگه می‌دارد. دو فراخوانی، دو حافظه:

Cuprel
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, ...) نوشته شود متدی از آن نوع است و با نقطه صدا زده می‌شود.

Cuprel
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 فهرستی بسته از نام‌هاست که هر کدام در یک خط نوشته می‌شود. یک عضو می‌تواند برچسب داشته باشد:

Cuprel
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 توضیح می‌دهد.

نگاه کنید به: مقدارها و نوع‌ها، ماژول‌ها و کتابخانه‌ها، ورودی‌ها.