1

Тема: Задача на 5

Всім привіт. Сьогодні виконав цікаву задачу вирішив запитати і вас, можливо хтось вирішить.

Отже є код

class Dealer {

    public $name = null;

    public function __construct($name) {
      $this->name = $name;
    }

    public function getName() {
      return MemoryCache::load(function () {
        return strtolower($this->name); //point 1
      });
    }

    public function getUName() {
      return MemoryCache::load(function () {
        return strtoupper($this->name); //point 2
      });
    }

  }

  $dealer = new Dealer('funivan');
  echo $dealer->getName() . "\n";
  echo $dealer->getName() . "\n";
  echo $dealer->getName() . "\n";
  echo $dealer->getUName() . "\n";
  echo $dealer->getUName() . "\n";
  echo $dealer->getUName() . "\n";

Необхідно реалізувати клас MemoryCache так, що б функції (там де point 1 i point 2 ) виконувались тільки 1н раз. Клас Dealer міняти не можна.
Враховуємо що у нас може буди безліч класів які викликають MemoryCache::load
Також ми не знаємо порядку виклику getName і getUName
Версія PHP - немає значення)

Для чого ця вся штука. Це простий приклад у якому функція обробки ім’я працює дуже швидко, але ми у кеш можемо засунути результат функції яка виконується досить довго. Відповідно всі наступні виклики будуть швидші.

Для тих хто знає відповідь засовуємо вирішення у тег code і це у spoiler
Бажаю успіху.

Подякували: leofun011

2

Re: Задача на 5

Сторонні бібліотеки можна застосовувати?

Прихований текст

Я не придумав нічого кращого за серіалізацію замикання і складання результату серіалізації і результату виконання в словник. Так можна вихлюпнути дитину разом з водою, але воно має працювати.

3

Re: Задача на 5

Загнати в файлик результат першого виконання методу,далі дані брати з нього ?)

4

Re: Задача на 5

Сторонні бібліотеки можна
Існує і інший спосіб крім серіалізації я не пробував сереалізувати, але пробуйте.
Втрім, просто зверху файла допишіть свою реалізацію класу.

5 Востаннє редагувалося VTrim (04.03.2015 13:38:25)

Re: Задача на 5

Версія PHP тут має значення,бо я почав писати на PHP 5.3,а використання оператора $this в анонімних функціях тільки з PHP 5.4

Для роботи створити директорію /cache/

Код..

Прихований текст
<?php 

class MemoryCache {

 public static function load($param) {

    $content = call_user_func($param);

    if(!file_exists('cache/'.$content)) {

     file_put_contents('cache/'.$content,$content);

     return $content;

    }

    else {

    return  file_get_contents('cache/'.$content);

   }

 }

}

class Dealer {
 
    public $name = null;
 
    public function __construct($name) {
      $this->name = $name;
    }
 
    public function getName() {
      return MemoryCache::load(function () {
        return strtolower($this->name); //point 1
      });
    }
 
    public function getUName() {
      return MemoryCache::load(function () {
        return strtoupper($this->name); //point 2
      });
    }
 
  }
 
  $dealer = new Dealer('funivan');
  echo $dealer->getName() . "\n";
  echo $dealer->getName() . "\n";
  echo $dealer->getName() . "\n";
  echo $dealer->getUName() . "\n";
  echo $dealer->getUName() . "\n";
  echo $dealer->getUName() . "\n";

6

Re: Задача на 5

Код працює але не так як треба. Ось умова задачі:
Необхідно реалізувати клас MemoryCache так, що б функції (там де point 1 i point 2 ) виконувались тільки 1н раз. Клас Dealer міняти не можна.

Тобто фукнція Dealer::getName має виконуватись 1н раз.
У вас вона виконується декілька разів. Але результат виконання береться тільки перший. Тобто тут немає смислу брати з кешу результат якщо функція і так повертає результат. Вона ж виконалась)

Спробуйте ще ;) якщо не ясно розкажу детальніше)

7 Востаннє редагувалося quez (04.03.2015 14:02:07)

Re: Задача на 5

Як Dealer::getName може виконуватись один раз, якщо ви викликаєте його три рази вручну?

8

Re: Задача на 5

Не зрозумів. У мене при першому виконанні може довго генеруватися результат,тому я заношу нього в кеш,щоб при наступному виклику функції взяти готовий результат,а не тратити час на генерацію.

9

Re: Задача на 5

Перепрошую. Код 

return strtolower($this->name); //point 1

має викликатись тільки 1н раз.
Тобто у функції Dealer::getName є callable функція. Вона і має викликатись тільки раз
А саму фукнцію Dealer::getName можна викликати безліч разів. Але перший раз вона виконує closure і записує десь в кеш а всі наступні рази просто з кешу дістається інформація )

10

Re: Задача на 5

VTrim написав:

Не зрозумів. У мене при першому виконанні може довго генеруватися результат,тому я заношу нього в кеш,щоб при наступному виклику функції взяти готовий результат,а не тратити час на генерацію.

Ви на вірному шляху але ви викликаєте замикання через call_user_func($param);
Відповідно  за весь час виконання strtolower має спрацьовувати 2 рази а у вас він спрацьовує кожного разу)

11

Re: Задача на 5

Це можна вирішити,перевіряючи просто наявність файлу без конкретного імені,тоді не прийдеться лишній раз викликати анонімну функцію для ідентифікації хешу наприклад для аргументу "funivan" файл funivan.file. Виклик буде тільки один раз,у разі відсутності хешу.

12

Re: Задача на 5

Vtrim - ну спробуйте )
Але як ви будете знати який файл завантажувати при виклику мутоду getName та при виклику методу getUName
Вони ж генерять різний вміст ;)

13 Востаннє редагувалося TwiStar (04.03.2015 16:25:40)

Re: Задача на 5

Я зробив досить брудним хаком.

Прихований текст
class MemoryCache
{
    static $stored;

    static function load(callable $function)
    {
        $ref = new ReflectionFunction($function);
        $refName = get_class($ref->getClosureScopeClass())."_".$ref->getEndLine();

        if (!self::$stored[$refName]) {
            self::$stored[$refName] = call_user_func($function);
        }
        return self::$stored[$refName];
    }
}

Також мушу зазначити дві речі
а) версія PHP все-ж таки має значення, бо навіть оригінальний код буде виконуватися лише на PHP 5.3
б) це за дизайном не дуже добрий метод прив'язувати функції до конейнеру, я б змінив сигнатуру методу Load на function load($alias, callable $function), та використовував щось на зразок неймспейсів, щоб уникнути коллізії імен, тобто виклик був би:

MemoryCache::load('Dealer.getName', function(){})

Це дозволить уникнути усіх цих хаків, бо сам по собі той спосіб, що використовується не дуже швидкий, я б навіть сказав, дуже не швидкий.

Подякували: funivan, quez, leofun013

14

Re: Задача на 5

TwiStar - норм) такий самий спосіб.

15 Востаннє редагувалося VTrim (04.03.2015 16:19:50)

Re: Задача на 5

Ясно,про такі фокуси не знав :)

16

Re: Задача на 5

Версія PHP не має значення - я мав на увазі наступне: на якій зможете реалізувати на такій і піде)

Це дозволить уникнути усіх цих хаків, бо сам по собі той спосіб, що використовується не дуже швидкий, я б навіть сказав, дуже не швидкий.

Звісно що це не швидкий спосіб) але знати що можна так зробити все ж таки варто.
У майбутньому може десь пригодитись рефлексія ) ;)

17

Re: Задача на 5

Не знаю, у мене майже кожен раз коли я використовую рефлексію виникає відчуття що я виконую якісь дії, яких я не повинен виконувати.

Але іноді без них ніяк. Наприклад, якщо робити свій DI-контейнер, то автоматична ін'єкція залежностей без рефлексії неможлива. Також, я працюю із самописним фреймворком, і там усі роути а також деякі інші параметри до дій контроллера виконуються через аннотацію до метода, і для їх обробки також використовується рефлексія (для створення кешу на основі аннотацій)

18 Востаннє редагувалося ktretyak (04.03.2015 19:21:12)

Re: Задача на 5

Тю, мені здавалось що моя відповідь буде очевидною і для інших

Прихований текст
class MemoryCache {
  
  static $cache;
  
  static function load($callback)
  {
    $hash = spl_object_hash($callback);
    
    if( isset(self::$cache[$hash]) )
      return self::$cache[$hash];
    else
      return self::$cache[$hash] = $callback();
  }
}

Оновлено:
Трошечки поспішив написавши і не перевіривши роботи, зараз трохи підправив. Суть залишилась та ж.

Подякували: quez1

19

Re: Задача на 5

ktretyak написав:

Тю, мені здавалось що моя відповідь буде очевидною і для інших

Прихований текст
class MemoryCache {
  
  static $cache;
  
  static function load($callback)
  {
    $hash = spl_object_hash($callback);
    
    if( isset(self::$cache[$hash]) )
      return self::$cache[$hash];
    else
      return self::$cache[$hash] = $callback();
  }
}

Оновлено:
Трошечки поспішив написавши і не перевіривши роботи, зараз трохи підправив. Суть залишилась та ж.

Так хеш може бути використаний для іншого об’єкта, а в колбека час життя дуже короткий. Тут теоретично можуть бути баги.

20

Re: Задача на 5

Але, як виявляється, моє рішення не є ще вирішенням, бо spl_object_hash() не працює, так як я очікував.

Обидві функції повертають однаковий хеш, але це не означає, що у них однаковий код:

echo spl_object_hash( function () { return strtolower($this->name); } ).'<br>';

echo spl_object_hash( function () { return strtoupper($this->name); } );