سؤال

في بيثون (وغيرها)، يمكنك معالجة مجلدات كبيرة بشكل تدريجي من البيانات باستخدام مشغل "العائد" في وظيفة. ماذا ستكون الطريقة المماثلة للقيام بذلك في فب؟

على سبيل المثال، دعنا نقول في بيثون، إذا كنت أرغب في قراءة ملف كبير للغاية، فقد أعمل على كل سطر واحد في وقت واحد مثل (هذا المثال، كما هو في الأساس نفس الشيء مثل "خط '):

def file_lines(fname):
    f = open(fname)
    for line in f:
        yield line
    f.close()

for line in file_lines('somefile'):
    #process the line

ما أفعله الآن (في PHP) هو استخدام متغير مثيل خاص لتتبع الحالة، والتصرف وفقا لذلك في كل مرة يتم استدعاء الوظيفة، ولكن يبدو أن هناك طريقة أفضل.

هل كانت مفيدة؟

المحلول

PHP لديه ما يعادل مباشر يسمى مولدات كهرباء.

قديم (ما قبل PHP 5.5 إجابة):

لسوء الحظ، ليس هناك ما يعادل اللغة. أسهل طريقة هي إما ما تفعله بالفعل، أو إنشاء كائن يستخدم متغيرات المثيل للحفاظ على الحالة.

ومع ذلك، هناك خيار جيد إذا كنت ترغب في استخدام الوظيفة بالاقتران مع العبارة FOREACTION: SPL ITERATONS.. وبعد يمكن استخدامها لتحقيق شيء مشابه تماما لمولدات Python.

نصائح أخرى

هناك RFC في https://wiki.php.net/rfc/generators. المعالجة فقط، والتي قد يتم تضمينها في PHP 5.5.

في الوقت نفسه، تحقق من هذا المفهوم من مفهوم "وظيفة مولد الفقراء" المنفذة في userland.

namespace Functional;

error_reporting(E_ALL|E_STRICT);

const BEFORE = 1;
const NEXT = 2;
const AFTER = 3;
const FORWARD = 4;
const YIELD = 5;

class Generator implements \Iterator {
    private $funcs;
    private $args;
    private $key;
    private $result;

    public function __construct(array $funcs, array $args) {
        $this->funcs = $funcs;
        $this->args = $args;
    }

    public function rewind() {
        $this->key = -1;
        $this->result = call_user_func_array($this->funcs[BEFORE], 
                                             $this->args);
        $this->next();
    }

    public function valid() {
        return $this->result[YIELD] !== false;
    }

    public function current() {
        return $this->result[YIELD];
    }

    public function key() {
        return $this->key;
    }

    public function next() {
        $this->result = call_user_func($this->funcs[NEXT], 
                                       $this->result[FORWARD]);
        if ($this->result[YIELD] === false) {
            call_user_func($this->funcs[AFTER], $this->result[FORWARD]);
        }
        ++$this->key;
    }
}

function generator($funcs, $args) {
    return new Generator($funcs, $args);
}

/**
 * A generator function that lazily yields each line in a file.
 */
function get_lines_from_file($file_name) {
    $funcs = array(
        BEFORE => function($file_name) { return array(FORWARD => fopen($file_name, 'r'));   },
        NEXT   => function($fh)        { return array(FORWARD => $fh, YIELD => fgets($fh)); },
        AFTER  => function($fh)        { fclose($fh);                                       },
    );
    return generator($funcs, array($file_name));
}

// Output content of this file with padded linenumbers.
foreach (get_lines_from_file(__FILE__) as $k => $v) {
    echo str_pad($k, 8), $v;
}
echo "\n";

أنا أولي كل شيء في بيثون قبل التنفيذ بأي لغة أخرى، بما في ذلك PHP. انتهى بي الأمر باستخدام عمليات الاسترجاعات لتحقيق ما أود به yield.

function doSomething($callback) 
{
    foreach ($something as $someOtherThing) {
        // do some computations that generates $data

        call_user_func($callback, $data);
    }
}

function myCallback($input)
{
    // save $input to DB 
    // log
    // send through a webservice
    // etc.
    var_dump($input);
}


doSomething('myCallback');

بهذه الطريقة كلها $data يتم تمريرها إلى وظيفة رد الاتصال ويمكنك أن تفعل ما تريد.

تمديد إجابة @ Luiz - طريقة رائعة أخرى هي استخدام وظائف مجهولة:

function iterator($n, $cb)
{
    for($i=0; $i<$n; $i++) {
        call_user_func($cb, $i);
    }
}

$sum = 0;
iterator(10,
    function($i) use (&$sum)
    {
        $sum += $i;
    }
);

print $sum;

قد لا يكون هناك مشغل معادل، ولكن الكود التالي هو مكافئ في الوظيفة والنفقات العامة:

function file_lines($file) {
  static $fhandle;

  if ( is_null($fhandle) ) {
    $fhandle = fopen($file, 'r');

    if ( $fhandle === false ) {
      return false;
    }
  }

  if ( ($line = fgets($fhandle))!== false ) {
    return $line;
  }


  fclose($fhandle);
  $fhandle = null;
}

while ( $line = file_lines('some_file') ) {
  // ...
}

هذا يبدو عن الحق. آسف، أنا لم اختبرها.

نفس الجملة موجودة الآن على PHP 5.5:

http://php.net/manual/en/language.generators.syntax.php.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top