वास्तव में पर्ल का "आशीर्वाद" क्या करता है?


142

मैं समझता हूं कि एक वर्ग के "नए" तरीके से पर्ल में "आशीर्वाद" कीवर्ड का उपयोग होता है:

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

लेकिन वास्तव में उस हैश संदर्भ के लिए "आशीर्वाद" क्या है?


2
1999 से "आशीर्वाद माय रेफरेंट्स" देखें । यह बहुत विस्तृत लगता है। ( पर्ल मैनुअल प्रविष्टि में इस पर कहने के लिए बहुत कुछ नहीं है, दुर्भाग्य से।)
जॉन स्कीट

जवाबों:


143

सामान्य तौर पर, blessएक वस्तु को एक वर्ग के साथ जोड़ा जाता है।

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

अब जब आप किसी विधि को लागू करते हैं $object, तो पर्ल को पता होता है कि किस विधि की खोज करनी है।

यदि दूसरा तर्क छोड़ दिया जाता है, जैसा कि आपके उदाहरण में, वर्तमान पैकेज / वर्ग का उपयोग किया जाता है।

स्पष्टता के लिए, आपका उदाहरण इस प्रकार लिखा जा सकता है:

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

संपादित करें: थोड़ा और विस्तार के लिए किक्सक्स का अच्छा जवाब देखें ।


79

bless एक पैकेज के साथ एक संदर्भ जोड़ता है।

इससे कोई फर्क नहीं पड़ता है कि संदर्भ क्या है, यह एक हैश (सबसे सामान्य मामला), एक सरणी (इतना सामान्य नहीं), एक स्केलर (आमतौर पर यह अंदर-बाहर वस्तु को इंगित करता है ) के लिए, एक नियमित अभिव्यक्ति के लिए हो सकता है , सबरूटीन या TYPEGLOB (पुस्तक ऑब्जेक्ट ओरिएंटेड पर्ल: ए कॉम्प्रिहेंसिव गाइड टू कॉन्सेप्ट्स एंड प्रोग्रामिंग टेक्निक्स बाय डेमियन कॉनवे उपयोगी उदाहरणों के लिए) या यहां तक ​​कि एक फाइल या डायरेक्टरी हैंडल (कम से कम सामान्य केस) का संदर्भ भी।

प्रभाव bless यह है कि यह आपको विशेष संदर्भ में विशेष वाक्यविन्यास लागू करने की अनुमति देता है।

उदाहरण के लिए, यदि एक धन्य संदर्भ $obj( blessपैकेज "क्लास" के साथ जुड़ा हुआ है) में संग्रहीत किया जाता है , तो $obj->foo(@args)एक सबरूटीन को कॉल करेगा fooऔर पहले तर्क के रूप में पारित करेगा $objजो बाकी के तर्कों ( @args) के बाद संदर्भ को संदर्भित करता है । उप-वर्ग को "क्लास" पैकेज में परिभाषित किया जाना चाहिए। यदि fooपैकेज "क्लास" में कोई सबरूटीन नहीं है , तो अन्य पैकेजों की एक सूची ( @ISAपैकेज "क्लास" में सरणी के रूप में ली गई ) की खोज की जाएगी और fooपाया गया पहला सबरूटीन कहा जाएगा।


16
आपका प्रारंभिक कथन गलत है। हां, आशीर्वाद एक संदर्भ को अपने पहले तर्क के रूप में लेता है, लेकिन यह संदर्भित चर है जो धन्य है, केवल संदर्भ नहीं। $ perl -le 'sub Somepackage :: फू {42}; % h = (); $ ज = \% ज; आशीर्वाद $ h, "Somepackage"; $ j = \% h; $ j-> UNIVERSAL :: कर सकते हैं ("foo") -> () '42
कनवर्टर

1
किक्सक्स की व्याख्या व्यापक है। हमें सैद्धांतिक minutiae पर कनवर्टर के चयन से परेशान नहीं होना चाहिए।
धन्य गीक

19
@ बेक्ड गीक, यह सैद्धांतिक minutiae नहीं है। अंतर व्यावहारिक अनुप्रयोग है।
इकेगामी

3
"अंदर-बाहर वस्तु" के लिए पुराना perlfoundation.org लिंक सबसे अच्छा है, अब एक लॉगिन दीवार के पीछे है। मूल का लिंक। लिंक यहां है
रफिन

2
शायद यह टूट लिंक @harmic के स्थान पर काम करेगा पर टिप्पणी की: perldoc.perl.org/perlobj.html#Inside-Out-objects
Rhubbarb

9

लघु संस्करण: यह उस हैश को चिह्नित कर रहा है जैसा कि वर्तमान पैकेज नेमस्पेस से जुड़ा हुआ है (ताकि पैकेज अपना वर्ग कार्यान्वयन प्रदान करता है)।


7

यह फ़ंक्शन REF द्वारा संदर्भित इकाई को बताता है कि यह अब CLASSNAME पैकेज में एक वस्तु है, या यदि CLASSNAME छोड़ा गया है तो वर्तमान पैकेज। आशीर्वाद के दो-तर्क रूप का उपयोग करने की सिफारिश की जाती है।

उदाहरण :

bless REF, CLASSNAME
bless REF

प्रतिलाभ की मात्रा

यह फ़ंक्शन CLASSNAME में दिए गए ऑब्जेक्ट के संदर्भ को वापस लौटाता है।

उदाहरण :

इसके मूल उपयोग को दर्शाने वाला उदाहरण कोड निम्नलिखित है, ऑब्जेक्ट का संदर्भ पैकेज के वर्ग के संदर्भ को आशीर्वाद देकर बनाया जाता है -

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

4

मैं यहाँ एक उत्तर प्रदान करूँगा क्योंकि यहाँ के लोग मेरे लिए बहुत क्लिक नहीं करते थे।

पर्ल का आशीर्वाद समारोह पैकेज के अंदर सभी कार्यों के लिए किसी भी संदर्भ को जोड़ता है।

हमें इसकी आवश्यकता क्यों होगी?

आइए जावास्क्रिप्ट में एक उदाहरण व्यक्त करके शुरू करते हैं:

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

अब कक्षा निर्माण को हटा दें और उसके बिना करें:

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

फ़ंक्शन अनऑर्डर किए गए गुणों की एक हैश तालिका लेता है (क्योंकि 2016 में गतिशील भाषाओं में एक विशिष्ट क्रम में गुण लिखने के लिए कोई मतलब नहीं है) और उन गुणों के साथ एक हैश तालिका देता है, या यदि आप नया कीवर्ड डालना भूल गए हैं, तो यह पूरे वैश्विक संदर्भ (उदाहरण के लिए ब्राउज़र में विंडो या नोडज में वैश्विक) वापस कर देगा।

पर्ल के पास कोई "यह" या "नया" नहीं है और न ही "क्लास" है, लेकिन इसमें अभी भी एक फ़ंक्शन हो सकता है जो समान व्यवहार करता है। हमारे पास न तो कोई निर्माता होगा और न ही कोई प्रोटोटाइप, लेकिन हम अपनी इच्छा के अनुसार नए जानवरों को बनाने और उनके व्यक्तिगत गुणों को संशोधित करने में सक्षम होंगे।

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

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

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

अब, हमारे पास एक समस्या है: क्या होगा अगर हम चाहते हैं कि जानवर हमारे बजाय खुद से ध्वनियों का प्रदर्शन करें, जो कि उनकी आवाज है। यही है, हम एक कार्य प्रदर्शन चाहते हैं जो जानवर की अपनी आवाज़ को प्रिंट करता है।

ऐसा करने का एक तरीका यह है कि प्रत्येक व्यक्तिगत पशु को यह सिखाया जाए कि यह कैसे ध्वनि है। इसका मतलब है कि प्रत्येक बिल्ली का प्रदर्शन करने के लिए अपना स्वयं का डुप्लिकेट फ़ंक्शन है।

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

यह बुरा है क्योंकि हर बार एक जानवर के निर्माण के दौरान PerformSound को पूरी तरह से नए फ़ंक्शन ऑब्जेक्ट के रूप में रखा जाता है। 10000 जानवरों का मतलब होता है 10000 प्रदर्शन। हम चाहते हैं कि एक एकल फंक्शन परफॉर्म किया जाए जो सभी जानवरों द्वारा उपयोग किया जाता है जो अपनी खुद की आवाज़ को देखता है और इसे प्रिंट करता है।

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

यहाँ वह जगह है जहाँ पर्ल थोरा के समानांतर रुकता है।

जावास्क्रिप्ट का नया ऑपरेटर वैकल्पिक नहीं है, इसके बिना, "यह" ऑब्जेक्ट विधियों के अंदर वैश्विक गुंजाइश को दूषित करता है:

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

})();

हम प्रत्येक पशु के लिए एक कार्य करना चाहते हैं जो कि उस जानवर की स्वयं की आवाज़ को देखता है जो निर्माण के दौरान इसे हार्डकोड करने के बजाय करता है।

आशीर्वाद हमें वस्तुओं के प्रोटोटाइप के रूप में एक पैकेज का उपयोग करने देता है। इस तरह, वस्तु "पैकेज" के बारे में पता है, यह "संदर्भित" है, और बदले में पैकेज में कार्य हो सकते हैं "पहुंच" उस विशिष्ट पैकेज के निर्माण से बनाए गए विशिष्ट उदाहरण हैं:

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

सारांश / टीएल; डीआर :

पर्ल का कोई "यह", "वर्ग" नहीं है, न ही "नया"। किसी पैकेज के लिए किसी वस्तु को आशीर्वाद देना उस वस्तु को पैकेज का संदर्भ देता है, और जब यह पैकेज में फ़ंक्शन को कॉल करता है, तो उनके तर्क 1 स्लॉट द्वारा ऑफसेट किए जाएंगे, और पहला तर्क ($ _ [0] या शिफ्ट) समकक्ष होगा। जावास्क्रिप्ट का "यह"। बदले में, आप कुछ हद तक जावास्क्रिप्ट के प्रोटोटाइप मॉडल का अनुकरण कर सकते हैं।

दुर्भाग्य से यह असंभव है (मेरी समझ में) रनटाइम पर "नई कक्षाएं" बनाने के लिए, जैसा कि आपको प्रत्येक "वर्ग" की आवश्यकता है कि उसका अपना पैकेज हो, जबकि जावास्क्रिप्ट में, आपको "नए" कीवर्ड के रूप में पैकेजों की आवश्यकता नहीं है रन-वे पर एक पैकेज के रूप में उपयोग करने के लिए एक अनाम हैशमैप बनाता है जिसमें आप नए फ़ंक्शंस जोड़ सकते हैं और फ़्लाई पर फ़ंक्शंस निकाल सकते हैं।

कुछ सीमाएँ लाइब्रेरी हैं जो अभिव्यक्ति में इस सीमा को भंग करने के अपने तरीके बनाते हैं, जैसे कि मूस।

भ्रम क्यों?:

पैकेज की वजह से। हमारा अंतर्ज्ञान हमें वस्तु को उसके 'प्रोटोटाइप' वाले हैशमैप से बांधने को कहता है। इससे हम जावास्क्रिप्ट जैसे रनटाइम पर "पैकेज" बना सकते हैं। पर्ल में ऐसा लचीलापन नहीं है (कम से कम में निर्मित नहीं है, आपको इसे आविष्कार करना होगा या इसे अन्य मॉड्यूल से प्राप्त करना होगा), और बदले में आपके रनटाइम की अभिव्यक्ति में बाधा होती है। इसे "आशीर्वाद" कहकर न तो यह बहुत एहसान करता है।

हम क्या करना चाहते हैं :

ऐसा कुछ है, लेकिन प्रोटोटाइप मैप के पुनरावर्ती के लिए बाध्यकारी है, और स्पष्ट रूप से ऐसा करने के बजाय प्रोटोटाइप से बाध्य होना चाहिए।

यहाँ इस पर एक भोली कोशिश है: मुद्दा यह है कि "कॉल" को "इसे क्या कहा जाता है" नहीं पता है, इसलिए यह एक सार्वभौमिक पर्ल फ़ंक्शन हो सकता है "objectInvokeMethod (ऑब्जेक्ट, विधि)" जो जाँचता है कि ऑब्जेक्ट की विधि है या नहीं , या इसके प्रोटोटाइप में यह है, या इसके प्रोटोटाइप में यह है, जब तक कि यह अंत तक नहीं पहुंचता और इसे पाता है या नहीं (प्रोटोटाइप इनहेरिटेंस)। पर्ल के पास इसे करने के लिए अच्छा स्पष्ट जादू है, लेकिन मैं बाद में कुछ करने की कोशिश कर सकता हूं।

वैसे भी यहाँ विचार है:

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

वैसे भी उम्मीद है कि किसी को यह पोस्ट उपयोगी लगेगी।


रनटाइम के दौरान नई कक्षाएं बनाना असंभव नहीं है। कक्षा my $o = bless {}, $anything;में किसी वस्तु को आशीर्वाद देगा $anything। इसी तरह, {no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++};नाम में वर्ग में 'सोमसब' नामक एक विधि का निर्माण करेगा $anything। यह सब रनटाइम पर संभव है। "संभावित", हालांकि, यह हर दिन के कोड में फिराना करने के लिए एक महान अभ्यास नहीं करता है। लेकिन यह मूस या मू जैसे ऑब्जेक्ट ओवरले सिस्टम के निर्माण में उपयोगी है।
21

दिलचस्प है, इसलिए आप कह रहे हैं कि मैं एक क्लास में एक रेफरेंट को आशीर्वाद दे सकता हूं, जिसका नाम रनटाइम में तय किया गया है। दिलचस्प लगता है, और मेरे शून्य करता हैunfortunately it makes it impossible(to my understanding) to create "new classes" at runtime दावे । मुझे लगता है कि मेरी चिंता अंततः उबली हुई है, जो रनटाइम के दौरान पैकेज सिस्टम में हेरफेर / आत्मनिरीक्षण करने के लिए काफी कम सहज है, लेकिन अभी तक मैं कुछ भी ऐसा दिखाने में विफल रहा है जो इसे स्वाभाविक रूप से नहीं कर सकता। पैकेज सिस्टम रनटाइम पर स्वयं को जोड़ने / हटाने / निरीक्षण / संशोधित करने के लिए आवश्यक सभी उपकरणों का समर्थन करता है।
दिमित्री

यह सही है; आप पर्ल के प्रतीक तालिका को प्रोग्रामेटिक रूप से हेरफेर कर सकते हैं, और इसलिए पर्ल के पैकेजों में हेरफेर कर सकते हैं, और रनटाइम पर एक पैकेज के सदस्यों, यहां तक ​​कि कहीं भी "पैकेज फू" घोषित किए बिना। रन टाइम पर सिंबल टेबल इंस्पेक्शन और मैनिपुलेशन, AUTOLOAD सिमेंटिक्स, सबरूटीन एट्रिब्यूट्स, वेरिएबल्स टू क्लासेस ... हूड के नीचे आने के कई तरीके हैं। उनमें से कुछ ऑटोगेनरिंग एपीआई, सत्यापन उपकरण, ऑटोडेक्जिनेशन एपीआई के लिए उपयोगी हैं; हम सभी उपयोग मामलों की भविष्यवाणी नहीं कर सकते। अपने आप को पैर में गोली मारना भी इस तरह की चालबाजी का एक संभावित परिणाम है।
डेविड नोव

4

कई अच्छे उत्तरों के साथ, जो विशेष रूप से एक- blessएड रेफरेंस को अलग करता है, वह SV यह है कि इसके लिए एक अतिरिक्त FLAGS( OBJECT) औरSTASH

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'

प्रिंट, एक ही (और इस के लिए अप्रासंगिक) भागों के साथ दबा दिया

एसवी = IV (0x12d5530) 0x12d5540 पर
  REFCNT = 1
  ध्वज = (आरओके)
  आरवी = 0x12a5a68
  SV = PVHV (0x12ab980) 0x12a5a68 पर
    REFCNT = 1
    FLAGS = (SHAREKEYS)
    ...
      एसवी = IV (0x12a5ce0) 0x12a5cf0 पर
      REFCNT = 1
      फ्लैग = (आईओके, पीआईओके)
      IV = 1
---
SV = IV (0x12cb8b8) 0x12cb8c8 पर
  REFCNT = 1
  फ्लैग्स = (पैडमैन, आरओके)
  आरवी = 0x12c26b0
  SV = PVHV (0x12aba00) 0x12c26b0 पर
    REFCNT = 1
    फ्लैग्स = (OBJECT, SHAREKEYS)
    STASH = 0x12d5300 "क्लास"
    ...
      एसवी = IV (0x12c26b8) 0x12c26c8 पर
      REFCNT = 1
      फ्लैग = (आईओके, पीआईओके)
      IV = 10

इसके साथ यह ज्ञात है कि 1) यह एक वस्तु 2 है) यह किस पैकेज का है, और यह इसके उपयोग की सूचना देता है।

उदाहरण के लिए, जब उस चर पर dereferencing का सामना किया जाता है ( $obj->name), उस नाम के साथ एक उप पैकेज (या पदानुक्रम) में मांगा जाता है, ऑब्जेक्ट को पहले तर्क के रूप में पारित किया जाता है, आदि।


1

मैंने इसके बाद विकास-उन्मुख पर्ल के मार्गदर्शन के लिए सोचा।

आशीर्वाद किसी भी डेटा संरचना संदर्भ को एक वर्ग के साथ जोड़ देता है। यह देखते हुए कि कैसे पर्ल वंशानुक्रम संरचना (एक प्रकार के वृक्ष में) बनाता है, वस्तु के मॉडल का लाभ उठाना आसान है ताकि रचना के लिए ऑब्जेक्ट बनाया जा सके।

इस एसोसिएशन के लिए हमने ऑब्जेक्ट को बुलाया, हमेशा विकसित करने के लिए यह ध्यान में रखना है कि ऑब्जेक्ट की आंतरिक स्थिति और वर्ग व्यवहार अलग हो जाते हैं। और आप किसी भी डेटा संदर्भ को किसी भी पैकेज / वर्ग व्यवहार का उपयोग करने के लिए आशीर्वाद / अनुमति दे सकते हैं। चूंकि पैकेज वस्तु की "भावनात्मक" स्थिति को समझ सकता है।


यहां समान घोषणाएं हैं कि कैसे पर्ल संकुल के नाम स्थान के साथ काम करता है और आपके नाम स्थान में पंजीकृत राज्यों के साथ कैसे काम करता है। क्योंकि यह प्रचलित नाम जैसे नामस्थान :: स्वच्छ है। लेकिन चीजों को सरल रखने की कोशिश करें।
स्टीवन कोच

-9

उदाहरण के लिए, यदि आप आश्वस्त हो सकते हैं कि कोई भी बग ऑब्जेक्ट एक धन्य हैश होने वाला है, तो आप बग में गुम कोड में प्रिंट कर सकते हैं :: 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";
 }

अब, जब भी प्रिंट_मे विधि को किसी भी हैश के संदर्भ में कहा जाता है जिसे बग वर्ग में आशीर्वाद दिया गया है, $ आत्म चर संदर्भ को संदर्भित करता है जिसे पहले तर्क के रूप में पारित किया गया था और फिर प्रिंट स्टेटमेंट धन्य हैश की विभिन्न प्रविष्टियों तक पहुंचते हैं।


@darch इस उत्तर को किस स्रोत से ख़त्म किया गया?
एंडरसन ग्रीन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.