পিএইচপি মধ্যে ফলন মানে কি?


232

আমি সম্প্রতি এই কোডটিতে হোঁচট খেয়েছি:

function xrange($min, $max) 
{
    for ($i = $min; $i <= $max; $i++) {
        yield $i;
    }
}

আমি এই yieldকীওয়ার্ডটি আগে কখনও দেখিনি । আমি যে কোডটি পেয়েছি তা চালানোর চেষ্টা করছি

বিশ্লেষণ ত্রুটি: সিনট্যাক্স ত্রুটি, এক্স লাইনে অপ্রত্যাশিত T_VARIABLE

তাহলে এই yieldকীওয়ার্ডটি কী? এটি কি বৈধ পিএইচপি? এবং যদি এটি হয় তবে আমি কীভাবে এটি ব্যবহার করব?

উত্তর:


355

কী yield?

yieldশব্দ একটি উত্পাদক ফাংশন থেকে আয় ডেটা:

একটি জেনারেটরের ফাংশনের হৃদয় হ'ল ফলন কীওয়ার্ড। এর সরল আকারে, একটি ফলন বিবরণীটি অনেকটা রিটার্ন স্টেটমেন্টের মতো দেখায়, ব্যতীত ফাংশনটি কার্যকর করা বন্ধ করে দেওয়া এবং প্রত্যাবর্তনের পরিবর্তে ফলন জেনারেটরের উপরের কোড লুপিংয়ের একটি মান সরবরাহ করে এবং জেনারেটরের ফাংশনটির কার্যকরকরণকে বিরতি দেয়।

জেনারেটরের কাজ কী?

একটি জেনারেটর ফাংশন কার্যকরভাবে একটি Iterator লেখার আরও কমপ্যাক্ট এবং দক্ষ উপায় । এটি আপনাকে এমন একটি ফাংশন (আপনার xrange) সংজ্ঞায়িত করতে দেয় যা গণনা করে এবং আপনি যখন লুপিংয়ের সময় মানগুলি ফিরিয়ে দেয় :

foreach (xrange(1, 10) as $key => $value) {
    echo "$key => $value", PHP_EOL;
}

এটি নিম্নলিখিত আউটপুট তৈরি করবে:

0 => 1
1 => 2

9 => 10

আপনি $keyএটি foreachব্যবহার করে নিয়ন্ত্রণও করতে পারেন

yield $someKey => $someValue;

উত্পাদক ফাংশন ইন, $someKeyযাই হোক না কেন আপনার জন্য প্রদর্শিত চাই $keyএবং $someValueমান হচ্ছে $val। প্রশ্নের উদাহরণে এটি $i

সাধারণ ফাংশনগুলির মধ্যে পার্থক্য কী?

এখন আপনি ভাবতে পারেন যে আমরা কেন এই আউটপুটটি অর্জনের জন্য পিএইচপি এর নেটিভ rangeফাংশনটি ব্যবহার করছি না । এবং ঠিক আপনি আছেন। আউটপুট একই হবে। পার্থক্যটি হল আমরা সেখানে কীভাবে পেলাম।

যখন আমরা ব্যবহার rangeপিএইচপি, এটা চালানো হবে, মেমরি সংখ্যার সম্পূর্ণ অ্যারে তৈরি এবং returnযে সমগ্র অ্যারের করতে foreachলুপ যা পরে এটা এবং আউটপুট মান ওভার যেতে হবে। অন্য কথায়, foreachঅ্যারে নিজেই চালিত হবে। rangeফাংশন এবং foreachশুধুমাত্র লাগে "Talk" একবার। এটিকে মেইলে প্যাকেজ পাওয়ার মতো ভাবেন। ডেলিভারি লোকটি আপনাকে প্যাকেজ দেয় এবং চলে যাবে। এবং তারপরে আপনি যা আছে তা বাইরে বের করে পুরো প্যাকেজটি মোড়ক করুন।

