Pergunta

Eu entendo um usa o "abençoar" palavra-chave em Perl dentro do método "novo" de uma classe:

sub new {
    my $self = bless { };
    return $self;
}    

Mas o que exatamente é "abençoar" a fazer para que a referência de hash?

Foi útil?

Solução

Em geral, bless associa um objeto com uma classe.

package MyClass;
my $object = { };
bless $object, "MyClass";

Agora, quando você chamar um método em $object, Perl saber qual pacote de pesquisa para o método.

Se o segundo argumento for omitido, como no seu exemplo, é usado o atual pacote / classe.

Por uma questão de clareza, o seu exemplo pode ser escrito da seguinte forma:

sub new { 
  my $class = shift; 
  my $self = { }; 
  bless $self, $class; 
} 

EDIT: Veja KiXX é bom resposta para um pouco mais detalhadamente.

Outras dicas

bless associa uma referência a um pacote.

Não importa o que a referência é a, que pode ser para um hash (caso mais comum), para uma matriz (não tão comum), a um escalar (geralmente isso indica uma dentro para fora objeto ), a uma expressão regular , sub-rotina ou TYPEGLOB (ver o livro Object Oriented Perl: Um guia completo para Conceitos e técnicas de programação por Damian Conway para exemplos úteis) ou até mesmo uma referência a um identificador de arquivo ou diretório (caso menos comum).

O efeito bless-ing tem é que ele permite que você aplique sintaxe especial à referência abençoada.

Por exemplo, se uma referência abençoada é armazenado em $obj (associado por bless com o pacote de "Classe"), então $obj->foo(@args) vai chamar um foo sub-rotina e passar como primeiro argumento o $obj referência seguido pelo resto dos argumentos (@args) . A sub-rotina deve ser definido no pacote de "classe". Se não houver foo sub-rotina no pacote "Classe", uma lista de outros pacotes (formulário tomado a @ISA matriz no pacote "Classe") será procurado ea primeira foo sub-rotina encontrada será chamado.

A versão curta:. Está marcando que hash como anexado ao namespace pacote atual (de modo que o pacote fornece sua implementação classe)

Esta função diz a entidade referenciada por REF que agora é um objeto no pacote CLASSNAME, ou o pacote atual se CLASSNAME é omitido. A utilização da forma de dois argumentos de abençoar é recomendado.

Exemplo :

bless REF, CLASSNAME
bless REF

Valor de retorno

Esta função devolve a referência a um objecto abençoado em CLASSNAME.

Exemplo :

A seguir é o código de exemplo mostrando seu uso básico, a referência do objeto é criado por abençoar uma referência para a classe do pacote -

#!/usr/bin/perl

package Person;
sub new
{
    my $class = shift;
    my $self = {
        _firstName => shift,
        _lastName  => shift,
        _ssn       => shift,
    };
    # Print all the values just for clarification.
    print "First Name is $self->{_firstName}\n";
    print "Last Name is $self->{_lastName}\n";
    print "SSN is $self->{_ssn}\n";
    bless $self, $class;
    return $self;
}

Eu vou dar uma resposta aqui desde os aqui não bastante clique para mim.

Perl do abençoe função associa qualquer referência a todas as funções dentro de um pacote.

Por que precisamos disso?

Vamos começar por expressar um exemplo em JavaScript:

(() => {
    'use strict';

    class Animal {
        constructor(args) {
            this.name = args.name;
            this.sound = args.sound;
        }
    }

    /* [WRONG] (global scope corruption)
     * var animal = Animal({
     *     'name': 'Jeff',
     *     'sound': 'bark'
     * }); 
     * console.log(animal.name + ', ' + animal.sound); // seems good
     * console.log(window.name); // my window's name is Jeff?
     */

    // new is important!
    var animal = new Animal(
        'name': 'Jeff',   
        'sound': 'bark'
    );

    console.log(animal.name + ', ' + animal.sound); // still fine.
    console.log(window.name); // undefined
})();

Agora vamos despir a construção de classe e fazer sem ele:

(() => {
    'use strict';

    var Animal = function(args) {
        this.name = args.name;
        this.sound = args.sound;
        return this; // implicit context hashmap
    };

    // the "new" causes the Animal to be unbound from global context, and 
    // rebinds it to an empty hash map before being constructed. The state is
    // now bound to animal, not the global scope.
    var animal = new Animal({
        'name': 'Jeff',
        'sound': 'bark'
    });
    console.log(animal.sound);    
})();

