Apa sebenarnya yang dilakukan "berkah" Perl?

142

Saya mengerti seseorang menggunakan kata kunci "berkah" di Perl di dalam metode "baru" kelas:

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

Tapi apa sebenarnya yang "diberkati" lakukan untuk referensi hash?

pengguna47145
sumber
2
Lihat "Bless My Referents" sejak tahun 1999. Terlihat cukup detail. ( Entri manual Perl tidak banyak berbicara tentang itu, sayangnya.)
Jon Skeet

Jawaban:

143

Secara umum, blessmengaitkan objek dengan kelas.

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

Sekarang ketika Anda memanggil metode aktif $object, Perl tahu paket mana yang harus dicari.

Jika argumen kedua dihilangkan, seperti pada contoh Anda, paket / kelas saat ini digunakan.

Demi kejelasan, contoh Anda dapat ditulis sebagai berikut:

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

EDIT: Lihat kixx 's baik jawaban untuk sedikit lebih rinci.

Gordon Wilson
sumber
79

bless menghubungkan referensi dengan suatu paket.

Tidak peduli apa rujukannya, bisa berupa hash (kasus paling umum), ke array (tidak begitu umum), ke skalar (biasanya ini menunjukkan objek luar-dalam ), ke ekspresi reguler , subroutine atau TYPEGLOB (lihat buku Object Oriented Perl: Sebuah Panduan Komprehensif untuk Konsep dan Teknik Pemrograman oleh Damian Conway untuk contoh yang berguna) atau bahkan referensi ke file atau direktori handle (kasus paling umum).

Efek yang blessdimiliki adalah memungkinkan Anda untuk menerapkan sintaks khusus untuk referensi yang diberkati.

Misalnya, jika referensi yang diberkati disimpan di $obj(terkait blessdengan paket "Kelas"), maka $obj->foo(@args)akan memanggil subrutin foodan lulus sebagai argumen pertama referensi $objdiikuti oleh sisa argumen ( @args). Subrutin harus didefinisikan dalam paket "Kelas". Jika tidak ada subrutin foodalam paket "Kelas", daftar paket lain (diambil dari larik @ISAdalam paket "Kelas") akan dicari dan subrutin pertama yang fooditemukan akan dipanggil.

kixx
sumber
16
Pernyataan awal Anda salah. Ya, bless mengambil referensi sebagai argumen pertama, tetapi itu adalah variabel referensi yang diberkati, bukan referensi itu sendiri. $ perl -le 'sub Somepackage :: foo {42}; % h = (); $ h = \% h; memberkati $ h, "Somepackage"; $ j = \% h; print $ j-> UNIVERSAL :: can ("foo") -> () '42
converter42
1
Penjelasan Kixx komprehensif. Kita seharusnya tidak repot-repot dengan memilih konverter pada hal-hal kecil teoritis.
Blessed Geek
19
@Blessed Geek, Ini bukan hal-hal kecil yang teoretis. Perbedaannya memiliki aplikasi praktis.
ikegami
3
Tautan lama perlfoundation.org untuk "objek luar-dalam", paling banter, di balik dinding masuk sekarang. Tautan asli Archive.org ada di sini .
ruffin
2
Mungkin ini akan melayani di tempat link @harmic rusak mengomentari: perldoc.perl.org/perlobj.html#Inside-Out-objects
Rhubbarb
9

Versi singkat: ini menandai hash yang melekat pada paket namespace saat ini (sehingga paket itu menyediakan implementasi kelasnya).

kekacauan
sumber
7

Fungsi ini memberi tahu entitas yang dirujuk oleh REF bahwa itu sekarang menjadi objek dalam paket CLASSNAME, atau paket saat ini jika CLASSNAME dihilangkan. Dianjurkan untuk menggunakan dua argumen bentuk berkat.

Contoh :

bless REF, CLASSNAME
bless REF

Nilai Pengembalian

Fungsi ini mengembalikan referensi ke objek yang diberkati menjadi CLASSNAME.

Contoh :

Berikut ini adalah contoh kode yang menunjukkan penggunaan dasarnya, referensi objek dibuat dengan memberkati referensi ke kelas paket -

#!/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;
}
linuxtestside
sumber
4

Saya akan memberikan jawaban di sini karena yang di sini tidak cukup klik untuk saya.

Fungsi memberkati Perl menghubungkan referensi apa pun ke semua fungsi di dalam sebuah paket.

Mengapa kita membutuhkan ini?

Mari kita mulai dengan mengungkapkan contoh dalam 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
})();

Sekarang mari kita hapus konstruk kelas dan lakukan tanpa itu:

(() => {
    '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);    
})();

Fungsi ini mengambil tabel hash dari properti tidak berurutan (karena tidak masuk akal harus menulis properti dalam urutan tertentu dalam bahasa dinamis di 2016) dan mengembalikan tabel hash dengan properti itu, atau jika Anda lupa memasukkan kata kunci baru, itu akan mengembalikan seluruh konteks global (mis. jendela di browser atau global dalam nodejs).