যখন আমরা জেনারেটর ফাংশনটি ব্যবহার করি, পিএইচপি ফাংশনে প্রবেশ করবে এবং এটি শেষ না হওয়া বা কোনও yieldকীওয়ার্ড না পাওয়া পর্যন্ত এটিকে সম্পাদন করবে । এটি যখন একটিটির সাথে মিলিত হয় yield, তারপরে এটি সেই সময়ের মানটি যা বাইরের লুপে ফিরে আসবে। তারপরে এটি আবার জেনারেটরের ফাংশনে ফিরে যায় এবং যেখানে ফলন হয়েছিল সেখান থেকে চালিয়ে যায়। যেহেতু আপনার xrangeএকটি forলুপ ধরে আছে তাই এটি কার্যকর হবে এবং $maxপৌঁছানো পর্যন্ত ফলন হবে । এটি foreachএবং জেনারেটরের পিং পংয়ের মতো চিন্তা করুন।

আমার কেন এটি দরকার?

স্পষ্টতই, জেনারেটরগুলি মেমরি সীমাতে কাজ করতে ব্যবহৃত হতে পারে। আপনার পরিবেশের উপর নির্ভর করে, range(1, 1000000)উইল করা আপনার স্ক্রিপ্টকে মারাত্মক করবে যেখানে জেনারেটরের সাহায্যে এটি ঠিক কাজ করবে। অথবা উইকিপিডিয়ায় যেমন লেখা আছে:

যেহেতু জেনারেটরগুলি কেবলমাত্র তাদের উত্পাদিত মানগুলি কেবল চাহিদার ভিত্তিতে গণনা করে, তারা ক্রমগুলি উপস্থাপনের জন্য দরকারী যা একবারে গণনা করা ব্যয়বহুল বা অসম্ভব। এর মধ্যে উদাহরণস্বরূপ অসীম অনুক্রম এবং লাইভ ডেটা স্ট্রিম রয়েছে।

জেনারেটরগুলিও বেশ দ্রুত হওয়ার কথা। তবে মনে রাখবেন যে আমরা যখন দ্রুত সম্পর্কে কথা বলি তখন আমরা সাধারণত খুব অল্প সংখ্যায় কথা বলি। সুতরাং এখন আপনি চালাবার আগে এবং জেনারেটরগুলি ব্যবহার করার জন্য আপনার সমস্ত কোড পরিবর্তন করার আগে এটি কোন অর্থবোধ করে তা দেখার জন্য একটি মানদণ্ড করুন।

জেনারেটরের জন্য অন্য ব্যবহারের কেস হ'ল অ্যাসিনক্রোনাস কর্টাইন ines yieldমূলশব্দটি কেবল মানগুলি দেয় না তবে এটি সেগুলি গ্রহণ করে। এ সম্পর্কে তথ্যের জন্য, নীচে লিঙ্কযুক্ত দুটি দুর্দান্ত ব্লগ পোস্ট দেখুন।

আমি কখন থেকে ব্যবহার করতে পারি yield?

জেনারেটর পিএইচপি 5.5 এ চালু করা হয়েছে । yieldসংস্করণটির আগে ব্যবহারের চেষ্টা করার ফলে কীওয়ার্ডটি অনুসরণ করা কোডের উপর নির্ভর করে বিভিন্ন পার্স ত্রুটি হবে। সুতরাং যদি আপনি এই কোডটি থেকে কোনও পার্স ত্রুটি পান তবে আপনার পিএইচপি আপডেট করুন।

সূত্র এবং আরও পড়া:


1
এর সুবিধাগুলি কীভাবে শেষ হয়েছে তা দয়া করে ব্যাখ্যা করুন, yeildএর মতো একটি সমাধান করুন: আদর্শ one.com/xgqevM
মাইক

1
আহ, ভাল, এবং আমি যে বিজ্ঞপ্তিগুলি উত্পন্ন করছিলাম হাহ। হ্যাঁ, আমি একজন সহায়ক-শ্রেণীর সাথে পিএইচপি> = 5.0.0 এর জন্য জেনারেটরগুলি অনুকরণে পরীক্ষা করেছি এবং হ্যাঁ, সামান্য কম পাঠযোগ্য, তবে ভবিষ্যতে আমি এটি ব্যবহার করতে পারি। আকর্ষণীয় বিষয়। ধন্যবাদ!
মাইক

পঠনযোগ্যতা নয় স্মৃতি ব্যবহার! পুনরাবৃত্তির জন্য ব্যবহৃত স্মৃতিটির তুলনা করুন return range(1,100000000)এবং for ($i=0; $i<100000000; $i++) yield $i
এমিক্স