A função usa uma tabela hash de propriedades não ordenadas (uma vez que não faz sentido ter de propriedades de escrita em uma ordem específica em linguagens dinâmicas, em 2016) e retorna uma tabela hash com essas propriedades, ou se você se esqueceu de colocar o novo palavra-chave, ele irá retornar todo o contexto global (por exemplo janela no navegador ou global em nodejs).

Perl não tem "isto" ou "novo" nem "classe", mas ele ainda pode ter uma função que se comporta de forma semelhante. Não vamos ter um construtor nem um protótipo, mas vamos ser capazes de criar novos animais à vontade e modificar suas propriedades individuais.

# self contained scope 
(sub {
    my $Animal = (sub {
        return {
            'name' => $_[0]{'name'},
            'sound' => $_[0]{'sound'}
        };
    });

    my $animal = $Animal->({
        'name' => 'Jeff',
        'sound' => 'bark'
    });

    print $animal->{sound};
})->();

Agora, temos um problema: e se nós queremos o animal para executar os sons por si mesmos, em vez de nos imprimir o que sua voz é. Isto é, queremos uma função performSound que imprime próprio som do animal.

Uma maneira de fazer isso é através do ensino de cada animal como fazê-lo de som. Isto significa que cada gato tem sua própria função duplicado para performSound.

# self contained scope 
(sub {
    my $Animal = (sub {
        $name = $_[0]{'name'};
        $sound = $_[0]{'sound'};

        return {
            'name' => $name,
            'sound' => $sound,
            'performSound' => sub {
                print $sound . "\n";
            }
        };
    });

    my $animal = $Animal->({
        'name' => 'Jeff',
        'sound' => 'bark'
    });

    $animal->{'performSound'}();
})->();

Isso é ruim porque performSound é colocado como um totalmente novo objeto de função cada vez que um animal é construído. 10000 animais significa 10000 performSounds. Queremos ter uma única função performSound que é usado por todos os animais que olha para cima seu próprio som e imprime-lo.

(() => {
    'use strict';

    /* a function that creates an Animal constructor which can be used to create animals */
    var Animal = (() => {
        /* function is important, as fat arrow does not have "this" and will not be bound to Animal. */
        var InnerAnimal = function(args) {
            this.name = args.name;
            this.sound = args.sound;
        };
        /* defined once and all animals use the same single function call */
        InnerAnimal.prototype.performSound = function() {
            console.log(this.name);
        };

        return InnerAnimal;
    })();

    /* we're gonna create an animal with arguments in different order
       because we want to be edgy. */
    var animal = new Animal({
        'sound': 'bark',
        'name': 'Jeff'
    });
    animal.performSound(); // Jeff
})();

Aqui é onde o paralelo com Perl pára meio.

novo operador do JavaScript não é opcional, sem ele, "este" escopo global dentro de métodos de objetos corrompe:

(() => {
    // 'use strict'; // uncommenting this prevents corruption and raises an error instead.

    var Person = function() {
        this.name = "Sam";
    };
//    var wrong = Person(); // oops! we have overwritten window.name or global.main.
//    console.log(window.name); // my window's name is Sam?
    var correct = new Person; // person's name is actually stored in the person now.

})();

Queremos ter uma função para cada animal que olha para cima próprio som daquele animal em vez de codificar-lo na construção.

Blessing nos permite usar um pacote como o protótipo de objetos. Desta forma, o objeto está ciente do "pacote" é "referenciado", e por sua vez pode ter as funções no pacote "alcance para" os casos específicos que foram criados a partir do construtor de que "objeto de pacote":

package Animal;
sub new {
    my $packageRef = $_[0];
    my $name = $_[1]->{'name'};
    my $sound = $_[1]->{'sound'};

    my $this = {
        'name' => $name,
        'sound' => $sound
    };   

    bless($this, $packageRef);
    return $this;
}

# all animals use the same performSound to look up their sound.
sub performSound {
    my $this = shift;
    my $sound = $this->{'sound'};
    print $sound . "\n";
}

package main;
my $animal = Animal->new({
    'name' => 'Cat',
    'sound' => 'meow'
});
$animal->performSound();

Resumo / TL; DR :

Perl não tem "isto", "classe", nem "novo". abençoando um objeto para um pacote dá esse objeto uma referência ao pacote, e quando ele chama funções no pacote, seus argumentos serão compensados ??por 1 slot, e o primeiro argumento ($ _ [0] ou shift) será equivalente a javascript de "isto". Por sua vez, você pode tanto simular modelo de protótipo de JavaScript.

Infelizmente torna impossível (no meu entender) para criar "novas classes" em tempo de execução, como você precisa de cada "classe" de ter seu próprio pacote, enquanto que em javascript, você não precisa de pacotes em tudo, como " nova" palavra-chave torna-se um hashmap anônimo para você usar como um pacote em tempo de execução para o qual você pode adicionar novas funções e funções de remover na mosca.

Existem algumas bibliotecas Perl criando suas próprias maneiras de colmatar esta limitação na expressividade, como alces.

Por que a confusão :

Por causa de pacotes. Nossa intuição nos diz para ligar o objeto para um hashmap contendo seu protótipo. Isso nos permite criar "pacotes" em tempo de execução como JavaScript pode. Perl não têm essa flexibilidade (pelo menos não embutido, você tem que inventá-la ou obtê-lo de outros módulos), e por sua vez a sua expressividade tempo de execução é impedida. Chamando-o de "abençoar" não fazê-lo muito favores nem.

O que nós queremos fazer :

Algo como isso, mas têm ligação ao mapa protótipo recursiva, e ser implicitamente ligado ao protótipo em vez de ter de fazê-lo explicitamente.

Aqui está uma tentativa ingênua para ele: o problema é que "chamada de" fazeres não saber "o que chamou de", por isso pode muito bem ser uma função perl universal "objectInvokeMethod (objeto, método)", que verifica se o objeto tem o método, ou o seu protótipo tem, ou o seu protótipo tem, até que chegar ao fim e encontra-se ou não (inheritence protótipo). Perl tem ótima magia eval para fazê-lo, mas vou deixar isso para algo que eu posso tentar fazer mais tarde.

De qualquer forma aqui é a idéia:

(sub {

    my $Animal = (sub {
        my $AnimalPrototype = {
            'performSound' => sub {
                return $_[0]->{'sound'};
            }
        };

        my $call = sub {
            my $this = $_[0];
            my $proc = $_[1];

            if (exists $this->{$proc}) {
                return $this->{$proc}->();
            } else {
                return $this->{prototype}->{$proc}->($this, $proc);
            }
        };

        return sub {
            my $name = $_[0]->{name};
            my $sound = $_[0]->{sound};

            my $this = { 
                'this' => $this,
                'name' => $name,
                'sound' => $sound,
                'prototype' => $AnimalPrototype,
                'call' => $call                
            };
        };
    })->();

    my $animal = $Animal->({
        'name' => 'Jeff',
        'sound'=> 'bark'
    });
    print($animal->{call}($animal, 'performSound'));
})->();

De qualquer forma espero que alguém vai encontrar este post útil.

I Seguindo esse pensamento para orientar o desenvolvimento orientado a objeto Perl.

Bless associar qualquer referência estrutura de dados com uma classe. Dado como Perl cria a estrutura de herança (em uma espécie de árvore) é fácil de tirar proveito do modelo de objeto para criar objetos de composição.

Para esta associação chamamos objeto, para desenvolver sempre em mente que o estado interno dos comportamentos de objectos e de classe são separados. E você pode abençoar / permitir que qualquer referência de dados para usar quaisquer comportamentos pacote / classe. Uma vez que o pacote pode compreender "o emocional" estado do objeto.

Por exemplo, se você pode ter certeza que qualquer objeto Bug vai ser um hash abençoada, você pode (finalmente!) Preencha o código faltando no método Bug :: print_me:

 package Bug;
 sub print_me
 {
     my ($self) = @_;
     print "ID: $self->{id}\n";
     print "$self->{descr}\n";
     print "(Note: problem is fatal)\n" if $self->{type} eq "fatal";
 }

Agora, sempre que o método print_me é chamado através de uma referência a qualquer hash que tem sido abençoado para a classe Bug, a variável $ self extrai a referência que foi passado como o primeiro argumento e, em seguida, as instruções de impressão aceder às várias entradas da de hash abençoado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top