2012-11-29 31 views
6

क्या Proc.new या Kernel.proc का उपयोग कर तत्काल प्रो की सख्त धैर्य प्रवर्तन "चालू" करने का कोई तरीका है, ताकि यह lambda के साथ तत्काल प्रो की तरह व्यवहार करे?क्या मैं किसी विधि को पारित ब्लॉक पर धर्मार्थ लागू कर सकता हूं?

मेरा initialize विधि ब्लॉक &action लेता है और इसे एक आवृत्ति चर में निर्दिष्ट करता है। मैं धैर्यपूर्वक कड़ाई से लागू करने के लिए action चाहता हूं, इसलिए जब मैं बाद में तर्क लागू करता हूं, तो यह ArgumentError उठाता है कि मैं एक और अधिक सार्थक अपवाद को बचा सकता हूं और बढ़ा सकता हूं। मूल रूप से:

class Command 
    attr_reader :name, :action 

    def initialize(name, &action) 
    @name = name 
    @action = action 
    end 

    def perform(*args) 
    begin 
     action.call(*args) 
    rescue ArgumentError 
     raise(WrongArity.new(args.size)) 
    end 
    end 
end 

class WrongArity < StandardError; end 

दुर्भाग्य से, action arity डिफ़ॉल्ट रूप से लागू नहीं करता है:

c = Command.new('second_argument') { |_, y| y } 
c.perform(1) # => nil 

action.to_proc काम नहीं करता है, और न ही lambda(&action) करता है।

कोई अन्य विचार? या समस्या के लिए बेहतर दृष्टिकोण?

धन्यवाद!

+0

आप सब पर 'और action' उपयोग करने की आवश्यकता नहीं है:

तो तुम कुछ इस तरह कर सकता है। बस एक्शन पैरामीटर और उस लाइन पर जहां आप इंस्टेंस वैरिएबल सेट करते हैं, 'lambda' के साथ' action' को प्रतिस्थापित करें। मैंने नीचे एक कोड नमूना पोस्ट किया। – wrhall

उत्तर

8

आपका @action हो जाएगा एक Proc उदाहरण और Proc रों ताकि आप जाँच कर सकते हैं कि कितने तर्क ब्लॉक माना जाता है एक arity विधि है:

def perform(*args) 
    if args.size != @action.arity 
    raise WrongArity.new(args.size) 
    end 
    @action.call(*args) 
end 

कि splatless ब्लॉकों का ख्याल { |a| ... } और { |a,b| ... } की तरह लेना चाहिए लेकिन चीजें splats के साथ थोड़ा और जटिल हैं। यदि आपके पास { |*a| ... } जैसे ब्लॉक हैं तो @action.arity -1 और { |a,*b| ... } आपको arity -2 प्रदान करेगा। Arity -1 वाला एक ब्लॉक किसी भी तर्क (किसी भी सहित) नहीं ले सकता है, arity -2 वाले ब्लॉक को कम से कम एक तर्क की आवश्यकता होती है लेकिन इससे अधिक ले सकती है, और इसी तरह। splatless परीक्षण का एक सरल संशोधन splatful ब्लॉकों का ध्यान रखना चाहिए:

def perform(*args) 
    if @action.arity >= 0 && args.size != @action.arity 
    raise WrongArity.new(args.size) 
    elsif @action.arity < 0 && args.size < -(@action.arity + 1) 
    raise WrongArity.new(args.size) 
    end 
    @action.call(*args) 
end 
+0

आप सही हैं, यह समस्या हल करता है। लेकिन मैं इंतजार करने जा रहा हूं और देख सकता हूं कि ऐसा करने के लिए कम वर्बोज़ तरीका है या नहीं। जिस व्यवहार को मैं ढूंढ रहा हूं वह लैम्ब्डा प्रो के समान है, इसलिए अगर मुझे ऐसा करने की ज़रूरत नहीं है, तो मैं इसे पुन: कार्यान्वित नहीं करना चाहता हूं (मान लीजिए, मेरी कमांड क्लास पहले से ही प्रो को कार्यान्वित करने की तरह है, लेकिन अनिवार्य रूप से अधिक डोमेन- विशिष्ट व्यवहार) – rickyrickyrice

+0

@rickyrickyrice: हाँ, यह थोड़ा उलझन में प्रतीत होता है लेकिन मैं कुछ भी बेहतर नहीं सोच सकता। ओटीओएच, यह देर हो चुकी है इसलिए मुझे कुछ स्पष्ट याद आ रही है। –

+0

उपयोग lambdas के अलावा कोई और रास्ता नहीं है। इस प्रकार रूबी डिज़ाइन की गई है: proc में कोई धैर्य जांच नहीं है। 1.9.2 रूबी से शुरू करने से प्राप्यता वैधता लागू होती है। – Sigurd

-1

आपका समाधान:

class Command 
    attr_reader :name, :action 

    def initialize(name) # The block argument is removed 
    @name = name 
    @action = lambda # We replace `action` with just `lambda` 
    end 

    def perform(*args) 
    begin 
     action.call(*args) 
    rescue ArgumentError 
     raise(WrongArity.new(args.size)) 
    end 
    end 
end 

class WrongArity < StandardError; end 
कुछ संदर्भों

: "Proc.new के किसी भी तर्क के बिना एक विधि के अंदर से कहा जाता है इसका अपना, यह अपनी नई विधि को दिए गए ब्लॉक युक्त एक नई प्रक्रिया वापस करेगा। " - http://mudge.name/2011/01/26/passing-blocks-in-ruby-without-block.html

यह पता चला है कि लैम्ब्डा उसी तरह काम करता है।

+0

दिलचस्प, लेकिन मुझे नहीं लगता कि यह काम करता है: (irb): 7: चेतावनी: ब्लॉक के बिना प्रो ऑब्जेक्ट बनाने की कोशिश की गई – rickyrickyrice

+1

असल में मुझे लगता है कि यह चेतावनियों के बावजूद काम करता है, लेकिन तथ्य यह है कि यह इन चेतावनियों को बताता है आदर्श समाधान नहीं! –

+0

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

1

this answer के अनुसार, एक प्रक्रिया को लैम्ब्डा में बदलने का एकमात्र तरीका define_method और दोस्तों का उपयोग कर रहा है। docs से:

define_method हमेशा चाल के बिना एक विधि को परिभाषित करता है [अर्थात एक लैम्ब्डा शैली प्रोक], भले ही एक गैर लैम्ब्डा प्रोक वस्तु दिया जाता है। यह एकमात्र अपवाद है जिसके लिए चालें संरक्षित नहीं हैं।

विशेष रूप से, के साथ-साथ वास्तव में एक विधि को परिभाषित करने, define_method(:method_name, &block) एक लैम्ब्डा देता है। अनावश्यक रूप से कुछ खराब ऑब्जेक्ट्स पर विधियों के समूह को परिभाषित किए बिना इसका उपयोग करने के लिए, आप अस्थायी वस्तु पर define_singleton_method का उपयोग कर सकते हैं।

def initialize(name, &action) 
    @name = name 
    @action = to_lambda(&action) 
end 

def perform(*args) 
    action.call(*args) 
    # Could rescue ArgumentError and re-raise a WrongArity, but why bother? 
    # The default is "ArgumentError: wrong number of arguments (0 for 1)", 
    # doesn't that say it all? 
end 

private 

def to_lambda(&proc) 
    Object.new.define_singleton_method(:_, &proc) 
end