@ মাই হ্যাঁ, এটি আমার উত্তরটিতে ইতিমধ্যে ব্যাখ্যা করা হয়েছে। অন্য মাইকের উদাহরণে মেমরিটি খুব কমই একটি সমস্যা কারণ তিনি কেবল 10 মানকেই পুনরাবৃত্তি করছেন।
গর্ডন

1
@ মাইকটি এক্সরেঞ্জের সাথে একটি সমস্যা হ'ল উদাহরণস্বরূপ এর স্ট্যাটিকস সীমা ব্যবহার বাসা বাঁধার জন্য দরকারীতা (উদাহরণস্বরূপ একটি এন ডাইমেনশনাল বহুগুণ অনুসন্ধান করা বা উদাহরণস্বরূপ জেনারেটর ব্যবহার করে পুনরাবৃত্ত কুইকোর্ট)। আপনি এক্সরেঞ্জ লুপগুলিকে বাসাতে পারবেন না কারণ এর কাউন্টারটির কেবলমাত্র একক উদাহরণ রয়েছে। ফলন সংস্করণ এই সমস্যায় পড়ে না।
শায়নে

43

এই ফাংশনটি ফলন ব্যবহার করছে:

function a($items) {
    foreach ($items as $item) {
        yield $item + 1;
    }
}

এটি ছাড়া এটির মতো প্রায়:

function b($items) {
    $result = [];
    foreach ($items as $item) {
        $result[] = $item + 1;
    }
    return $result;
}

কেবলমাত্র একটি পার্থক্য হ'ল a()একটি জেনারেটর এবং b()কেবল একটি সাধারণ অ্যারে প্রদান করে। আপনি উভয় উপর পুনরাবৃত্তি করতে পারেন।

এছাড়াও, প্রথমটি একটি সম্পূর্ণ অ্যারে বরাদ্দ করে না এবং তাই মেমরি-চাহিদা কম।


2
অফিসিয়াল ডক্স থেকে নোট যুক্ত করুন: পিএইচপি 5-তে, একটি জেনারেটর কোনও মান ফিরিয়ে দিতে পারেনি: এটি করার ফলে একটি সংকলন ত্রুটি হবে। একটি খালি রিটার্ন স্টেটমেন্টটি একটি জেনারেটরের মধ্যে বৈধ সিনট্যাক্স ছিল এবং এটি জেনারেটরটি সমাপ্ত করবে। পিএইচপি .0.০ থেকে, একটি জেনারেটর মান ফিরিয়ে দিতে পারে, যা জেনারেটর :: getReturn () ব্যবহার করে পুনরুদ্ধার করা যেতে পারে। php.net/manual/en/language.generators.syntax.php
প্রোগ্রামার ড্যানকুক

সহজ এবং সংক্ষিপ্ত।
জন মিলার

24

সহজ উদাহরণ

<?php
echo '#start main# ';
function a(){
    echo '{start[';
    for($i=1; $i<=9; $i++)
        yield $i;
    echo ']end} ';
}
foreach(a() as $v)
    echo $v.',';
echo '#end main#';
?>

আউটপুট

#start main# {start[1,2,3,4,5,6,7,8,9,]end} #end main#

উন্নত উদাহরণ

<?php
echo '#start main# ';
function a(){
    echo '{start[';
    for($i=1; $i<=9; $i++)
        yield $i;
    echo ']end} ';
}
foreach(a() as $k => $v){
    if($k === 5)
        break;
    echo $k.'=>'.$v.',';
}
echo '#end main#';
?>

আউটপুট

#start main# {start[0=>1,1=>2,2=>3,3=>4,4=>5,#end main#

সুতরাং, এটি ফাংশন বাধা ছাড়াই ফিরে?
লুকাস বুস্তামন্তে

22

yieldকীওয়ার্ড পিএইচপি 5.5-এ "জেনারেটর" এর সংজ্ঞা দেয়। ঠিক আছে, তাহলে কি জেনারেটর কী?

পিএইচপি.net থেকে:

জেনারেটরগুলি আইট্রেটার ইন্টারফেস প্রয়োগ করে এমন কোনও ক্লাস বাস্তবায়নের ওভারহেড বা জটিলতা ছাড়াই সহজ পুনরাবৃত্তি কার্যকর করার সহজ উপায় সরবরাহ করে।

কোনও জেনারেটর আপনাকে মেমরিতে অ্যারে তৈরি করার প্রয়োজন ছাড়াই ডেটা সংকলনের উপর পুনরাবৃত্তি করতে ফোরচ ব্যবহার করে এমন কোড লিখতে দেয় যা আপনাকে মেমরির সীমা ছাড়িয়ে যেতে পারে, বা উত্পাদন করার জন্য যথেষ্ট পরিমাণে প্রক্রিয়াজাতকরণের প্রয়োজন হতে পারে। পরিবর্তে, আপনি একটি জেনারেটর ফাংশন লিখতে পারেন, যা সাধারণ ফাংশন হিসাবে একই, একবারে ফিরে আসার পরিবর্তে, একটি জেনারেটর যতবার মান পুনরাবৃত্তি করতে পারে তার সরবরাহের জন্য যত বার প্রয়োজন তার ফলন করতে পারে।

এই জায়গা থেকে: জেনারেটর = জেনারেটর, অন্যান্য ফাংশন (কেবল একটি সাধারণ ফাংশন) = ফাংশন।

সুতরাং, এগুলি কার্যকর যখন:

  • আপনার সাধারণ জিনিস (বা সাধারণ জিনিস) করতে হবে;

    জেনারেটর আসলে Iterator ইন্টারফেস বাস্তবায়ন অনেক সহজ। অন্যদিকে, অফসোর্স, যে জেনারেটরগুলি কম কার্যকরী হয়। তাদের তুলনা করুন

  • আপনার বড় পরিমাণে ডেটা তৈরি করতে হবে - মেমরি সংরক্ষণ করা;

    প্রকৃতপক্ষে স্মৃতি সংরক্ষণ করতে আমরা প্রতিটি লুপ পুনরাবৃত্তির জন্য ফাংশনগুলির মাধ্যমে প্রয়োজনীয় ডেটা তৈরি করতে পারি এবং পুনরাবৃত্তির পরে আবর্জনা ব্যবহার করতে পারি। সুতরাং এখানে মূল পয়েন্টগুলি হল - পরিষ্কার কোড এবং সম্ভবত পারফরম্যান্স। আপনার প্রয়োজনের জন্য ভাল কি দেখুন।

  • আপনাকে ক্রম তৈরি করতে হবে যা মধ্যবর্তী মানগুলির উপর নির্ভর করে;

    এটি পূর্বের চিন্তার প্রসারিত। জেনারেটরগুলি ফাংশনের সাথে তুলনা করে জিনিসগুলিকে সহজ করে তুলতে পারে। ফিবোনাচি উদাহরণ পরীক্ষা করে দেখুন এবং জেনারেটর ছাড়াই ক্রম তৈরির চেষ্টা করুন to জেনারেটরগুলি দ্রুত এই ক্ষেত্রে কাজ করতে পারে, অন্তত স্থানীয় ভেরিয়েবলগুলিতে অন্তর্বর্তী মানগুলি সংরক্ষণ করার কারণে;

  • আপনার কর্মক্ষমতা উন্নত করতে হবে।

    তারা দ্রুত কাজ করতে পারে তবে কিছু ক্ষেত্রে কাজ করে (পূর্ববর্তী সুবিধা দেখুন);


1
জেনারেটর কীভাবে কাজ করে তা আমি বুঝতে পারি নি। এই শ্রেণি পুনরুক্তি ইন্টারফেস প্রয়োগ করে। আমি যা জানি পুনরাবৃত্তি ক্লাসগুলি আমাকে কীভাবে কোনও বস্তুর উপর পুনরাবৃত্তি করতে চাই তা কনফিগার করার অনুমতি দেয়। উদাহরণস্বরূপ অ্যারেআইটিরেটর একটি অ্যারে বা অবজেক্ট পেয়ে যায় তাই আমি এটির পুনরুক্তির সময় মান এবং কীগুলি সংশোধন করতে পারি। সুতরাং যদি পুনরাবৃত্তিগুলি পুরো অবজেক্ট / অ্যারে পায় তবে জেনারেটরটিকে কীভাবে মেমরিতে পুরো অ্যারে তৈরি করতে হবে না ???
ব্যবহারকারী 3021621

7

সঙ্গে yieldআপনি সহজেই একটি একক ফাংশনে একাধিক কর্ম মধ্যে ব্রেকপয়েন্ট বর্ণনা করতে পারেন। এগুলিই, এটি সম্পর্কে বিশেষ কিছু নেই।

$closure = function ($injected1, $injected2, ...){
    $returned = array();
    //task1 on $injected1
    $returned[] = $returned1;
//I need a breakpoint here!!!!!!!!!!!!!!!!!!!!!!!!!
    //task2 on $injected2
    $returned[] = $returned2;
    //...
    return $returned;
};
$returned = $closure($injected1, $injected2, ...);

যদি টাস্ক 1 এবং টাস্ক 2 অত্যন্ত সম্পর্কিত হয় তবে অন্য কিছু করার জন্য আপনার মধ্যে একটি ব্রেকপয়েন্ট দরকার:

  • প্রক্রিয়াকরণ ডাটাবেস সারি মধ্যে বিনামূল্যে মেমরি
  • পরবর্তী কাজগুলিতে নির্ভরতা সরবরাহ করে এমন অন্যান্য কার্যগুলি চালান, তবে যা বর্তমান কোডটি বোঝার সাথে সম্পর্কিত নয়
  • অ্যাসিঙ্ক কল করা এবং ফলাফলগুলির জন্য অপেক্ষা করুন
  • এবং তাই ...

তারপরে জেনারেটর হ'ল সর্বোত্তম সমাধান, কারণ আপনার কোডটি অনেকগুলি ক্লোজারে বিভক্ত করতে বা অন্যান্য কোডের সাথে মিশ্রিত করতে বা কলব্যাকস ইত্যাদি ব্যবহার করতে হবে না ... আপনি কেবল ব্যবহার করেন yield করবেন না ব্রেক ব্রেকপয়েন্ট যুক্ত করতে করেন এবং আপনি এ থেকে চালিয়ে যেতে পারেন ব্রেকপয়েন্ট আপনি প্রস্তুত থাকলে।

জেনারেটর ছাড়াই ব্রেকপয়েন্ট যুক্ত করুন:

$closure1 = function ($injected1){
    //task1 on $injected1
    return $returned1;
};
$closure2 = function ($injected2){
    //task2 on $injected2
    return $returned1;
};
//...
$returned1 = $closure1($injected1);
//breakpoint between task1 and task2
$returned2 = $closure2($injected2);
//...

জেনারেটরগুলির সাথে ব্রেকপয়েন্ট যুক্ত করুন

$closure = function (){
    $injected1 = yield;
    //task1 on $injected1
    $injected2 = (yield($returned1));
    //task2 on $injected2
    $injected3 = (yield($returned2));
    //...
    yield($returnedN);
};
$generator = $closure();
$returned1 = $generator->send($injected1);
//breakpoint between task1 and task2
$returned2 = $generator->send($injected2);
//...
$returnedN = $generator->send($injectedN);

দ্রষ্টব্য: জেনারেটরগুলির সাথে ভুল করা সহজ, সুতরাং কার্যকর করার আগে সর্বদা ইউনিট পরীক্ষা লিখুন! নোট 2: অসীম লুপে জেনারেটর ব্যবহার করা এমন একটি বন্ধন লেখার মতো যা অসীম দৈর্ঘ্য ...


4

উপরের উত্তরগুলির মধ্যে কোনওটিই অ-সংখ্যাযুক্ত সদস্যদের দ্বারা বিস্তৃত বিশাল অ্যারে ব্যবহার করে একটি দৃ concrete় উদাহরণ দেখায় না। এখানে explode()একটি বড় .txt ফাইল (আমার ব্যবহারের ক্ষেত্রে 262MB) দ্বারা উত্পন্ন অ্যারে ব্যবহার করে একটি উদাহরণ দেওয়া হয়েছে:

<?php

ini_set('memory_limit','1000M');

echo "Starting memory usage: " . memory_get_usage() . "<br>";

$path = './file.txt';
$content = file_get_contents($path);

foreach(explode("\n", $content) as $ex) {
    $ex = trim($ex);
}

echo "Final memory usage: " . memory_get_usage();

আউটপুটটি ছিল:

Starting memory usage: 415160
Final memory usage: 270948256

এখন yieldকীওয়ার্ডটি ব্যবহার করে এটি একটি অনুরূপ স্ক্রিপ্টের সাথে তুলনা করুন :

<?php

ini_set('memory_limit','1000M');

echo "Starting memory usage: " . memory_get_usage() . "<br>";

function x() {
    $path = './file.txt';
    $content = file_get_contents($path);
    foreach(explode("\n", $content) as $x) {
        yield $x;
    }
}

foreach(x() as $ex) {
    $ex = trim($ex);
}

echo "Final memory usage: " . memory_get_usage();

এই স্ক্রিপ্টের জন্য আউটপুট ছিল:

Starting memory usage: 415152
Final memory usage: 415616

স্পষ্টতই মেমরির ব্যবহারের সঞ্চয় যথেষ্ট ছিল (emMemoryUsage -----> example ২ example০.৫ এমবি প্রথম উদাহরণে, দ্বিতীয় উদাহরণে 50 450B )।


3

একটি আকর্ষণীয় দিক, যা এখানে আলোচনা করা মূল্যবান, তা রেফারেন্স দ্বারা ফলন করছে । প্রতিবার যখন আমাদের কোনও পরামিতি পরিবর্তন করা দরকার যা এটি ফাংশনের বাইরে প্রতিবিম্বিত হয়, আমাদের এই পরামিতিটি রেফারেন্স দিয়ে পাস করতে হবে। জেনারেটরগুলিতে এটি প্রয়োগ করতে, আমরা কেবল জেনারেটরের &নাম এবং পুনরাবৃত্তির ক্ষেত্রে ব্যবহৃত ভেরিয়েবলের জন্য একটি এম্পারস্যান্ড প্রিন্ট করি :

 <?php 
 /**
 * Yields by reference.
 * @param int $from
 */
function &counter($from) {
    while ($from > 0) {
        yield $from;
    }
}

foreach (counter(100) as &$value) {
    $value--;
    echo $value . '...';
}

// Output: 99...98...97...96...95...

উপরের উদাহরণটি দেখায় যে foreachলুপের মধ্যে পুনরাবৃত্ত মানগুলি কীভাবে $fromজেনারেটরের মধ্যে পরিবর্তনশীল পরিবর্তন করে । এর কারণ হল হয় $fromহয় রেফারেন্স দ্বারা পাওয়া জেনারেটর নাম আগে এম্পারসেন্ড কারণে। যে কারণে লুপের $valueমধ্যে পরিবর্তনশীল foreachহ'ল $fromজেনারেটর ফাংশনের মধ্যে চলকের একটি রেফারেন্স ।


0

নিচের কোডটি বর্ণনা করে যে কীভাবে জেনারেটর ব্যবহার করে সম্পূর্ণ হওয়ার আগে একটি ফলাফল ফিরে আসে, iteতিহ্যবাহী নন জেনারেটরের পদ্ধতির বিপরীতে যা সম্পূর্ণ পুনরাবৃত্তির পরে একটি সম্পূর্ণ অ্যারে প্রদান করে। নীচের জেনারেটরের সাথে, প্রস্তুত হওয়ার পরে মানগুলি ফেরত দেওয়া হয়, অ্যারে সম্পূর্ণরূপে পূর্ণ হওয়ার জন্য অপেক্ষা করার প্রয়োজন নেই:

<?php 

function sleepiterate($length) {
    for ($i=0; $i < $length; $i++) {
        sleep(2);
        yield $i;
    }
}

foreach (sleepiterate(5) as $i) {
    echo $i, PHP_EOL;
}

সুতরাং, পিএইচপি এইচটিএমএল কোড উত্পন্ন করার জন্য ফলন ব্যবহার করা সম্ভব নয়? আমি সত্যিকারের পরিবেশে সুবিধাগুলি জানি না
জিউসেপ লোদি রিজিনি

@ জিউস্পেপলডিজিজ্জনি কি এমন ভাবনা তৈরি করে?
ব্র্যাড কেন্ট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.