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 قابل مقایسه است، زیرا برای افزایش به یک آرایه/مجموعه نیاز دارد. چند نکته کلیدی وجود دارد که باید از آنها آگاه بود:
- شما نمی توانید متغیرها را در آن تنظیم کنید
- نمی تواند خود را به روز کند (شما نمی توانید مجموعه ای را که در حال حلقه زدن هستید وصله کنید)
- حلقه باید کامل شود (بدون وقفه / توقف)
- همیشه مراحل یک (مثلاً نمی توان در 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)}
)
);
نتیجه
اگرچه نکته سریعی است که میتوانید از عملکردهای خاصی در مجموعهها نیز استفاده کنید و نیاز به 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
vbTimerStart
vbTimerDuration
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 غیرمعمول باشند، اما طیف کاملی از عملکرد وجود دارد، فقط کمی دور از ذهن است.
بیشتر خواندن