2012-06-01 14 views
9

क्या कोई विधि तर्क सूची में एक आवृत्ति परिवर्तनीय नाम निर्दिष्ट करने की कॉफीस्क्रिप्ट सुविधा के समान रूबी व्यवहार को लागू करने की कोई योजना है? रूबी: विधि तर्क के रूप में स्वचालित रूप से आवृत्ति चर सेट करें?

तरह
class User 
    def initialize(@name, age) 
    # @name is set implicitly, but @age isn't. 
    # the local variable "age" will be set, just like it currently works. 
    end 
end 

मैं इस सवाल के बारे में पता कर रहा हूँ: in Ruby can I automatically populate instance variables somehow in the initialize method?, लेकिन सभी समाधान (मेरे अपने सहित) गहरे लाल रंग का सादगी दर्शन फिट करने के लिए नहीं है।

और, क्या इस व्यवहार के लिए कोई डाउनसाइड्स होगा?

अद्यतन

इस के लिए कारणों में से एक सूखी (अपने आप को दोहराने नहीं है) माणिक समुदाय की विचारधारा है। मुझे अक्सर एक तर्क चर के नाम को दोहराने की आवश्यकता होती है क्योंकि मैं इसे उसी नाम के इंस्टेंस वैरिएबल को असाइन करना चाहता हूं।

def initialize(name) 
    # not DRY 
    @name = name 
end 

एक नकारात्मक पक्ष यह है कि मैं के बारे में सोच सकते हैं कि यह के रूप में यद्यपि एक विधि कुछ भी नहीं कर रही है, तो यह कोई शरीर है लग सकता है कि है। यदि आप जल्दी से स्कैन कर रहे हैं, तो यह नो-ऑप जैसा दिख सकता है। लेकिन मुझे लगता है कि समय दिया गया है, हम अनुकूलित कर सकते हैं।

एक और नकारात्मक पक्ष: यदि आप शरीर में अन्य आवृत्ति चर सेट कर रहे हैं, और आप शुरुआत में सभी असाइनमेंट डालकर पठनीय होने की कोशिश करते हैं, तो यह देखने के लिए और अधिक संज्ञानात्मक "शक्ति" ले सकती है कि वहां असाइनमेंट भी हो रहा है तर्क सूची। लेकिन मुझे नहीं लगता कि यह एक निरंतर या विधि कॉल देखने और इसकी परिभाषा पर कूदने के लिए कहने से कहीं अधिक कठिन है।

# notice: instance var assignments are happening in 2 places! 
def initialize(@name) 
    @errors = [] 
end 

उत्तर

4

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

और यह संभव है: How to get argument names using reflection

हाँ! इसलिए मैं इसे मेरे लिए संभालने के लिए एक मॉड्यूल लिख सकता हूं। तब मैं अटक गया क्योंकि अगर मैं मॉड्यूल की सहायक विधि को कॉल करता हूं, तो यह तर्कों के मूल्यों को नहीं जानता क्योंकि वे कॉलर के लिए स्थानीय हैं। आह, लेकिन रूबी बाध्यकारी वस्तुओं है।

यहाँ मॉड्यूल (गहरे लाल रंग का 1.9 केवल) है:

module InstanceVarsFromArgsSlurper 
    # arg_prefix must be a valid local variable name, and I strongly suggest 
    # ending it with an underscore for readability of the slurped args. 
    def self.enable_for(mod, arg_prefix) 
    raise ArgumentError, "invalid prefix name" if arg_prefix =~ /[^a-z0-9_]/i 
    mod.send(:include, self) 
    mod.instance_variable_set(:@instance_vars_from_args_slurper_prefix, arg_prefix.to_s) 
    end 

    def slurp_args(binding) 
    defined_prefix = self.class.instance_variable_get(:@instance_vars_from_args_slurper_prefix) 
    method_name = caller[0][/`.*?'/][1..-2] 
    param_names = method(method_name).parameters.map{|p| p.last.to_s } 
    param_names.each do |pname| 
     # starts with and longer than prefix 
     if pname.start_with?(defined_prefix) and (pname <=> defined_prefix) == 1 
     ivar_name = pname[defined_prefix.size .. -1] 
     eval "@#{ivar_name} = #{pname}", binding 
     end 
    end 
    nil 
    end 
end 

और यहाँ उपयोग है:

class User 
    InstanceVarsFromArgsSlurper.enable_for(self, 'iv_') 

    def initialize(iv_name, age) 
    slurp_args(binding) # this line does all the heavy lifting 
    p [:iv_name, iv_name] 
    p [:age, age] 
    p [:@name, @name] 
    p [:@age, @age] 
    end 
end 

user = User.new("Methuselah", 969) 
p user 

आउटपुट:

[:iv_name, "Methuselah"] 
[:age, 969] 
[:@name, "Methuselah"] 
[:@age, nil] 
#<User:0x00000101089448 @name="Methuselah"> 

यह नहीं करता है आप एक खाली है विधि शरीर, लेकिन यह DRY है। मुझे यकीन है कि इसे प्रत्येक विधि में slurp_args को कॉल करने के बजाय, केवल यह निर्दिष्ट करके कि कौन सी विधियों को इस व्यवहार (alias_method के माध्यम से कार्यान्वित किया जाना चाहिए) निर्दिष्ट किया जा सकता है - विनिर्देश सभी विधियों को परिभाषित करने के बाद होना चाहिए।

ध्यान दें कि मॉड्यूल और सहायक विधि का नाम शायद सुधार किया जा सकता है। मैंने बस पहली चीज का इस्तेमाल किया जो दिमाग में आया था।

+0

इस उत्तर के लिए धन्यवाद। यह निश्चित रूप से अधिक काम की तरह लगता है कि यह लायक है। :) – hrdwdmrbl

+0

@jackquack अच्छी तरह से, आपको वास्तव में केवल एक बार सेटअप करना होगा। फिर अपनी कक्षाओं में, आपको बस 'enable_for' और' slurp_args' कॉल करना है। – Kelvin

0

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

कुछ परिदृश्य @ परम का सामना करना होगा:

def initialize(first, last, @scope, @opts = {}) 

def search(@query, condition) 

def ratchet(@*arg ) 

इन परिदृश्यों के सभी मान्य होना चाहिए? बस initialize? @*arg मेरे दिमाग में विशेष रूप से पागल लगता है। इन सभी नियमों और बहिष्कार रूबी भाषा को और अधिक जटिल बनाते हैं। ऑटो इंस्टेंस चर के लाभ के लिए, मुझे नहीं लगता कि यह इसके लायक होगा।

+2

मैं एक स्प्लेटेड इंस्टेंस तर्क को '* @ arg' होने की कल्पना करता हूं। इसके अलावा, किसी भी परिदृश्य में विशेष रूप से बदसूरत, जटिल, या यहां तक ​​कि पालन करने में कठिनाई नहीं होती है। उदाहरण के तर्क के संदर्भ में, आप उन्हें पहले से ही आर्ग सूची में सेट कर सकते हैं, यद्यपि एक विपरीत तरीके से: 'def run (a = (@ v = 1)); end'। हां, यह बदसूरत है, और अगर कोई तर्क पारित नहीं होता है तो var केवल तभी सेट हो जाता है। लेकिन यह दर्शाता है कि इस तरह की असाइनमेंट पहले ही अनुमति है। मुझे लगता है कि यह कोड DRYER रखने में मदद करता है। मैं अपने प्रश्न में एक और उदाहरण जोड़ने जा रहा हूं। – Kelvin

+0

ओह, जब मैंने कहा कि यह कोड DRYer बनाता है, तो मेरा मतलब है कि मेरे प्रश्न में प्रस्ताव, मेरी टिप्पणी में प्रदूषित उदाहरण नहीं। एक बहादुर प्रयास के लिए – Kelvin

2

ठीक है, वास्तव में ... 1.8.7 में

class User 
    define_method(:initialize) { |@name| } 
end 

User.new(:name).instance_variable_get :@name 
# => :name 

काम करता है, लेकिन 1.9.3 में नहीं। अब, जहां मैंने इस बारे में सीखा ...

+2

+1। अभी तक यह कोशिश नहीं की है लेकिन यह एक नवीनता है यदि यह 1.9.3 पर काम नहीं करता है। – Kelvin

संबंधित मुद्दे