Sök…


Varför använda en generator?

Generatorer är användbara när du behöver generera en stor samling för att senare uppdateras. De är ett enklare alternativ till att skapa en klass som implementerar en Iterator , som ofta är överdödig.

Tänk till exempel på funktionen nedan.

function randomNumbers(int $length)
{
    $array = [];
    
    for ($i = 0; $i < $length; $i++) {
        $array[] = mt_rand(1, 10);
    }
    
    return $array;
}

All denna funktion gör är att generera en matris som är fylld med slumpmässiga nummer. För att använda det kan vi göra randomNumbers(10) , vilket ger oss en matris med 10 slumpmässiga nummer. Tänk om vi vill generera en miljon slumpmässiga nummer? randomNumbers(1000000) kommer att göra det för oss, men till en minneskostnad. En miljon heltal lagrade i en grupp använder ungefär 33 megabyte minne.

$startMemory = memory_get_usage();

$randomNumbers = randomNumbers(1000000);

echo memory_get_usage() - $startMemory, ' bytes';

Detta beror på att hela en miljon slumpmässiga nummer genereras och returneras samtidigt, snarare än en åt gången. Generatorer är ett enkelt sätt att lösa problemet.

Omskriva randomNumbers () med hjälp av en generator

Vår randomNumbers() -funktion kan skrivas om för att använda en generator.

<?php

function randomNumbers(int $length)
{
    for ($i = 0; $i < $length; $i++) {
        // yield tells the PHP interpreter that this value
        // should be the one used in the current iteration.
        yield mt_rand(1, 10);
    }
}

foreach (randomNumbers(10) as $number) {
    echo "$number\n";
}

Med en generator behöver vi inte bygga en hel lista med slumpmässiga nummer för att återvända från funktionen, vilket leder till att mycket mindre minne används.

Läser en stor fil med en generator

Ett vanligt fall för generatorer är att läsa en fil från disken och iterera över dess innehåll. Nedan är en klass som låter dig iterera över en CSV-fil. Minnesanvändningen för detta skript är mycket förutsägbart och kommer inte att variera beroende på storleken på CSV-filen.

<?php

class CsvReader
{
    protected $file;
 
    public function __construct($filePath) {
        $this->file = fopen($filePath, 'r');
    }
 
    public function rows()
    {
        while (!feof($this->file)) {
            $row = fgetcsv($this->file, 4096);
            
            yield $row;
        }
        
        return;
    }
}
 
$csv = new CsvReader('/path/to/huge/csv/file.csv');

foreach ($csv->rows() as $row) {
    // Do something with the CSV row.
}

Utbyte nyckelord

Ett yield liknar ett returrätt, förutom att istället för att stoppa exekveringen av funktionen och returnera, returnerar avkastningen istället ett Generator- objekt och pausar exekveringen av generatorfunktionen.

Här är ett exempel på intervallfunktionen, skriven som en generator:

function gen_one_to_three() {
    for ($i = 1; $i <= 3; $i++) {
        // Note that $i is preserved between yields.
        yield $i;
    }
}

Du kan se att den här funktionen returnerar ett Generator- objekt genom att kontrollera utgången från var_dump :

var_dump(gen_one_to_three())

# Outputs:
class Generator (0) {
}

Utbyte värden

Generator- objektet kan sedan itereras över som en matris.

foreach (gen_one_to_three() as $value) {
    echo "$value\n";
}

Exemplet ovan kommer att matas ut:

1
2
3

Att ge värden med nycklar

Förutom att ge värden kan du också ge nyckel- / värdepar.

function gen_one_to_three() {
    $keys = ["first", "second", "third"];

    for ($i = 1; $i <= 3; $i++) {
        // Note that $i is preserved between yields.
        yield $keys[$i - 1] => $i;
    }
}

foreach (gen_one_to_three() as $key => $value) {
    echo "$key: $value\n";
}

Exemplet ovan kommer att matas ut:

first: 1
second: 2
third: 3

Använd funktionen skicka () - för att skicka värden till en generator

Generatorer är snabbkodade och i många fall ett smalt alternativ till tunga iterator-implementationer. Med den snabba implementeringen kommer lite brist på kontroll när en generator ska sluta generera eller om den skulle generera något annat. Detta kan emellertid uppnås genom att använda funktionen send() , vilket gör att den begärande funktionen kan skicka parametrar till generatorn efter varje slinga.

//Imagining accessing a large amount of data from a server, here is the generator for this:
function generateDataFromServerDemo()
{
    $indexCurrentRun = 0; //In this example in place of data from the server, I just send feedback everytime a loop ran through.

    $timeout = false;
    while (!$timeout)
    {
        $timeout = yield $indexCurrentRun; // Values are passed to caller. The next time the generator is called, it will start at this statement. If send() is used, $timeout will take this value.
        $indexCurrentRun++;
    }

    yield 'X of bytes are missing. </br>';
}

// Start using the generator
$generatorDataFromServer = generateDataFromServerDemo ();
foreach($generatorDataFromServer as $numberOfRuns)
{
    if ($numberOfRuns < 10)
    {
        echo $numberOfRuns . "</br>";
    }
    else
    {
        $generatorDataFromServer->send(true); //sending data to the generator
        echo $generatorDataFromServer->current(); //accessing the latest element (hinting how many bytes are still missing.
    }
}

Detta resulterar i denna utgång:

ange bildbeskrivning här



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow