Скаляр с памятью или как правильно `завязать` - PullRequest
7 голосов
/ 23 сентября 2019

Это моя попытка решить задачу № 2 из еженедельно .

Задача очень расплывчатая, поэтому я решил попытаться реализовать скалярное значение с помощью памяти.Возможно, мое понимание того, как должны работать контейнеры, неверно, но я действительно не понимаю, почему say self.VAR.WHAT - это Proxy, а не HistoryProxy, даже когда я прямо говорю об этом.

class HistoryProxy is Proxy
{
    has @!history;
    method HISTORY() { @!history };
    method SAVE( $value ) { @!history.push($value) }
}

sub historic(::T $type, $value) {
    my T $c-value = $value;
    return-rw HistoryProxy.new(
        FETCH => method () { $c-value },
        STORE => method (T $new-value) {
            say self.VAR.WHAT;         # Why is this "Proxy" and not "HistoryProxy"?
            self.VAR.SAVE( $c-value ); # Why does this not work?
            $c-value = $new-value;
        }
    );
}

my $a := historic(Int, 10);
$a = 12;
$a = 14;
say $a.VAR.HISTORY; #should print [10, 12]

Ответы [ 2 ]

6 голосов
/ 23 сентября 2019

Это не поможет вам получить желаемую функциональность, но пока отвечает на ваши конкретные вопросы:

say self.VAR.WHAT;# Почему это «Proxy», а не «HistoryProxy»?

Я думаю, это потому, что класс Proxy в настоящее время не настроен для подкласса.Его new метод в основном делает Proxy.new вместо self.new, поэтому он пропускает подклассы.Рассмотрим это сейчас.

self.VAR.SAVE ($ c-value);# Почему это не работает?

self является всегда деконтейнером.Таким образом, вы всегда видите основную ценность.Если вы хотите иметь фактический объект, вам нужно будет изменить подпись method, например:

STORE => method (\SELF: T $new-value) {

, а затем использовать:

 SELF.VAR

Но так как объектна самом деле не является благословенным подклассом, в любом случае это вам не очень поможет.

ОБНОВЛЕНИЕ: https://github.com/rakudo/rakudo/pull/3196 должно разрешить создание подклассов Proxy объектов в будущем.

ОБНОВЛЕНИЕ: с https://github.com/rakudo/rakudo/commit/d00674b31c этот запрос на объединение был объединен.Он должен стать доступным в выпуске компилятора Rakudo 2019.10.

0 голосов
/ 26 сентября 2019

Еще раз спасибо, Лиз, за ​​то, что так быстро обратилась к этому.Однако, как я писал на Github, в то время как подклассы работают сейчас, кажется, что невозможно инициализировать атрибуты подкласса.Это прекрасно, я знаю, я могу решить это с помощью метода get-position.

Это потому, что Proxy не является "правильным" классом или я что-то пропускаю?

use Test;

class Scalar::History::Proxy is Proxy
{
    has @!history;
    has $!position = 0; # The assignment gets ignored ...

    # ... so do these

    submethod TWEAK( *@args )
    {
        say "Why oh why?";
    }

    submethod BUILD( *@args )
    {
        say "Do we never get called";
    }

    method get-position( \SELF: )
    {
        $!position // @!history.elems
    }

    method current-value( \SELF: )
    {
        @!history[ SELF.get-position ]
    }

    method get-history( \SELF: Bool :$all = False )
    {
        my $to-index = $all ?? @!history.elems - 1 !! SELF.get-position;
        @!history[ ^$to-index ].Array
    }

    method reset-history( \SELF: )
    {
        @!history = ();
        $!position = 0;
    }

    method forward-history( \SELF: $steps )
    {
        $!position = SELF.get-position + $steps;
        $!position = @!history.elems - 1
            if $!position >= @!history.elems;
        $!position;
    }

    method rewind-history( \SELF: $steps )
    {
        $!position = SELF.get-position - $steps;
        $!position = 0
            if $!position < 0;
        $!position;
    }

    method store-value( \SELF: $new-value, $register-duplicates )
    {
        # Forget stuff after rewind
        if @!history.elems > SELF.get-position + 1
        {
            @!history.splice( SELF.get-position + 1 );
        }

        if !($new-value eqv SELF.current-value) || $register-duplicates
        {
            @!history.push( $new-value );
            $!position = @!history.elems - 1;
        }
    }
}

class Scalar::History
{
    method create( $value, ::T $type = Any, Bool :$register-duplicates = False )
    {
        return-rw Scalar::History::Proxy.new(
            FETCH => method ( \SELF: ) {
                SELF.current-value() },
            STORE => method ( \SELF: T $new-value ) {
                SELF.store-value( $new-value, $register-duplicates ); }
        ) = $value;
    }
}

Это проходит все мои тесты

use Scalar::History;

use Test;

subtest 'untyped' =>
{
    plan 2;

    my $untyped := Scalar::History.create("a");
    my $sub = sub foo() { * };
    my $rx  = rx/x/;

    $untyped = $sub;
    $untyped = $rx;
    $untyped = 42;

    ok( $untyped == 42, "Current value is correct" );
    is-deeply( $untyped.VAR.get-history, ["a", $sub, $rx], "History is correct" );
}

subtest 'typed' =>
{
    plan 3;

    my $typed := Scalar::History.create("a", Str);
    $typed = "b";
    $typed = "42";

    ok( $typed == "42", "Current value is correct" );
    is-deeply( $typed.VAR.get-history, ["a", "b"], "History is correct" );
    dies-ok( { $typed = 2; }, "Cannot assign invalid type" );
}

subtest 'duplicates' =>
{
    plan 2;

    my $with-duplicates := Scalar::History.create( "a", Str, :register-duplicates(True) );
    $with-duplicates = "a";
    $with-duplicates = "a";
    is-deeply( $with-duplicates.VAR.get-history, ["a", "a"], "duplicates get registered" );

    my $no-duplicates := Scalar::History.create( "a", Str );
    $no-duplicates = "a";
    $no-duplicates = "a";
    is-deeply( $no-duplicates.VAR.get-history, [], "duplicates get ignored" );
}

subtest 'position/forward/backward' =>
{
    plan 8;
    my Int $int := Scalar::History.create(10, Int);
    #say $int.VAR.get-position;
    $int = 100 ;
    $int = 1000 ;
    ok( $int.VAR.get-position == 2, "current position is 2 after 3 assignments" );
    $int.VAR.rewind-history(2);
    ok( $int == 10, "current value is correct after rewind" );
    $int.VAR.forward-history(1);
    ok( $int == 100, "current value is correct after forward" );
    $int.VAR.rewind-history(Inf);
    ok( $int == 10, "current value equals start value after rewind to infinity" );
    $int.VAR.forward-history(Inf);
    ok( $int == 1000, "current value equals last known value after forward to infinity" );

    $int.VAR.rewind-history(2);
    is-deeply( $int.VAR.get-history, [], "history empty after rewind" );
    is-deeply( $int.VAR.get-history(:all), [10, 100], "but still there if needed" );
    $int = 101;
    $int = 1001;
    is-deeply( $int.VAR.get-history, [10, 101], "history gets truncated after rewind and assign" );
}

subtest 'behaviour' =>
{
    plan 2;

    sub add-one( Int $v ) { return $v + 1 }

    my Int $int := Scalar::History.create(1, Int);
    $int++;
    $int = $int + 1;
    $int = add-one( $int );
    $int = 42;

    is-deeply( $int.VAR.get-history, [1,2,3,4], "historic Int behaves like normal Int" ); # probably testing the language here, but  meh
    $int.VAR.reset-history();
    is-deeply( $int.VAR.get-history(:all), [], "history can be reset" );
}

done-testing;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...