Можно ли вызвать сортировку суперкласса с помощью сравнения подклассов в perl? - PullRequest
0 голосов
/ 23 февраля 2019

Я хочу использовать сортировку суперкласса, которая использует функцию сравнения подклассов.Я попытался выяснить природу вопроса в следующем коде.Это не «производственный» код, но он представлен здесь для иллюстрации.Он проверен.

#!/usr/bin/perl
# $Id: foo,v 1.10 2019/02/23 14:14:33 bennett Exp bennett $

use strict;
use warnings;

package Fruit;
use Scalar::Util 'blessed';

sub new {
    my $class = shift;
    my $self = bless({}, $class);
    $self->{itemList} = [];
    warn "Called with class ", blessed $self, "\n";
    return $self;
}

package Apples;

use parent qw(-norequire Fruit);

sub mySort {
    my $self = shift;
    @{$self->{itemList}} = sort compare @{$self->{itemList}};
    return $self;
}

sub compare {
    $a->{mass} <=> $b->{mass};
}

package main;

my $apfel = Apples->new();
push(@{$apfel->{itemList}}, { "name" => "grannysmith", "mass" => 12 });
push(@{$apfel->{itemList}}, { "name" => "macintosh", "mass" => 6 });
push(@{$apfel->{itemList}}, { "name" => "Alkmene", "mass" => 8 });

$apfel->mySort();

for my $f (@{$apfel->{itemList}}) {
    printf("%s is %d\n", $f->{name}, $f->{mass});
}

exit 0;

Я хочу переместить mySort() в абстрактный суперкласс Fruit.Я пробовал несколько способов обратиться к подпрограмме $self->compare(), но мне не очень повезло.

Есть мысли?

Я получил его, чтобы вызвать правильную подпрограмму,но никогда с правильными $a и $b.Я оставил все свои неудачные попытки из этого вопроса в надежде, что кто-то сразу поймет, как переместить пакет mySort() в Fruit, чтобы я мог сортировать свои апельсины с помощью той же подпрограммы.

Ответы [ 3 ]

0 голосов
/ 23 февраля 2019

Переменные пунктуации, такие как $_ [1] , называются "суперглобальными", поскольку они ссылаются на переменную в пространстве имен main::. [2] ВДругими словами, независимо от текущего пакета, $_ - это сокращение от $main::_.

$a и $b не являются суперглобальными.Это обычные переменные пакета.sort заполняет $a и $b пакета, в котором находится sort, что приводит к проблемам, если sort и функция сравнения находятся в разных пакетах.Это означает, что перемещение mySort в Fruit :: приведет к заполнению sort $Fruit::a и $Fruit::b, но ваша функция compare читает $Apple::a и $Apple::b.

.Несколько решений, которые вы могли бы использовать, когда задействовано несколько пакетов, но самое простое - использовать прототип ($$) в функции сравнения.Это заставляет sort передавать значения для сравнения в качестве аргументов вместо использования $a и $b.

package Foo;
my $compare = \&Bar::compare;
my @sorted = sort $compare @unsorted;

package Bar;
sub compare($$) { $_[0] cmp $_[1] }

sort вызывает подпрограмму как функцию, а не метод.Если вы хотите, чтобы он вызывался как метод, вам понадобится обертка.

package Foo;
my @sorted = sort { Bar->compare($a, $b) } @unsorted;

package Bar;
sub compare { $_[1] cmp $_[2] }

Тем не менее, идея иметь sort в одном классе и сортировщике в подклассе в корне ошибочна.Предположительно, у вас может быть список, содержащий как яблоки, так и апельсины, так как вы можете определить, какой compare метод вызывать?

package Foo;
my @sorted = sort { ???->compare($a, $b) } @unsorted;

package Bar;
sub compare { $_[1] cmp $_[2] }

  1. И несколько именованных тоженапример, STDIN.

  2. Используя полное имя (например, $package::_), вы можете получить доступ к переменным пунктуации других пакетов.Это не имеет особого значения;они не используются самим Perl.

0 голосов
/ 23 февраля 2019

У тебя две проблемы.Во-первых, вам нужна функция mySort в суперклассе, чтобы вызвать функцию compare для правильного подкласса.Во-вторых, вам нужна функция compare в подклассе, чтобы иметь возможность получать два элемента, которые она хочет сравнить, из вызова в другом пакете.

Неясно, разработали ли вы решение для первогопроблема, но одним из решений является использование UNIVERSAL::can, чтобы найти правильный метод сравнения.

package Fruit;
sub mySort {
    my $self = shift;
    my $compare_func = $self->can("compare");
    @{$self->{itemList}} = sort $compare_func @{$self->{itemList}};
}

