foreach විවිධ වර්ගයේ අගයන් තුනකට වඩා පුනරාවර්තනයට සහය දක්වයි:
පහත දැක්වෙන අවස්ථා වලදී, විවිධ අවස්ථා වලදී නැවත ක්රියා කරන ආකාරය හරියටම පැහැදිලි කිරීමට මම උත්සාහ කරමි. වඩාත්ම සරලම අවස්ථාව වන්නේ Traversableවස්තූන් වන අතර, මේවා foreachඅත්යවශ්යයෙන්ම මෙම රේඛා ඔස්සේ කේත සඳහා සින්ටැක්ස් සීනි පමණි:
foreach ($it as $k => $v) { /* ... */ }
/* translates to: */
if ($it instanceof IteratorAggregate) {
$it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
$v = $it->current();
$k = $it->key();
/* ... */
}
අභ්යන්තර පංති සඳහා, Iteratorසී ඒ මට්ටමේ අතුරු මුහුණත පිළිබිඹු කරන අභ්යන්තර API භාවිතා කිරීමෙන් සත්ය ක්රම ඇමතුම් වළක්වා ගත හැකිය .
අරා සහ සරල වස්තූන් අනුකරණය කිරීම සැලකිය යුතු ලෙස සංකීර්ණ වේ. පළමුවෙන්ම, PHP හි “අරා” සැබවින්ම ඇණවුම් කරන ලද ශබ්දකෝෂ වන අතර ඒවා මෙම ඇණවුමට අනුව ගමන් කරනු ඇත (ඔබ ඇතුළු දෙයක් භාවිතා නොකළ තාක් කල් එය ඇතුළත් කිරීමේ අනුපිළිවෙලට ගැලපේ sort). යතුරු වල ස්වාභාවික අනුපිළිවෙල (වෙනත් භාෂාවල ලැයිස්තු බොහෝ විට ක්රියා කරන ආකාරය) හෝ කිසිසේත්ම නිශ්චිත අනුපිළිවෙලක් නොමැති වීම (වෙනත් භාෂාවල ශබ්දකෝෂ බොහෝ විට ක්රියා කරන ආකාරය) මගින් මෙය නැවත කිරීමට විරුද්ධ වේ.
වස්තු ගුණාංග වෙනත් (ඇණවුම් කරන ලද) ශබ්ද කෝෂයක් ලෙස දේපල නම් ඒවායේ අගයන්ට අනුරූපණය කිරීම සහ දෘශ්යතාව හැසිරවීම ලෙස දැකිය හැකි බැවින් එය වස්තු සඳහා ද අදාළ වේ. බොහෝ අවස්ථාවන්හීදී, වස්තු ගුණාංග ඇත්ත වශයෙන්ම මෙම අකාර්යක්ෂම ආකාරයෙන් ගබඩා නොවේ. කෙසේ වෙතත්, ඔබ වස්තුවක් හරහා නැවත යෙදීම ආරම්භ කරන්නේ නම්, සාමාන්යයෙන් භාවිතා වන ඇසුරුම් නිරූපණය සැබෑ ශබ්ද කෝෂයක් බවට පරිවර්තනය වේ. එම අවස්ථාවෙහිදී, සරල වස්තූන්ගේ පුනරාවර්තනය අරා වල ක්රියාකාරීත්වයට බොහෝ සෙයින් සමාන වේ (මේ නිසා මම මෙහි බොහෝ දුරට සරල-වස්තු පුනරාවර්තනය ගැන සාකච්ඡා නොකරමි).
මේ වනතෙක් ගොඩක් හොඳයි. ශබ්ද කෝෂයක් ගැන කතා කිරීම එතරම් අපහසු විය නොහැක, නේද? නැවත ආරම්භයේදී අරාව / වස්තුව වෙනස් විය හැකි බව ඔබ තේරුම් ගත් විට ගැටළු ආරම්භ වේ. මෙය සිදුවිය හැකි විවිධ ක්රම තිබේ:
- ඔබ යොමුව භාවිතා කර
foreach ($arr as &$v)නැවත යොමු කරන්නේ නම් $arrඑය යොමු කිරීමක් බවට පත් වන අතර ඔබට එය නැවත සිදුකිරීමේදී වෙනස් කළ හැකිය.
- PHP 5 හි ඔබ අගය අනුව පුනරාවර්තනය කළත් එය අදාළ වේ, නමුත් අරාව කලින් යොමු කිරීමක් විය:
$ref =& $arr; foreach ($ref as $v)
- වස්තූන් සතුව හැසිරවීමේ අර්ථකථන ඇත, බොහෝ ප්රායෝගික අරමුණු සඳහා ඒවා යොමු දැක්වීම් මෙන් හැසිරේ. එබැවින් පුනරාවර්තනයේදී වස්තූන් සැමවිටම වෙනස් කළ හැකිය.
පුනරාවර්තනයේදී වෙනස් කිරීමට ඉඩ දීමේ ගැටළුව වන්නේ ඔබ දැනට සිටින මූලද්රව්යය ඉවත් කිරීමයි. ඔබ දැනට සිටින්නේ කුමන අරාවෙහි මූලද්රව්යයද යන්න නිරීක්ෂණය කිරීමට ඔබ දර්ශකයක් භාවිතා කරන බව පවසන්න. මෙම මූලද්රව්යය දැන් නිදහස් කර ඇත්නම්, ඔබට ඉතිරිව ඇත්තේ දර්ශක දර්ශකයක් පමණි (සාමාන්යයෙන් එහි ප්රති se ලයක් ලෙස සෙග්ෆෝල්ට්).
මෙම ගැටළුව විසඳීමට විවිධ ක්රම තිබේ. PHP 5 සහ PHP 7 මේ සම්බන්ධයෙන් සැලකිය යුතු ලෙස වෙනස් වන අතර මම පහත සඳහන් හැසිරීම් දෙකම විස්තර කරමි. සාරාංශය නම්, PHP 5 හි ප්රවේශය තරමක් ගොළු වූ අතර සියලු ආකාරයේ අමුතු අද්භූත ගැටළු වලට තුඩු දෙන අතර, PHP 7 හි වැඩි සම්බන්ධිත ප්රවේශයක් මඟින් වඩාත් පුරෝකථනය කළ හැකි හා ස්ථාවර හැසිරීම් ඇති කරයි.
අවසාන මූලික වශයෙන්, මතකය කළමනාකරණය කිරීම සඳහා PHP යොමු ගණන් කිරීම සහ පිටපත් මත ලිවීම භාවිතා කරන බව සැලකිල්ලට ගත යුතුය. මෙයින් අදහස් කරන්නේ ඔබ අගයක් "පිටපත්" කරන්නේ නම්, ඔබ ඇත්ත වශයෙන්ම පැරණි අගය නැවත භාවිතා කර එහි විමර්ශන ගණන (refcount) වැඩි කරන බවයි. ඔබ යම් ආකාරයක වෙනස් කිරීමක් කළ පසු පමණක් සත්ය පිටපතක් ("අනුපිටපතක්" ලෙස හැඳින්වේ) සිදු කෙරේ. බලන්න කියලා බොරු කරමින් ඔබ මෙම මාතෘකාව මත වඩාත් පුළුල් හඳුන්වා දීම සඳහා.
PHP 5
අභ්යන්තර අරාව දර්ශකය සහ හැෂ්පොයින්ටර්
PHP 5 හි ඇති අරා වලට එක් "අභ්යන්තර අරා පොයින්ටරයක්" (IAP) ඇත, එය නිවැරදිව වෙනස් කිරීම් සඳහා සහය දක්වයි: මූලද්රව්යයක් ඉවත් කළ විට, IAP මෙම මූලද්රව්යයට යොමු කරන්නේ දැයි පරීක්ෂා කරනු ලැබේ. එය එසේ නම්, එය ඒ වෙනුවට ඊළඟ අංගයට දියුණු වේ.
අතර foreachඑම IAP සකස් කරලා පාවිච්චි කරන, අතිරේක යුද්ධය නිමාකර ඇත: එක් IAP ඇත, නමුත් එක් මාලාවක් බහු කොටසක් විය හැකි foreachවළළු:
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
foreach ($arr as &$v) {
// ...
}
}
එක් අභ්යන්තර අරා පොයින්ටරයක් සහිත එකවර ලූප දෙකකට සහය දැක්වීම foreachසඳහා, පහත දැක්වෙන ෂෙනානිගන් සිදු කරයි: ලූප් බොඩි ක්රියාත්මක foreachකිරීමට පෙර, වත්මන් මූලද්රව්යයට දර්ශකයක් සහ එහි හැෂ් එක පෙර-පුරෝකථනයකට උපස්ථ HashPointerකරයි. ලූප් බොඩි ධාවනය වූ පසු, IAP තවමත් පවතී නම් මෙම මූලද්රව්යය වෙත නැවත සැකසෙනු ඇත. කෙසේ වෙතත් මූලද්රව්යය ඉවත් කර ඇත්නම්, අපි දැනට IAP සිටින ඕනෑම තැනක භාවිතා කරමු. මෙම යෝජනා ක්රමය බොහෝ දුරට කාරුණික ආකාරයේ වැඩකි, නමුත් ඔබට එයින් මිදිය හැකි අමුතු හැසිරීම් රාශියක් ඇත, ඒවායින් සමහරක් මම පහත නිරූපණය කරමි.
අරාව අනුපිටපත් කිරීම
IAP යනු අරාවෙහි දෘශ්යමාන ලක්ෂණයකි ( currentශ්රිත පවුල හරහා නිරාවරණය වේ), IAP ගණනය කිරීම්වල එවැනි වෙනස්කම් පිටපත් මත ලිවීමේ අර්ථ නිරූපණය යටතේ වෙනස් කිරීම් ලෙස දැක්විය හැකිය. මෙය, අවාසනාවකට, එයින් අදහස් වන්නේ foreachඑය බොහෝ විට එය නැවත නැවතත් අරාව අනුපිටපත් කිරීමට බල කරන බවයි. නිශ්චිත කොන්දේසි:
- අරාව යොමු කිරීමක් නොවේ (is_ref = 0). එය සඳහන් වේ නම්, ඒ සඳහා වෙනස් වේ යැයි කියනු ප්රචාරය කිරීමට, එම නිසා එය නැවත කළ යුතු නොවේ.
- අරාවෙහි refcount> 1 ඇත. නම්
refcount1, පසුව අරාවට හවුල් වී නැති අතර එය අපි කෙලින්ම එය වෙනස් කිරීමට නිදහස්.
අරාව අනුපිටපත් නොකළේ නම් (is_ref = 0, refcount = 1), එවිට එය පමණක් refcountවැඩි වේ (*). මීට අමතරව, foreachයොමු කිරීම මඟින් භාවිතා කරන්නේ නම්, (අනුපිටපත් කළ හැකි) අරාව යොමු කිරීමක් බවට පත් වේ.
අනුපිටපත් සිදු වන උදාහරණයක් ලෙස මෙම කේතය සලකා බලන්න:
function iterate($arr) {
foreach ($arr as $v) {}
}
$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
$arrIAP වෙනස්කම් $arrකාන්දු වීම වැළැක්වීම සඳහා මෙහි අනුපිටපත් කරනු ලැබේ $outerArr. ඉහත කොන්දේසි අනුව, අරාව යොමු කිරීමක් නොවේ (is_ref = 0) එය ස්ථාන දෙකක භාවිතා වේ (refcount = 2). මෙම අවශ්යතාවය අවාසනාවන්ත වන අතර උපපෝෂිත ක්රියාවට නැංවීමේ පුරාවස්තුවකි (මෙහි නැවත සිදුකිරීමේදී වෙනස් කිරීම ගැන කිසිදු තැකීමක් නොමැත, එබැවින් අපට මුලින් ම IAP භාවිතා කිරීම අවශ්ය නොවේ).
(*) එම Incrementing refcountමෙතන ඉතුරු ශබ්ද, නමුත් පිටපතක්-on-ලිවීම් (ගව) semantics උල්ලංඝනය: ගව ආඥාවන් වෙනස් කිරීම් පමණක් refcount = මත සිදු කළ හැකි බව වන අතර අප එය refcount = 2 අරාවක් IAP වෙනස් කිරීමට යන්නේ එම මෙම අදහස් අගයන් 1 යි. මෙම උල්ලං violation නය හේතුවෙන් පරිශීලකයාට දෘශ්යමාන හැසිරීම් වෙනසක් සිදු වේ (COW සාමාන්යයෙන් පාරදෘශ්ය වන අතර), නැවත සැකසූ අරාවෙහි IAP වෙනස නිරීක්ෂණය කළ හැකි වනු ඇත - නමුත් අරාවෙහි පළමු IAP නොවන වෙනස් කිරීම තෙක් පමණි. ඒ වෙනුවට, "වලංගු" විකල්ප තුන වනු ඇත්තේ අ) සැමවිටම අනුපිටපත් කිරීම, ආ) වැඩි නොකරන්න refcountසහ එමඟින් පුනරාවර්තී අරාව අත්තනෝමතික ලෙස ලූපයේ වෙනස් කිරීමට ඉඩ දීම හෝ ඇ) IAP කිසිසේත් භාවිතා නොකරන්න (PHP 7 විසඳුම).
ස්ථානගත කිරීමේ අනුපිළිවෙල
පහත දැක්වෙන කේත සාම්පල නිසි ලෙස අවබෝධ කර ගැනීම සඳහා ඔබ දැනුවත් විය යුතු අවසාන ක්රියාත්මක කිරීමේ විස්තරයක් ඇත. සමහර දත්ත ව්යුහයක් හරහා “සාමාන්ය” ලූපය ව්යාජ කේතයෙන් පෙනේ:
reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
code();
move_forward(arr);
}
කෙසේ වෙතත් foreach, තරමක් විශේෂ හිම පියල්ලක් වීම නිසා දේවල් තරමක් වෙනස් ලෙස කිරීමට තෝරා ගනී:
reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
move_forward(arr);
code();
}
එනම්, ලූප් බොඩි ධාවනය වීමට පෙර අරාව දර්ශකය ඉදිරියට ගෙනයනු ලැබේ. මෙයින් අදහස් කරන්නේ ලූප් බොඩි මූලද්රව්යය මත ක්රියා කරන $iඅතර IAP දැනටමත් මූලද්රව්යයේ පවතින $i+1බවයි. පුනරාවර්තනයේදී වෙනස් කිරීම් පෙන්වන කේත සාම්පල සෑම විටම unsetවත්මන් මූලද්රව්යයට වඩා ඊළඟ මූලද්රව්යය වීමට හේතුව මෙයයි .
උදාහරණ: ඔබේ පරීක්ෂණ අවස්ථා
ඉහත විස්තර කර ඇති අංශ තුන මඟින් foreachක්රියාවට නැංවීමේ මුග්ධත්වය පිළිබඳ පූර්ණ හැඟීමක් ඔබට ලබා දිය යුතු අතර අපට උදාහරණ කිහිපයක් සාකච්ඡා කිරීමට ඉදිරියට යා හැකිය.
මෙම අවස්ථාවෙහිදී ඔබේ පරීක්ෂණ අවස්ථා වල හැසිරීම පැහැදිලි කිරීම සරල ය:
පරීක්ෂණ අවස්ථා වලදී 1 සහ 2 $arrayආරම්භ වන්නේ refcount = 1 සමඟිනි, එබැවින් එය අනුපිටපත් නොකරනු ඇත foreach: වැඩි කරන්නේ පමණි refcount. ලූප් බොඩි පසුව අරාව වෙනස් කරන විට (එම අවස්ථාවේදී refcount = 2 ඇත), අනුපිටපත එම අවස්ථාවේදී සිදුවනු ඇත. Foreach හි නවීකරණය නොකළ පිටපතක් මත දිගටම වැඩ කරනු ඇත $array.
පරීක්ෂණ අවස්ථාව 3 හි, නැවත වරක් අරාව අනුපිටපත් නොකෙරේ, එමඟින් විචල්යයේ foreachIAP වෙනස් කරනු ඇත $array. පුනරාවර්තනය අවසානයේ, IAP යනු NULL වේ (එහි අර්ථය පුනරාවර්තනය සිදු කර ඇත), එය eachනැවත පැමිණීමෙන් පෙන්නුම් කරයි false.
5 දෙකම ටෙස්ට් අවස්ථාවල දී 4 සහ eachසහ resetඅතුරු යොමු කාර්යයන් වේ. එය ඔවුන්ට $arrayලබා දෙන refcount=2විට එය ඇත, එබැවින් එය අනුපිටපත් කළ යුතුය. එනිසා foreachනැවත වෙනම අරාවකට වැඩ කරනු ඇත.
උදාහරණ: currentforeach හි බලපෑම්
විවිධ අනුපිටපත් හැසිරීම් පෙන්වීමට හොඳ ක්රමයක් current()නම් foreachලූපයක් තුළ ශ්රිතයේ හැසිරීම නිරීක්ෂණය කිරීමයි . මෙම උදාහරණය සලකා බලන්න:
foreach ($array as $val) {
var_dump(current($array));
}
/* Output: 2 2 2 2 2 */
current()අරාව වෙනස් නොකළද එය අතුරු ref ශ්රිතයක් බව ඔබ සැබවින්ම දැනගත යුතුය (ඇත්ත වශයෙන්ම: කැමති-යොමු කරන්න). එය අනෙක් සියලුම කාර්යයන් සමඟ හොඳින් ක්රීඩා කිරීම සඳහා විය යුතුය next. විසින්-යොමු සම්මත අරාව මෙසේ වෙන් කිරීමට ඇති අතර බව එයින් ගම්ය $arrayහා foreach-arrayවෙනස් වේ. ඒ 2වෙනුවට ඔබට ලැබීමට හේතුවද 1ඉහත සඳහන් කර ඇත: පරිශීලක කේතය ධාවනය කිරීමට පෙරforeach අරාව දර්ශකය ඉදිරියට ගෙන යයි, පසුව නොවේ. කේතය පළමු මූලද්රව්යයේ වුවද, foreachදැනටමත් දර්ශකය දෙවන ස්ථානයට ගෙන ඇත.
දැන් කුඩා වෙනස් කිරීමක් කිරීමට උත්සාහ කරමු:
$ref = &$array;
foreach ($array as $val) {
var_dump(current($array));
}
/* Output: 2 3 4 5 false */
මෙන්න අපට is_ref = 1 නඩුව ඇත, එබැවින් අරාව පිටපත් නොකෙරේ (ඉහත ආකාරයටම). නමුත් දැන් එය යොමු කිරීමක් බැවින්, අතුරු ref current()ශ්රිතය වෙත යන විට අරාව තවදුරටත් අනුපිටපත් කළ යුතු නොවේ . මේ අනුව current()සහ foreachඑම අරාවක් මත වැඩ. foreachදර්ශකය ඉදිරියට යන ආකාරය නිසා ඔබ තවමත් එක් එක් හැසිරීම දැක ඇත .
අතුරු-පුනරාවර්තනයක් සිදු කිරීමේදී ඔබට එකම හැසිරීමක් ලැබේ:
foreach ($array as &$val) {
var_dump(current($array));
}
/* Output: 2 3 4 5 false */
මෙහිදී වැදගත් කොටස නම්, foreach විසින් $arrayඑය යොමු කිරීම මඟින් පුනරාවර්තනය වන විට is_ref = 1 බවට පත් කරනු ඇත, එබැවින් මූලික වශයෙන් ඔබට ඉහත තත්වය සමාන වේ.
තවත් කුඩා විචලනයක්, මෙවර අපි අරාව වෙනත් විචල්යයකට පවරමු:
$foo = $array;
foreach ($array as $val) {
var_dump(current($array));
}
/* Output: 1 1 1 1 1 */
මෙන්න $arrayපුඩුවක් ආරම්භ කරන විට 2 හි පරාවර්තනය වේ, එබැවින් එක් වරක් අපට ඇත්ත වශයෙන්ම අනුපිටපත පෙරට ගෙන යා යුතුය. මේ අනුව $array, foreach විසින් භාවිතා කරන අරාව මුල සිටම සම්පූර්ණයෙන්ම වෙන් කරනු ඇත. ලූපයට පෙර කොතැනක සිටියත් ඔබට IAP හි පිහිටීම ලැබෙන්නේ එබැවිනි (මේ අවස්ථාවේ දී එය පළමු ස්ථානයේ විය).
උදාහරණ: පුනරාවර්තනය අතරතුර වෙනස් කිරීම
පුනරාවර්තනයේදී සිදුවන වෙනස් කිරීම් සඳහා ගණනය කිරීමට උත්සාහ කිරීම යනු අපගේ සියලු පුරෝකථන කරදර ඇති වූ ස්ථානයයි, එබැවින් මෙම නඩුව සඳහා උදාහරණ කිහිපයක් සලකා බැලීමට එය සේවය කරයි.
එකම අරාව හරහා මෙම කැදැලි වළළු සලකා බලන්න (එහිදී එය එකම එකක් බව තහවුරු කර ගැනීම සඳහා අතුරු-පුනරාවර්තනය භාවිතා කරයි):
foreach ($array as &$v1) {
foreach ($array as &$v2) {
if ($v1 == 1 && $v2 == 1) {
unset($array[1]);
}
echo "($v1, $v2)\n";
}
}
// Output: (1, 1) (1, 3) (1, 4) (1, 5)
මෙහි අපේක්ෂිත කොටස වන්නේ (1, 2)මූලද්රව්යය 1ඉවත් කළ නිසා ප්රතිදානයෙන් අස්ථානගත වීමයි . බොහෝ විට බලාපොරොත්තු නොවූ දෙය නම් පිටත ලූපය පළමු මූලද්රව්යයෙන් පසුව නතර වීමයි. ඇයි ඒ?
මෙයට හේතුව ඉහත විස්තර කර ඇති කැදැලි-ලූප් හැක් ය: ලූප් බොඩි ධාවනය වීමට පෙර, වර්තමාන IAP පිහිටීම සහ හැෂ් a ට උපස්ථ HashPointerවේ. ලූප් බොඩි පසු එය ප්රතිෂ් ored ාපනය වේ, නමුත් මූලද්රව්යය තවමත් පවතී නම් පමණි, එසේ නොමැතිනම් වත්මන් IAP පිහිටීම (එය කුමක් වුවත්) ඒ වෙනුවට භාවිතා වේ. ඉහත උදාහරණයේ හරියටම මෙයයි: පිටත පුඩුවේ වත්මන් මූලද්රව්යය ඉවත් කර ඇත, එබැවින් එය IAP භාවිතා කරනු ඇත, එය දැනටමත් අභ්යන්තර ලූපය විසින් අවසන් කර ඇති බව සලකුණු කර ඇත!
HashPointerඋපස්ථ + ප්රතිෂ් restore ාපන යාන්ත්රණයේ තවත් ප්රති consequ ලයක් වනුයේ IAP reset()ආදිය හරහා සිදුවන වෙනස්කම් සාමාන්යයෙන් බලපාන්නේ නැත foreach. උදාහරණයක් ලෙස, පහත කේතය reset()කිසිසේත්ම නොතිබූ ආකාරයට ක්රියාත්මක වේ :
$array = [1, 2, 3, 4, 5];
foreach ($array as &$value) {
var_dump($value);
reset($array);
}
// output: 1, 2, 3, 4, 5
හේතුව, reset()තාවකාලිකව IAP වෙනස් කරන අතරම , එය ලූප් ශරීරයෙන් පසු වත්මන් foreach මූලද්රව්යයට ප්රතිෂ් ored ාපනය වේ. ලූපයට බලපෑමක් reset()ඇති කිරීමට බල කිරීම සඳහා, ඔබට අතිරේකව වත්මන් මූලද්රව්යය ඉවත් කළ යුතුය, එවිට උපස්ථ / ප්රතිස්ථාපන යාන්ත්රණය අසමත් වේ:
$array = [1, 2, 3, 4, 5];
$ref =& $array;
foreach ($array as $value) {
var_dump($value);
unset($array[1]);
reset($array);
}
// output: 1, 1, 3, 4, 5
එහෙත්, එම උදාහරණ තවමත් බුද්ධිමත් ය. සැබෑ විනෝදය ආරම්භ වන්නේ ප්රතිෂ් HashPointerrestore ාපනය මූලද්රව්යයට දර්ශකයක් සහ එහි හැෂ් භාවිතා කරන බව ඔබට මතක නම් එය තවමත් පවතීද යන්න තීරණය කරයි. නමුත්: හෑෂ් isions ට්ටන ඇති අතර, දර්ශක නැවත භාවිතා කළ හැකිය! මෙයින් අදහස් කරන්නේ, අරාව යතුරු පරිස්සමින් තෝරා ගැනීමෙන්, foreachඉවත් කරන ලද මූලද්රව්යයක් තවමත් පවතින බව අපට විශ්වාස කළ හැකි බැවින් එය කෙලින්ම එයට පනින බවයි. උදාහරණයක්:
$array = ['EzEz' => 1, 'EzFY' => 2, 'FYEz' => 3];
$ref =& $array;
foreach ($array as $value) {
unset($array['EzFY']);
$array['FYFY'] = 4;
reset($array);
var_dump($value);
}
// output: 1, 4
මෙහිදී අපි සාමාන්යයෙන් 1, 1, 3, 4කලින් නියමයන්ට අනුව ප්රතිදානය අපේක්ෂා කළ යුතුය . සිදුවන්නේ කෙසේද යන්න 'FYFY'ඉවත් කරන ලද මූලද්රව්යයට සමාන හැෂ් එකක් ඇති අතර, මූලද්රව්යය 'EzFY'ගබඩා කිරීම සඳහා එකම මතක ස්ථානය නැවත භාවිතා කිරීමට විබෙදන්නා සිදු වේ. එබැවින් foreach අවසන් වන්නේ අළුතින් ඇතුළු කරන ලද මූලද්රව්යයට කෙලින්ම පැනීමෙනි.
පුඩුවක් අතරතුර නැවත සැකසූ වස්තුව ආදේශ කිරීම
මා සඳහන් කිරීමට කැමති අවසාන අමුතුම අවස්ථාව නම්, ලූපය තුළදී නැවත සැකසූ වස්තුව ආදේශ කිරීමට PHP ඔබට ඉඩ සලසයි. එබැවින් ඔබට එක් අරාවකින් නැවත යෙදීම ආරම්භ කළ හැකි අතර පසුව එය වෙනත් අරාවකින් අඩක් ආදේශ කරන්න. නැතහොත් අරාව මත නැවත යෙදීම ආරම්භ කර එය වස්තුවකින් ආදේශ කරන්න:
$arr = [1, 2, 3, 4, 5];
$obj = (object) [6, 7, 8, 9, 10];
$ref =& $arr;
foreach ($ref as $val) {
echo "$val\n";
if ($val == 3) {
$ref = $obj;
}
}
/* Output: 1 2 3 6 7 8 9 10 */
මෙම අවස්ථාවේ දී ඔබට දැකිය හැකි පරිදි, ආදේශනය සිදු වූ වහාම PHP අනෙක් ආයතනය ආරම්භයේ සිටම නැවත ආරම්භ කිරීමට පටන් ගනී.
PHP 7
කඩිනම් අනුකාරක
ඔබට තවමත් මතක නම්, අරාව පුනරාවර්තනයේ ඇති ප්රධාන ගැටළුව වූයේ මූලද්රව්ය ඉවත් කිරීම හැසිරවිය යුතු ආකාරයයි. පීඑච්පී 5 මේ සඳහා තනි අභ්යන්තර අරාව දර්ශකයක් (අයිඒපී) භාවිතා කළ අතර එය තරමක් උපපෝෂිත විය, මන්දයත් එක් වර්ග දර්ශකයක් දිගු කළ යුතු වූ අතර එකවර එකවර පුරෝකථනය කළ හැකි ලූප සහ ඊටreset() ඉහළින් අන්තර්ක්රියා කිරීමට ය.
PHP 7 වෙනස් ප්රවේශයක් භාවිතා කරයි, එනම්, එය අත්තනෝමතික ලෙස බාහිර, ආරක්ෂිත හැෂ් ටේබල් ඉරේටරයක් නිර්මාණය කිරීමට සහාය වේ. මෙම අනුකාරක අරාවෙහි ලියාපදිංචි කළ යුතු අතර, එතැන් සිට ඒවාට IAP හා සමාන අර්ථකථන ඇත: අරාව මූලද්රව්යයක් ඉවත් කළ හොත්, එම මූලද්රව්යය වෙත යොමු කරන සියලුම හැෂ් ටේබල් අනුකාරක ඊළඟ මූලද්රව්යය දක්වා ඉදිරියට යනු ඇත.
මේ මාර්ගයෙන් foreachතවදුරටත් IAP භාවිතා කරනු ඇත සියලු දී . මෙම foreachලූපය පරම ප්රතිඵල මත කිසිදු බලපෑමක් කරනු ඇත current()සහ ආදිය එහි ම හැසිරීම වැනි කාර්යයන් බලපෑමට නොවන්නේ ය reset()ආදිය
අරාව අනුපිටපත් කිරීම
PHP 5 සහ PHP 7 අතර තවත් වැදගත් වෙනසක් අරාව අනුපිටපත් කිරීම හා සම්බන්ධ වේ. දැන් IAP තවදුරටත් භාවිතා නොකෙරේ, අතුරු අගය අරාව නැවත කිරීම මඟින් සෑම අවස්ථාවකම refcountවර්ධකයක් (අරාව අනුපිටපත් කිරීම වෙනුවට) සිදු කරයි. foreachලූපය තුළ අරාව වෙනස් කර ඇත්නම් , එම අවස්ථාවේදී අනුපිටපතක් සිදුවනු ඇත (පිටපත් මත ලිවීමට අනුව) සහ foreachපැරණි අරාව මත දිගටම ක්රියා කරයි.
බොහෝ අවස්ථාවන්හීදී, මෙම වෙනස විනිවිද පෙනෙන අතර වඩා හොඳ කාර්ය සාධනයක් හැර වෙනත් බලපෑමක් නැත. කෙසේ වෙතත්, එය වෙනස් හැසිරීම් වල ප්රති results ලයක් වන එක් අවස්ථාවක් තිබේ, එනම් අරාව කලින් යොමු කිරීමක් වූ අවස්ථාව:
$array = [1, 2, 3, 4, 5];
$ref = &$array;
foreach ($array as $val) {
var_dump($val);
$array[2] = 0;
}
/* Old output: 1, 2, 0, 4, 5 */
/* New output: 1, 2, 3, 4, 5 */
මීට පෙර යොමු-අරා වල අගය අනුව නැවත යෙදීම විශේෂ අවස්ථා විය. මෙම අවස්ථාවෙහිදී, අනුපිටපතක් සිදු නොවූ අතර, පුනරාවර්තනයේදී අරාවෙහි සියලු වෙනස් කිරීම් ලූපය මගින් පිළිබිඹු වේ. PHP 7 හි මෙම විශේෂ අවස්ථාව අහෝසි වී ඇත: අරාවෙහි අතුරු-අගය නැවත සැකසීම සෑම විටම මුල් මූලද්රව්යයන් මත ක්රියා කරමින්, ලූපය තුළ සිදුවන වෙනස් කිරීම් නොසලකා හරිනු ඇත.
ඇත්ත වශයෙන්ම මෙය අතුරු විමර්ශන ක්රියාවලියට අදාළ නොවේ. ඔබ අතුරු-යොමු යොමු කරන්නේ නම්, සියලු වෙනස් කිරීම් ලූපය මගින් පිළිබිඹු වේ. සරල වස්තූන්ගේ අතුරු-අගය පුනරාවර්තනය සඳහා ද මෙය අදාළ වේ:
$obj = new stdClass;
$obj->foo = 1;
$obj->bar = 2;
foreach ($obj as $val) {
var_dump($val);
$obj->bar = 42;
}
/* Old and new output: 1, 42 */
මෙය වස්තූන්ගේ අතුරු හැසිරවීමේ අර්ථ නිරූපණය පිළිබිඹු කරයි (එනම් ඒවා අතුරු වටිනාකම් සන්දර්භය තුළ පවා යොමු දැක්වීම් මෙන් හැසිරේ).
උදාහරණ
ඔබගේ පරීක්ෂණ අවස්ථා වලින් ආරම්භ වන උදාහරණ කිහිපයක් සලකා බලමු:
පරීක්ෂණ අවස්ථා 1 සහ 2 එකම ප්රතිදානය රඳවා ගනී: අතුරු-අගය අරාව පුනරාවර්තනය සෑම විටම මුල් මූලද්රව්ය මත ක්රියා කරයි. (මෙම අවස්ථාවේ දී, ඒකාකාර refcountingහා අනුපිටපත් හැසිරීම PHP 5 සහ PHP 7 අතර හරියටම සමාන වේ).
පරීක්ෂණ අවස්ථාව 3 වෙනස් වේ: Foreachතවදුරටත් IAP භාවිතා නොකරයි, එබැවින් ලූපයට එය each()බලපාන්නේ නැත. එයට පෙර සහ පසු එකම ප්රතිදානය ඇත.
පරීක්ෂණ අවස්ථා 4 සහ 5 එකම මට්ටමක පවතී : each()සහ reset()IAP වෙනස් කිරීමට පෙර අරාව අනුපිටපත් කරනු ඇත, foreachතවමත් මුල් අරාව භාවිතා කරයි. (අරාව බෙදාගෙන තිබුණත් IAP වෙනස වැදගත් වනු ඇත.)
දෙවන උදාහරණ මාලාව current()විවිධ reference/refcountingවින්යාසයන් යටතේ හැසිරීම හා සම්බන්ධ විය . මෙය තවදුරටත් අර්ථවත් නොවේ, current()ලූපයට සම්පූර්ණයෙන්ම බලපාන්නේ නැති නිසා එහි ප්රතිලාභ අගය සැමවිටම එලෙසම පවතී.
කෙසේ වෙතත්, පුනරාවර්තනයේදී සිදුවන වෙනස් කිරීම් සලකා බැලීමේදී අපට සිත්ගන්නා සුළු වෙනස්කම් කිහිපයක් ලැබේ. නව හැසිරීම් සනීපාරක්ෂාව ඔබ සොයා ගනු ඇතැයි මම බලාපොරොත්තු වෙමි. පළමු උදාහරණය:
$array = [1, 2, 3, 4, 5];
foreach ($array as &$v1) {
foreach ($array as &$v2) {
if ($v1 == 1 && $v2 == 1) {
unset($array[1]);
}
echo "($v1, $v2)\n";
}
}
// Old output: (1, 1) (1, 3) (1, 4) (1, 5)
// New output: (1, 1) (1, 3) (1, 4) (1, 5)
// (3, 1) (3, 3) (3, 4) (3, 5)
// (4, 1) (4, 3) (4, 4) (4, 5)
// (5, 1) (5, 3) (5, 4) (5, 5)
ඔබට පෙනෙන පරිදි, පිටත පුඩුවක් පළමු පුනරාවර්තනයෙන් පසුව තවදුරටත් නතර නොවේ. හේතුව, ලූප දෙකම දැන් මුළුමනින්ම වෙනම හැෂ් ටේබල් ඉරේටරයන් ඇති අතර, හවුල් අයි.ඒ.පී හරහා ලූප දෙකේම හරස් දූෂණයක් තවදුරටත් නොමැත.
දැන් සවි කර ඇති තවත් අමුතු දාරයක්, එකම හැෂ් ඇති මූලද්රව්ය ඉවත් කර එකතු කළ විට ඔබට ලැබෙන අමුතු බලපෑමයි:
$array = ['EzEz' => 1, 'EzFY' => 2, 'FYEz' => 3];
foreach ($array as &$value) {
unset($array['EzFY']);
$array['FYFY'] = 4;
var_dump($value);
}
// Old output: 1, 4
// New output: 1, 3, 4
මීට පෙර හැෂ්පොයින්ටර් ප්රතිස්ථාපන යාන්ත්රණය නව මූලද්රව්යයට දකුණට පැන්නේ එය ඉවත් කළ මූලද්රව්යයට සමාන බව පෙනෙන නිසා (හැෂ් හා පොයින්ටරය ගැටීමෙන්). අපි තවදුරටත් කිසිවක් සඳහා මූලද්රව්ය හැෂ් මත රඳා නොසිටින බැවින් මෙය තවදුරටත් ගැටළුවක් නොවේ.