Perl tidak memiliki "ini" atau "baru" atau "kelas", tetapi masih dapat memiliki fungsi yang berperilaku sama. Kami tidak akan memiliki konstruktor atau prototipe, tetapi kami akan dapat membuat hewan baru sesuka hati dan memodifikasi properti masing-masing.

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

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

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

Sekarang, kita memiliki masalah: Bagaimana jika kita ingin hewan itu melakukan suara sendiri alih-alih kita mencetak suara mereka. Artinya, kami ingin fungsi performSound yang mencetak suara binatang itu sendiri.

Salah satu cara untuk melakukan ini adalah dengan mengajar masing-masing Hewan bagaimana melakukannya dengan suara. Ini berarti bahwa masing-masing Cat memiliki fungsi duplikat sendiri untuk melakukan Suara.

# 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'}();
})->();

Ini buruk karena performSound diletakkan sebagai objek fungsi yang sama sekali baru setiap kali seekor hewan dikonstruksi. 10.000 hewan berarti 10.000 performSounds. Kami ingin memiliki satu fungsi performSound yang digunakan oleh semua hewan yang mencari suara mereka sendiri dan mencetaknya.

(() => {
    '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
})();

Di sinilah paralel ke Perl agak berhenti.

Operator baru JavaScript tidak opsional, tanpa itu, "ini" di dalam metode objek merusak lingkup global:

(() => {
    // '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.

})();

Kami ingin memiliki satu fungsi untuk masing-masing Hewan yang mencari suara binatang itu sendiri daripada mengkodekannya saat konstruksi.

Berkat memungkinkan kita menggunakan paket sebagai prototipe objek. Dengan cara ini, objek menyadari "paket" itu "dirujuk", dan pada gilirannya dapat memiliki fungsi dalam paket "menjangkau" contoh spesifik yang dibuat dari konstruktor "objek paket" itu:

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();

Ringkasan / TL; DR :

Perl tidak memiliki "ini", "kelas", atau "baru". memberkati objek ke suatu paket memberikan objek itu referensi ke paket, dan ketika ia memanggil fungsi dalam paket, argumen mereka akan diimbangi dengan 1 slot, dan argumen pertama ($ _ [0] atau shift) akan setara dengan javascript "ini". Pada gilirannya, Anda dapat mensimulasikan model prototipe JavaScript.

Sayangnya itu tidak memungkinkan (untuk pengertian saya) untuk membuat "kelas baru" saat runtime, karena Anda membutuhkan setiap "kelas" untuk memiliki paket sendiri, sedangkan di javascript, Anda tidak perlu paket sama sekali, sebagai kata kunci "baru" membuat hashmap anonim untuk Anda gunakan sebagai paket saat runtime yang Anda dapat menambahkan fungsi baru dan menghapus fungsi dengan cepat.

Ada beberapa perpustakaan Perl menciptakan cara mereka sendiri untuk menjembatani keterbatasan ini dalam ekspresi, seperti Moose.

Kenapa kebingungan? :

Karena paket. Intuisi kita memberitahu kita untuk mengikat objek ke sebuah hashmap yang berisi prototipe. Ini memungkinkan kami membuat "paket" pada saat runtime seperti yang dilakukan JavaScript. Perl tidak memiliki fleksibilitas seperti itu (setidaknya tidak built-in, Anda harus menciptakannya atau mendapatkannya dari modul lain), dan pada gilirannya runtime ekspresivitas Anda terhambat. Menyebutnya "berkah" juga tidak banyak membantu.

Apa yang ingin kita lakukan :

Sesuatu seperti ini, tetapi memiliki ikatan ke prototipe peta rekursif, dan secara implisit terikat pada prototipe daripada harus secara eksplisit melakukannya.

Berikut ini adalah upaya naif untuk itu: masalahnya adalah bahwa "panggilan" tidak tahu "apa yang disebutnya", sehingga mungkin juga menjadi fungsi perl universal "objectInvokeMethod (objek, metode)" yang memeriksa apakah objek memiliki metode , atau prototipe memilikinya, atau prototipe memilikinya, hingga mencapai akhir dan menemukannya atau tidak (pewarisan prototipe). Perl memiliki sihir eval yang bagus untuk melakukannya, tetapi saya akan membiarkannya untuk sesuatu yang bisa saya coba nanti.

Bagaimanapun, inilah idenya:

(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'));
})->();

Pokoknya mudah-mudahan seseorang akan menemukan posting ini bermanfaat.

