millionchart
English

انتخاب‌ها و حلقه‌ها

با if و match تصمیم بگیرید، با for و while تکرار کنید، و قاعدهٔ جهت حلقه، حلقهٔ خالی و عملگرهای and و or را یاد بگیرید.

این صفحه دستورهایی را پوشش می‌دهد که انتخاب می‌کنند و تکرار می‌کنند. اول تصمیم‌ها می‌آیند، بعد حلقه‌ها، و بعد عملگرهای and و or که قاعدهٔ آن‌ها دربارهٔ state همه را غافلگیر می‌کند.

if به‌صورت دستور#

if با یک بلاک زیرش مشخص می‌کند چه کاری انجام شود. else if و else اختیاری‌اند.

Cuprel
indicator "Zone number"
    edition = 1

mut zone = 0
if close > open + 1
    zone = 2
else if close > open
    zone = 1
else
    zone = -1
plot(zone, name = "Zone", style = histogram)

دستور if مقدار ندارد. فقط خط‌های داخل بلاکش اجرا می‌شوند.

if به‌صورت مقدار#

if ... then ... else ... در یک خط یک عبارت است. مقدار دارد، برای همین به else نیاز دارد.

Cuprel
indicator "If as a value"
    edition = 1
    display = price

let direction = if close > open then 1 else -1
plot(close, name = "Close", color = if direction > 0 then color.green else color.red)

نوشتن ifای که مقدارش استفاده می‌شود ولی else ندارد، E2001 است.

match#

match یکی از چند حالت را انتخاب می‌کند. اگر بعد از match مقداری بیاید (مقدار مورد بررسی)، هر حالت مقداری است که با آن مقایسه می‌شود:

Cuprel
indicator "Match on a value"
    edition = 1

input mode: text = "Fast", name = "Mode", options = ["Fast", "Normal", "Slow"]
let length = match mode
    "Fast" => 8
    "Normal" => 21
    _ => 55
plot(tech.ema(close, length), name = "Average")

اگر بعد از match مقداری نباشد، هر حالت یک شرط است و اولین شرطِ درست برنده می‌شود. این نمونه فقط از شرط‌ها استفاده می‌کند. (اگر بعد از match مقداری باشد، یک حالت می‌تواند با if یک شرط نگهبان هم اضافه کند: 0 if close > open => 10 هم مقدار را لازم دارد هم شرط را.)

Cuprel
indicator "Match with conditions"
    edition = 1

let state_of_bar = match
    close > open + 1 => 2
    close > open => 1
    close < open - 1 => -2
    close < open => -1
    _ => 0
plot(state_of_bar, name = "Bar state", style = histogram)

_ حالتی است که با هر چیزی جور درمی‌آید و باید آخرین حالت باشد. بدون آن، کندل‌هایی که با هیچ حالتی جور نشوند na می‌گیرند و بررسی‌کننده اخطار می‌دهد:

Cuprel · هشدار W-MATCH-OPEN
indicator "Match without a default"
    edition = 1

let size = match int(close) % 2
    0 => 1
    1 => 2
plot(size)

match روی یک enum اعضای آن را با نام می‌آورد: Mode.fast => 8. مقدارها و نوع‌ها enum را معرفی می‌کند.

for#

for i in a to b از a تا خود b می‌شمارد و b هم جزو آن است. until انتها را کنار می‌گذارد. by اندازهٔ گام را تعیین می‌کند.

Cuprel
indicator "Loop ranges"
    edition = 1

mut up = 0
for i in 0 to 3
    up += 1
mut until_end = 0
for i in 0 until 3
    until_end += 1
mut stepped = 0
for i in 0 to 10 by 5
    stepped += 1
plot(up, name = "0 to 3: four passes")
plot(until_end, name = "0 until 3: three passes")
plot(stepped, name = "0 to 10 by 5: three passes")
Cuprel
indicator "Counting down"
    edition = 1

mut forwards = 0
for i in 3 to 0
    forwards += 1
mut backwards = 0
for i in 3 to 0 by -1
    backwards += 1
plot(forwards, name = "3 to 0: nothing")
plot(backwards, name = "3 to 0 by -1: four passes")

این وقتی بیشتر اهمیت پیدا می‌کند که انتهای حلقه از یک متغیر بیاید. for i in 0 to n - 1 وقتی n = 0 باشد از 0 تا -1 می‌شمارد: هیچ دوری اجرا نمی‌شود و چیزی هم خراب نمی‌شود. حلقه‌ای که روی هیچ می‌چرخد خطا نیست.

Cuprel
indicator "Sum of the last n closes"
    edition = 1

input n: int = 10, name = "Bars", min = 0, max = 1000
mut total = 0.0
for i in 0 to n - 1
    total += close[i]
plot(total, name = "Sum")

گام صفر هرگز جلو نمی‌رود، برای همین بررسی‌کننده صفرِ نوشته‌شده را رد می‌کند (E2041) و اگر گام صفر یا na هنگام اجرا برسد، موتور اجرا را متوقف می‌کند (E-LOOP-STEP).

Cuprel · خطا E2041
indicator "A step of zero"
    edition = 1
mut count = 0
for i in 0 to 5 by 0
    count += 1
plot(count)

for ... in روی یک مجموعه#

for x in items هر عضو یک آرایه را یکی‌یکی می‌بیند. for (i, x) in items جای عضو را هم می‌دهد. .keys() در یک نگاشت (map) آرایه‌ای از کلیدهای آن است.

Cuprel
indicator "Walk an array"
    edition = 1

let levels = [high, low, close]
mut total = 0.0
mut weighted = 0.0
for (i, level_) in levels
    total += level_
    weighted += level_ * (i + 1)
plot(total, name = "Sum")
plot(weighted, name = "Weighted sum")

while، break و continue#

while تا وقتی شرطش درست است تکرار می‌کند. break از نزدیک‌ترین حلقه بیرون می‌رود و continue به دور بعدی آن می‌پرد.

Cuprel
indicator "Bars since a higher high"
    edition = 1

mut found = -1
for i in 1 to 20
    if close[i] > high
        found = i
        break
mut odd = 0
for i in 1 to 10
    if i % 2 == 0
        continue
    odd += 1
plot(found, name = "Bars back to a higher close (-1: none)")
plot(odd, name = "Odd numbers up to 10")

موتور هر حلقه را محدود می‌کند: یک کندل می‌تواند 500,000 گام مصرف کند و کل اجرا 600,000,000 گام. حلقه‌ای که تمام نشود اسکریپت را با E-LIMIT-OPS متوقف می‌کند و کندل و خط را نام می‌برد. سقف‌ها و بودجهٔ اجرا را ببینید.

حلقه نمی‌تواند تابعی را صدا بزند که بین کندل‌ها state نگه می‌دارد (tech.sma، tech.crosses_above، تابعی از خودتان که state دارد)، چون هر دور یک حافظه را با دورهای دیگر شریک می‌شد. آن را یک بار قبل از حلقه صدا بزنید، یا صدا زدن‌ها را یکی‌یکی بنویسید.

Cuprel · خطا E3301
indicator "A stateful call in a loop"
    edition = 1

mut total = 0.0
for i in 1 to 3
    total += tech.sma(close, 5)
plot(total)

and، or و not#

and، or و not روی درست و نادرست کار می‌کنند. اولویت not از مقایسه کمتر است، پس not a == b یعنی not (a == b).

سمت راست فقط وقتی اجرا می‌شود که لازم باشد. a and b فقط وقتی a درست است به b نگاه می‌کند، a or b فقط وقتی a نادرست است، a ?? b فقط وقتی a برابر na است، و if c then x else y فقط شاخه‌ای را اجرا می‌کند که انتخاب شده. نگهبانی مثل i < n and items.at(i) > 0 هرگز بیرون از آرایه نمی‌خواند.

یک نوع فراخوانی نباید آنجا بایستد. فراخوانی‌ای که state نگه می‌دارد، مثل فراخوانی tech.*، data.series، یک state که می‌خواند یا تابعی از خودتان که state نگه می‌دارد، برای همان خطی که در آن نوشته شده مقدارها را به خاطر می‌سپارد و این حافظه فقط وقتی جلو می‌رود که آن خط اجرا شود. اگر مستقیم در سمت راست and نوشته شود، حافظه‌اش فقط در کندل‌هایی جلو می‌رود که سمت چپ درست بوده، و این به‌ندرت همان چیزی است که خط می‌گوید. زبان آن را با E3302 رد می‌کند:

Cuprel · خطا E3302
indicator "A call that keeps state in the right side"
    edition = 1

let trend = close > tech.sma(close, 50)
let signal = trend and tech.crosses_above(close, tech.sma(close, 10))
plot(if signal then 1 else 0)

منظورتان را به یکی از دو راه می‌گویید. فراخوانی را اول در خطی جدا بنویسید تا در هر کندل اجرا شود:

Cuprel
indicator "Run on every bar"
    edition = 1

let trend = close > tech.sma(close, 50)
let crossed = tech.crosses_above(close, tech.sma(close, 10))
let signal = trend and crossed
plot(if signal then 1 else 0)

یا شرط را به‌صورت بلوک if بنویسید تا فراخوانی فقط همان‌جا که بلوک اجرا می‌شود اجرا شود. هشدار W3302 برای این است که کسی آن را اشتباهاً «هر کندل» نخواند: در کندل‌هایی که بلوک اجرا نمی‌شود، حافظهٔ فراخوانی همان‌طور که بود می‌ماند.

Cuprel · هشدار W3302
indicator "Run only where reached"
    edition = 1

let trend = close > tech.sma(close, 50)
let signal = if trend
    tech.crosses_above(close, tech.sma(close, 10))
else
    false
plot(if signal then 1 else 0)

دو خط شمارش متفاوتی دارند. در اولی تقاطع هر کندل را می‌بیند. در دومی فقط کندل‌هایی را می‌بیند که trend در آن‌ها درست بوده، پس یک کندل را با آخرین کندلی مقایسه می‌کند که دیده، و آن می‌تواند چند ساعت قدیمی‌تر باشد. وقتی منظورتان همین است از بلوک استفاده کنید، مثلاً برای پرهیز از یک فراخوانی پرهزینه؛ وقتی فراخوانی state نگه می‌دارد و باید هر کندل را ببیند، آن را اول ببندید. همین قاعده برای شاخه‌های مقدار if ... then ... else و سمت راست ?? هم هست.

نگاه کنید به: تابع‌ها، نوع‌ها و enum‌ها، متغیرها، اشتباه‌های رایج.