درس 6: تابعهای خودتان
یک محاسبه را با fn زیر یک نام بگذارید: پارامترها و مقدار پیشفرض، نگهبان با return، چند نتیجه با هم، و تابعهایی که به یاد میسپارند.
درس 6 از 18. وقتی یک محاسبه را دو بار مینویسید، یا محاسبهای ارزش یک نام دارد، آن را در یک تابع بگذارید. تابع را با fn و بالای کدی که از آن استفاده میکند مینویسید، و مثل یک تابع داخلی صدایش میزنید.
تابعی در یک خط#
indicator "Percent change"
edition = 1
display = pane
fn percent_change(now: float, before: float): float = (now - before) / before * 100
input back: int = 10, name = "Bars back", min = 1, max = 200
plot(percent_change(close, close[back]), name = "Change %", style = histogram)
level(0, name = "Zero", color = color.gray)fn percent_change(now: float, before: float): floatنام تابع و دو پارامتر آن را میگوید. داخل تابع،nowوbeforeهمان مقدارهاییاند که فراخوانی داده است.- بعد از
=نتیجه میآید. تابعی که در یک خط نوشته شود، یک عبارت است. - نوعها (
float) به خواننده کمک میکنند. در edition 1 موتور آنها را بررسی نمیکند.
تابعی با یک بلوک#
تابع بلندتر یک بلوک تورفته دارد. return نتیجه را میدهد و تابع را همان لحظه تمام میکند؛ به همین دلیل نگهبان خوبی است:
indicator "Small bodies"
edition = 1
display = price
fn body_share(o: float, c: float, h: float, l: float): float
let span = h - l
if span == 0
return na
return math.abs(c - o) / span
input limit: float = 0.1, name = "Largest share", min = 0.01, max = 1.0, step = 0.01
let share = body_share(open, close, high, low)
mark.char(share < limit, name = "Small body", char = "s", place = above, color = color.gray)
plot(close, name = "Close", color = color.gray)body_share بدنهٔ کندل را به صورت سهمی از کل دامنهٔ آن میدهد: 0 برای کندلی که در همان قیمتی بسته شد که باز شده بود، و 1 برای کندلی که سایه ندارد. کندلی که سقف و کفش برابر است، تقسیم بر صفر میشد؛ پس نگهبان اول na برمیگرداند.
مقدار پیشفرض و نام آرگومان#
پارامتر میتواند مقدار پیشفرض داشته باشد، و فراخوانی میتواند آرگومانی را که میدهد با نام بیاورد:
indicator "Envelope"
edition = 1
display = price
fn envelope(source: float, length: int, percent: float = 1.0): (float, float, float)
let middle = tech.sma(source, length)
let offset = middle * percent / 100
return (middle + offset, middle, middle - offset)
let (upper, middle, lower) = envelope(close, 20, percent = 0.5)
plot(upper, name = "Upper", color = color.red)
plot(middle, name = "Middle", color = color.gray)
plot(lower, name = "Lower", color = color.green)این تابع سه مقدار را با هم برمیگرداند، یک تاپل؛ (float, float, float) همین را میگوید. فراخوان آنها را با let (upper, middle, lower) = ... از هم جدا میکند؛ تعداد نامها باید با تعداد مقدارها برابر باشد. برای مقداری که لازم ندارید _ بنویسید: let (upper, _, lower) = envelope(close, 20).
تابعی که به یاد میسپارد#
تابع میتواند state یا تابعی مثل tech.ema به کار ببرد. آن وقت هر جایی که تابع از آن صدا زده میشود، حافظهٔ خودش را دارد:
indicator "Rising in a row"
edition = 1
display = pane
fn rising_count(x: float): int
state count = 0
if x > x[1]
count += 1
else
count = 0
return count
plot(rising_count(close), name = "Closes rising in a row", color = color.blue)
plot(rising_count(volume), name = "Volume rising in a row", color = color.orange)دو فراخوانی count را با هم شریک نیستند: یکی بستهشدنهای رو به بالا را میشمارد و دیگری حجم رو به بالا را. تابع نمیتواند mut یا stateی را که بیرون از آن اعلام شده تغییر دهد؛ مقدار را به صورت پارامتر به آن بدهید و مقدار تازه را به صورت نتیجه بگیرید.
تابعی که به یاد میسپارد کجا میتواند بیاید#
چون چنین تابعی کندلهایی را که دیده به یاد دارد، باید در هر کندل اجرا شود. اگر در سمت راست and نوشته شود، فقط وقتی اجرا میشود که سمت چپ درست باشد، و بررسیکننده آن را نمیپذیرد:
indicator "Called only sometimes"
edition = 1
display = pane
fn rising_count(x: float): int
state count = 0
if x > x[1]
count += 1
else
count = 0
return count
let strong = close > open and rising_count(close) > 2
plot(if strong then 1 else 0, name = "Strong")اول آن را در یک خط جدا صدا بزنید، بعد نتیجه را به کار ببرید:
indicator "Called on every bar"
edition = 1
display = pane
fn rising_count(x: float): int
state count = 0
if x > x[1]
count += 1
else
count = 0
return count
let rising = rising_count(close)
let strong = close > open and rising > 2
plot(if strong then 1 else 0, name = "Strong")تابع نمیتواند خودش را صدا بزند (E3310)؛ به جای آن از حلقه استفاده کنید. بقیهٔ قاعدهها در تابعها، نوعها و enumها آمده است.
امتحان کنید#
- در «Percent change»،
percent_change(close, open)را صدا بزنید: هیستوگرام تغییر از بازشدن تا بستهشدن هر کندل میشود. - در «Small bodies»، Largest share را روی 0.05 بگذارید: کندلهای کمتری علامت میخورند.
- در «Envelope»،
percent = 0.5را حذف کنید: پیشفرض 1.0 به کار میرود و باندها دورتر میروند. - در «Rising in a row»، خط سومی اضافه کنید:
plot(rising_count(high), name = "Highs rising in a row")؛ این فراخوانی هم حافظهٔ خودش را میگیرد.
نقطهٔ بررسی#
- میتوانید تابعی در یک خط و تابعی با بلوک بنویسید و از
returnبه عنوان نگهبان استفاده کنید. - میتوانید به پارامتر مقدار پیشفرض بدهید و آرگومان را با نامش بفرستید.
- میتوانید چند مقدار برگردانید و آنها را با
let (a, b, c) = ...از هم جدا کنید. - میدانید که هر فراخوانیِ تابعی که به یاد میسپارد حافظهٔ خودش را دارد و باید در هر کندل صدا زده شود.