Dmitry
sumber
Bukan tidak mungkin untuk membuat kelas baru saat runtime. my $o = bless {}, $anything;akan memberkati objek ke dalam $anythingkelas. Demikian pula, {no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++};akan membuat metode bernama 'somesub' di kelas yang bernama in $anything. Ini semua dimungkinkan saat runtime. "Kemungkinan", bagaimanapun, tidak membuatnya menjadi praktik yang hebat untuk menggunakan kode setiap hari. Tetapi ini berguna dalam membangun sistem overlay objek seperti Moose atau Moo.
DavidO
menarik, jadi Anda mengatakan saya bisa memberkati referensi ke kelas yang namanya diputuskan saat runtime. Tampaknya menarik, dan membatalkan unfortunately it makes it impossible(to my understanding) to create "new classes" at runtimeklaim saya . Saya kira kekhawatiran saya akhirnya menjadi kurang intuitif untuk memanipulasi / mengintrospeksi sistem paket pada saat runtime, tetapi sejauh ini saya telah gagal menunjukkan sesuatu yang secara inheren tidak dapat dilakukan. Sistem paket tampaknya mendukung semua alat yang diperlukan untuk menambah / menghapus / memeriksa / memodifikasi sendiri saat runtime.
Dmitry
Ini benar; Anda dapat memanipulasi tabel simbol Perl secara sistematis, dan karenanya dapat memanipulasi paket Perl, dan anggota paket saat runtime, bahkan tanpa mendeklarasikan "paket Foo" di mana saja. Pemeriksaan dan manipulasi tabel simbol pada saat runtime, semantik AUTOLOAD, atribut subrutin, pengikatan variabel ke kelas ... ada banyak cara untuk masuk ke dalam sungkup. Beberapa di antaranya berguna untuk autogenerating API, alat validasi, autodocumenting APIs; kami tidak dapat memprediksi semua kasus penggunaan. Menembak diri sendiri di kaki juga merupakan kemungkinan hasil dari tipu daya semacam itu.
DavidO
4

Seiring dengan sejumlah jawaban yang baik, apa yang secara khusus membedakan blessreferensi -ed adalah bahwa SV untuk mengambil tambahan FLAGS( OBJECT) danSTASH

perl -MDevel::Peek -wE'
    package Pack  { sub func { return { a=>1 } } }; 
    package Class { sub new  { return bless { A=>10 } } }; 
    $vp  = Pack::func(); print Dump $vp;   say"---"; 
    $obj = Class->new;   print Dump $obj'

Cetakan, dengan bagian yang sama (dan tidak relevan untuk ini) ditekan

SV = IV (0x12d5530) pada 0x12d5540
  REFCNT = 1
  BENDERA = (ROK)
  RV = 0x12a5a68
  SV = PVHV (0x12ab980) pada 0x12a5a68
    REFCNT = 1
    BENDERA = (SHAREKEYS)
    ...
      SV = IV (0x12a5ce0) pada 0x12a5cf0
      REFCNT = 1
      BENDERA = (IOK, pIOK)
      IV = 1
---
SV = IV (0x12cb8b8) pada 0x12cb8c8
  REFCNT = 1
  BENDERA = (PADMY, ROK)
  RV = 0x12c26b0
  SV = PVHV (0x12aba00) pada 0x12c26b0
    REFCNT = 1
    BENDERA = (OBYEK, SHAREKEYS)
    STASH = 0x12d5300 "Kelas"
    ...
      SV = IV (0x12c26b8) pada 0x12c26c8
      REFCNT = 1
      BENDERA = (IOK, pIOK)
      IV = 10

Dengan itu diketahui bahwa 1) itu adalah objek 2) paket miliknya, dan ini menginformasikan penggunaannya.

Sebagai contoh, ketika dereferencing pada variabel itu ditemui ( $obj->name), sub dengan nama itu dicari dalam paket (atau hierarki), objek dilewatkan sebagai argumen pertama, dll.

zdim
sumber
1

Saya mengikuti pemikiran ini untuk memandu pengembangan berorientasi objek Perl.

Bless kaitkan referensi struktur data apa pun dengan kelas. Mengingat bagaimana Perl menciptakan struktur warisan (dalam semacam pohon), mudah untuk mengambil keuntungan dari model objek untuk membuat Objek untuk komposisi.

Untuk asosiasi ini kita sebut objek, untuk mengembangkan selalu ada dalam pikiran bahwa keadaan internal objek dan perilaku kelas dipisahkan. Dan Anda dapat memberkati / mengizinkan referensi data untuk menggunakan perilaku paket / kelas apa pun. Karena paket dapat memahami keadaan "emosional" objek.

Steven Koch
sumber
Berikut ini pengumuman yang sama dengan cara Perl bekerja dengan ruang nama paket dan cara kerja dengan status yang terdaftar di namespace Anda. Karena ada pragma seperti use namespace :: clean. Tetapi cobalah untuk menjaga hal-hal sesederhana mungkin.
Steven Koch
-9

Misalnya, jika Anda dapat yakin bahwa objek Bug apa pun akan menjadi hash yang diberkati, Anda dapat (akhirnya!) Mengisi kode yang hilang dalam metode 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";
 }

Sekarang, setiap kali metode print_me dipanggil melalui referensi ke hash apa pun yang telah diberkati ke dalam kelas Bug, variabel $ self mengekstrak referensi yang diteruskan sebagai argumen pertama dan kemudian pernyataan cetak mengakses berbagai entri hash yang diberkati.

Bimlesh Sharma
sumber
@ raja Dari sumber mana jawaban ini dijiplak?
Anderson Green