Это найдет правильную функцию подкласса compare и будет использовать ее в вызове сортировки.

Теперь проблема в функции Apples::compare заключается в том, что когда Fruit::mySort будет готов сравнить пару элементов, он установит переменные пакета $Fruit::a и $Fruit::b, а не $Apples::a и $Apples::b.Поэтому ваша Apples::compare функция должна быть готова к этому.Вот несколько решений:

package Apples;
sub compare {
    package Fruit;
    $a->{mass} <=> $b->{mass};
}

или

sub compare {
    $Fruit::a->{mass} <=> $Fruit::b->{mass}
}

или более,

package Apples;
sub compare {
    my $pkg = caller;
    if ($pkg ne __PACKAGE__) {
        no strict 'refs';
        $a = ${"${pkg}::a"};
        $b = ${"${pkg}::b"};
    }
    $a->{mass} <=> $b->{mass}
}

Обновление :Я думал о создании атрибута подпрограммы, который копировал бы значения $a и $b в правильный пакет, но после сравнения его и обдумывания альтернатив, я решил отказаться от него.Вот мои результаты для потомков:

Рассмотрим три процедуры сортировки (которые могут быть в другом пакете и которые трудно использовать из текущего пакета)

sub numsort { $a <=> $b }
sub lexsort { $a cmp $b }
sub objsort { $a->{value} <=> $b->{value} }

Вот несколько способов, которыми мы можем сделать этодоступные пакеты:

  1. реализуют атрибут подпрограммы для подготовки переменных $a и $b в нужном пакете.Реализация здесь слишком длинная, чтобы включить ее, но под-объявление будет выглядеть так:

    sub numsort : CrossPkg { $a <=> $b }
    
  2. переписать функцию сравнения для сравнения $_[0] и $_[1] вместо $a и $b, и используйте обертку в вызове sort

    sub lexcmp { $_[0] cmp $_[1] }
    ...
    @output = sort { lexcmp($a,$b) } @input
    
  3. Выполните вызов сортировки в правильном пакете, чтобы установить правильные значения $a и $b.

    @output = do { package OtherPackage; sort numsort @input };
    

И вот результаты сравнительного анализа.local - это обычный вызов sort без проблем с несколькими пакетами.

                 Rate attrib-numsort    wrap-numcmp  local-numsort repkg-numsort
attrib-numsort 1.17/s             --           -90%           -96%          -96%
wrap-numcmp    11.6/s           885%             --           -61%          -64%
local-numsort  29.5/s          2412%           155%             --           -8%
repkg-numsort  32.2/s          2639%           178%             9%            --

                 Rate attrib-lexsort  repkg-lexsort    wrap-lexcmp local-lexsort
attrib-lexsort 3.17/s             --           -12%           -14%          -17%
repkg-lexsort  3.60/s            13%             --            -2%           -5%
wrap-lexcmp    3.68/s            16%             2%             --           -3%
local-lexsort  3.80/s            20%             6%             3%            --

                 Rate attrib-objsort    wrap-objcmp  local-objsort repkg-objsort
attrib-objsort 1.22/s             --           -81%           -88%          -89%
wrap-objcmp    6.32/s           417%             --           -38%          -44%
local-objsort  10.1/s           730%            61%             --          -10%
repkg-objsort  11.3/s           824%            79%            11%            --

Сводка : издержки менее важны для lexsort, где каждое сравнениезанимает больше времениАтрибутный подход мертв по прибытии.Установка пакета, входящего в вызов sort, дает наилучшие результаты - более или менее без дополнительных затрат - но он не подходит для этого приложения (в иерархии объектов).Переписать функцию сравнения и включить функцию в вызов sort не так уж плохо для снижения производительности, и она работает в иерархии объектов, поэтому окончательная рекомендация:

package Fruit;
sub compare { ... }
sub mySort {
    my $self = shift;
    @{$self->{itemList}} =
        sort { $self->can("compare")->($a,$b) } @{$self->{itemList}};
}

package Apples;
our @ISA = qw(Fruit)
sub compare { $_[0]->{mass} <=> $_[1]->{mass} }
0 голосов
/ 23 февраля 2019

Переменные $a и $b используются sort как переменные пакета в том же пакете, который был вызван для сортировки, поэтому, чтобы дочерний класс мог их увидеть, вы можете попробовать это.

В родительском классе:

sub mySort {
    my $self = shift;
    @{$self->{itemList}} = sort { $self->compare($a, $b) } @{$self->{itemList}};
    return $self;
}

В дочернем классе:

sub compare {
    my ( $self, $a, $b ) = @_;
    $a->{mass} <=> $b->{mass};
}
...