برنامه نویسی

Power Apps- نحوه انجام حلقه ها در Power FX

Power FX زبان LowCode پلتفرم Power است، در اصل فقط در Canvas Apps به Dataverse و Power Virtual Agents گسترش نمی یابد. با این حال، به دلیل اینکه مبتنی بر عبارات (مانند Excel و Power Automate) است، به طور مستقیم با چیزی مانند VBA یا Python قابل مقایسه نیست. مانند اکسل، Power FX در یک سطح پایه است که با مؤلفه‌ها در هم تنیده شده است (فرمول‌ها/توابع اکسل با سلول‌ها/کاربرگ‌ها و غیره در هم تنیده می‌شوند)، این بدان معناست که عملکرد اصلی را می‌توان به مؤلفه‌ها برون سپاری کرد (مانند تایمرها/ تأخیرها).

یکی از جالب‌ترین جنبه‌های Power FX نحوه مدیریت حلقه‌ها است. خارج از جعبه، سایر زبان‌های مبتنی بر عبارت حلقه ندارند (Excel none و Power Automate به اقداماتی نیاز دارند). Power FX یک حلقه (ForAll) دارد، اما کاملاً مشابه حلقه های زبان های دیگر نیست. برای پوشش برخی از حذفیات آن جزء تایمر است که به دلیل تکرارپذیری آن می تواند به عنوان یک حلقه نیز در نظر گرفته شود.

برای همه

تابع ForAll بیشتر با یک حلقه ForEach قابل مقایسه است، زیرا برای افزایش به یک آرایه/مجموعه نیاز دارد. چند نکته کلیدی وجود دارد که باید از آنها آگاه بود:

  • شما نمی توانید متغیرها را در آن تنظیم کنید
    خطای مجموعه ForAll
  • نمی تواند خود را به روز کند (شما نمی توانید مجموعه ای را که در حال حلقه زدن هستید وصله کنید)
    خطای خود به روز رسانی ForAll
  • حلقه باید کامل شود (بدون وقفه / توقف)
  • همیشه مراحل یک (مثلاً نمی توان در 2 ثانیه افزایش داد)
  • هیچ شاخص داخلی وجود ندارد
  • همه موارد داخلی به عنوان ThisRecord ارجاع می‌شوند (حلقه‌های درون حلقه‌ها برای ارجاع به حلقه صحیح دشوار است)

اگر می‌خواهید از کاربردهای استاندارد برای موارد جالب‌تر صرفنظر کنید، اینجا را کلیک کنید

برای همه – استفاده استاندارد

بنابراین یک ForAll اولیه به شکل زیر است:

ClearCollect(array,[1,2,3,5,8,13,21]);
ForAll(array,
    Notify(ThisRecord.Value)
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اما همانطور که می بینید استفاده از آن به این شکل بی معنی است (از آنجایی که هیچ تاخیری وجود ندارد بنابراین فقط آخرین اعلان را خواهید دید).

استفاده مفیدتر ایجاد یک مجموعه جدید خواهد بود:

ClearCollect(array,[1,2,3,5,8,13,21]);
ClearCollect(array2,
    ForAll(array,
        {Value:Value+1}
    )
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در بالا مجموعه ای از 2،3،4،6،9،14،22 ایجاد می کنیم.

ما همچنین می توانیم این کار را در مجموعه های Object انجام دهیم:

ClearCollect(array,[
    {fib:1,word:"one",id:1},
    {fib:2,word:"two",id:2},
    {fib:3,word:"three",id:3},
    {fib:5,word:"five",id:4},
    {fib:8,word:"eight",id:5},
    {fib:13,word:"thirteen",id:6},
    {fib:21,word:"twenty on",id:7}
]);
ClearCollect(array2,
    ForAll(array,
        {Value:Left(word,2)}
    )
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه
نتایج مجموعه آرایه 2

اگرچه نکته سریعی است که می‌توانید از عملکردهای خاصی در مجموعه‌ها نیز استفاده کنید و نیاز به ForAll را از بین ببرید.

ClearCollect(array2,Left(array.word,2));
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

موارد فوق همان نتایج ForAll بالا را به همراه خواهد داشت.

استاندارد دیگر از آن برای به روز رسانی مجموعه دیگری با مقداری از مجموعه اول استفاده می کند:

ClearCollect(Objects,
    {a:1,b:"A",c:true,ID:1},
    {a:11,b:"B",c:false,ID:2},
    {a:-11,b:"C",c:false,ID:3},
    {a:7,b:"D",c:true,ID:4}
);
ForAll( Objects, 
    Patch(variables,{ID:ThisRecord.ID},
        {LetterNo:ThisRecord.b&ThisRecord.ID}
    );
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگرچه نکته دیگر استفاده از Drop/Add/RenameColumns برای انجام همین کار است:

ClearCollect(varaibles2,
    DropColumns(
        AddColumns(Objects,"Letter",b&ID)
    ,
        "a","b","c"
    )
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ForAll – استفاده غیر استاندارد

منظور من از استفاده غیر استاندارد چیست، خوب این روشی است برای دور زدن برخی از محدودیت هایی که ذکر کردیم.

نمی توان متغیرها را تنظیم کرد
ممکن است نتوانید متغیرها را تنظیم کنید، اما می توانید آرایه ها/مجموعه ها را به روز کنید. و یک مجموعه تک ردیف تقریباً یک شی است و یک شیء گروهی از متغیرها است (می توانید ببینید به کجا می روم). بنابراین تنها کاری که باید انجام دهیم این است که مجموعه ای با یک ردیف با فیلدهایی که می خواهید به عنوان متغیر ایجاد کنیم. سپس در ForAll از یک If برای Patch مقادیر مجموعه استفاده کنید.

ClearCollect(Objects,
    {a:1,b:"A",c:true,ID:1},
    {a:11,b:"B",c:false,ID:2},
    {a:-11,b:"C",c:false,ID:3},
    {a:7,b:"D",c:true,ID:4}
);
ClearCollect(variables3,
    {ID:1,Field_b:"",
    Field_c:false,
    Field_a:0}
);
ForAll(Objects,
    If(ThisRecord.b="B",
        Patch(variables3,{ID:1},
            {Field_b:ThisRecord.b}
        )
    );
    If(ThisRecord.c=true,
        Patch(variables3,{ID:1},
            {Field_c:ThisRecord.c}
        )
    );
      If(ThisRecord.a=-11,
        Patch(variables3,{ID:1},
            {Field_a:ThisRecord.a}
        )
    )
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

توضیحات تصویر

نمی تواند خودش را به روز کند
بر اساس تنظیم متغیرها، تنها کاری که باید انجام دهیم این است که از تابع Select() استفاده کنیم، زیرا در اینجا می‌توانیم Patch را به دکمه دیگری اضافه کنیم، که وقتی OnSelected از متغیرها/مجموعه‌ای که ایجاد کرده‌ایم استفاده می‌کند.

ClearCollect(Objects,
    {a:1,b:"A",c:true,ID:1},
    {a:11,b:"B",c:false,ID:2},
    {a:-11,b:"C",c:false,ID:3},
    {a:7,b:"D",c:true,ID:4}
);
ClearCollect(variables4,
    {ID:1,Set:0}
);
ForAll(Objects,
    If(ThisRecord.b="B",
        Patch(variables4,{ID:1},
            {Set:ThisRecord.a}
        );
        Select(buUpdateSelf);
    );
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

buUpdateSelf

Patch(Objects,{ID:Index(variables4,1).ID},{
    a: Index(variables4,1).Set
    }
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید


اگر در مورد بی نهایت تعجب می کنید
حلقه ها، متأسفانه شما نمی توانید آنها را ایجاد کنید. من سعی کردم دکمه را به اضافه کردن یک ردیف جدید تغییر دهم، اما به نظر می رسد ForAll مجموعه را در حافظه پنهان نگه می دارد، بنابراین هر تغییری پس از تکمیل آن اتفاق می افتد).

buUpdateSelf

Patch(Objects,Defaults(Objects),{
    a: Index(variables4,1).Set,
    b:"B",
    ID:Max(Objects,ID+1),
    c:false
    }
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید


حلقه باید مراحل و همیشه مرحله یک را کامل کند
با کمک یک اگر غلبه بر این بسیار آسان است:

در 2 توقف کنید

ClearCollect(Objects,
    {a:1,b:"A",c:true,ID:1},
    {a:11,b:"B",c:false,ID:2},
    {a:-11,b:"C",c:false,ID:3},
    {a:7,b:"D",c:true,ID:4}
);

ForAll( Objects, 
    If(ThisRecord.ID<3,
        Collect(variable,ThisRecord);
    );
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گام 2

ForAll( Objects, 
    If(Mod(ThisRecord.ID,2)=0,
        Collect(variable,ThisRecord);
    );
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

آخرین نکته جالب، تابع Index() است، زیرا به ما اجازه می‌دهد به رکوردهای دیگر مجموعه ارجاع دهیم. زیر یکی از قبلی، یکی از فعلی و یکی از بعدی را دریافت می کند:

ClearCollect(Objects,
    {a:1,b:"A",c:true,ID:1},
    {a:11,b:"B",c:false,ID:2},
    {a:-11,b:"C",c:false,ID:3},
    {a:7,b:"D",c:true,ID:4}
);
ClearCollect(Objects2,
    ForAll(Objects,
        Switch(ID,
        1,{a:a,b:b,c:Index(Objects,2).c,ID:ID},
        Max(Objects,ID),{a:Index(Objects,ID-1).a,b:b,c:c,ID:ID},
        {a: Index(Objects,ID-1).a,b:b,c:Index(Objects,ID+1).c,ID:ID}
        )
    )
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تایمرها

اولین تصور از یک تایمر این است که یک عمل تاخیری است. اما چون گزینه تکرار را دارد می توان از آن به عنوان حلقه نیز استفاده کرد. چه ارزشی نسبت به ForAll اضافه می کند، خوب:

  • ناهمگام است (بدون مسدود کردن، بنابراین کاربر می تواند به تعامل با برنامه ادامه دهد)
  • می تواند شامل تاخیر باشد
  • نیازی به آرایه ندارد
  • شما می توانید به هر مقدار قدم بردارید
  • شما می توانید حلقه را متوقف یا راه اندازی مجدد کنید
  • هر تابعی را می توان در حلقه فراخوانی کرد

و در مورد موارد منفی چطور:

  • کندی آن (حداکثر سرعت 1 میلی ثانیه است)
  • باید کد بیشتری بنویسی

همانطور که می بینید، تا زمانی که سرعت حیاتی نیست، یک تایمر به طور کلی بهترین حلقه برای استفاده است.

برای تبدیل یک تایمر به یک حلقه، باید پارامترهای زیر را تنظیم کنید (vbTimer فقط یک متغیر بولی است).

Repeat درست است، واقعی
Reset vbTimer
Start vbTimer
Duration 1 (یا بالاتر اگر می خواهید تاخیر بیشتری داشته باشید)
OnTimerStart یا OnTimerEnd کد شما

کد زیر نمونه ای از یک ForLoop ساده است
دکمه شروع

Set(viCounter,0);
Set(viLimit,100);
Set(vbTimer,true);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

OnTimerEnd

If(viCounter<viLimit,
    Notify(viCounter);
    viCounter=viCounter+1;
,
    Set(vbTimer,false);
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر می خواهید موارد یک مجموعه را به روز کنید، می توانید از Patch & Index استفاده کنید

Set(viCounter,1);
Set(viLimit,CountRows(Objects)+1);
Set(vbTimer,true);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

OnTimerEnd

If(viCounter<viLimit,
    viCounter=viCounter+1;
    Patch(Objects,{ID:viCounter},
        {a: Index(Objects,viCounter).a+viCounter}
    )
,
    Set(vbTimer,false)
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همچنین می توانید از آن برای پیدا کردن یک مقدار و سپس شکستن استفاده کنید:

If(viCounter>viLimit,Set(viCounter,1),Set(viCounter,viCounter+1));
If(Index(Objects,viCounter).b="D",
    Set(viFind,Index(Objects,viCounter).ID);
    Set(vbTimer,false);
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

آنچه مفید است این است که مجموعه در حافظه پنهان ذخیره نمی شود، بنابراین می توانید مجموعه را به روز کنید و در حلقه نشان داده می شود. در مثال بالا، حلقه هر مقدار را بررسی می کند، اگر نتواند آن را پیدا کند، سپس حلقه مجدداً تنظیم می شود و هر مورد را دوباره امتحان می کند. به محض به روز رسانی مجموعه، خوانده می شود و اگر با شرایط مطابقت داشت، حلقه متوقف می شود.

علاوه بر این، می تواند یک حلقه مستقیم DoWhile باشد:

If(Not(vbTimer),Set(viEndValue,viCounter));
Set(viCounter,viCounter+1);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید


اگرچه حلقه‌ها می‌توانند در PowerFX غیرمعمول باشند، اما طیف کاملی از عملکرد وجود دارد، فقط کمی دور از ذهن است.


بیشتر خواندن

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا