रूबी में एक पैरामीटर के रूप में एक विधि पारित करना


117

मैं रूबी के साथ थोड़ा सा गड़बड़ करने की कोशिश कर रहा हूं। इसके बाद मैं "प्रोग्रामिंग कलेक्टिव इंटेलिजेंस" रूबी पुस्तक से एल्गोरिदम (पायथन में दिए गए) को लागू करने की कोशिश करता हूं।

अध्याय 8 में लेखक एक विधि को पैरामीटर के रूप में पारित करता है। यह पायथन में काम करने लगता है लेकिन रूबी में नहीं।

मैं यहाँ विधि है

def gaussian(dist, sigma=10.0)
  foo
end

और इसे दूसरी विधि से कॉल करना चाहते हैं

def weightedknn(data, vec1, k = 5, weightf = gaussian)
  foo
  weight = weightf(dist)
  foo
end

मुझे जो मिला वह त्रुटि है

ArgumentError: wrong number of arguments (0 for 1)

जवाबों:


100

आप एक खरीद वस्तु चाहते हैं:

gaussian = Proc.new do |dist, *args|
  sigma = args.first || 10.0
  ...
end

def weightedknn(data, vec1, k = 5, weightf = gaussian)
  ...
  weight = weightf.call(dist)
  ...
end

बस ध्यान दें कि आप इस तरह एक ब्लॉक घोषणा में एक डिफ़ॉल्ट तर्क सेट नहीं कर सकते। इसलिए आपको एक स्पैट का उपयोग करने की आवश्यकता है और स्वयं ही खरीद कोड में डिफ़ॉल्ट सेटअप करें।


या, इस सब के अपने दायरे के आधार पर, इसके बजाय विधि नाम में पारित करना आसान हो सकता है।

def weightedknn(data, vec1, k = 5, weightf = :gaussian)
  ...
  weight = self.send(weightf)
  ...
end

इस मामले में आप केवल एक विधि को कॉल कर रहे हैं जो कोड के एक पूर्ण भाग में पारित होने के बजाय एक ऑब्जेक्ट पर परिभाषित किया गया है। पर निर्भर करता है कि आप इस आप को बदलने की आवश्यकता हो सकती संरचना self.sendके साथobject_that_has_the_these_math_methods.send


पिछले नहीं बल्कि कम से कम, आप विधि से एक ब्लॉक लटका सकते हैं।

def weightedknn(data, vec1, k = 5)
  ...
  weight = 
    if block_given?
      yield(dist)
    else
      gaussian.call(dist)
    end
  end
  ...
end

weightedknn(foo, bar) do |dist|
  # square the dist
  dist * dist
end

लेकिन ऐसा लगता है कि आप यहां कोड के अधिक पुन: प्रयोज्य टुकड़े पसंद करेंगे।


1
मुझे लगता है कि दूसरा विकल्प सबसे अच्छा विकल्प है (वह है, Object.send () का उपयोग करके), दोष यह है कि आपको इसके लिए एक वर्ग का उपयोग करने की आवश्यकता है (जो कि वैसे भी आपको OO में क्या करना चाहिए :))। यह हर समय एक ब्लॉक (प्रोक) पास करने से ज्यादा DRY होता है, और आप रैपर पद्धति को गढ़ने वाले तर्क भी पारित कर सकते हैं।
जिमी स्टेनके 12

4
इसके अतिरिक्त, यदि आप foo.bar(a,b)भेजना चाहते हैं, तो यह है foo.send(:bar, a, b)। स्पैट (*) ऑपरेटर आपको यह करने की अनुमति देता है कि foo.send(:bar, *[a,b])क्या आपको पता होना चाहिए कि आप मनमानी लंबाई के तर्क चाहते हैं - यह मानकर कि बार विधि उन्हें सोख सकती है
xxjjnn

99

ब्लॉक और प्रोक्स का जिक्र करने वाली टिप्पणियां सही हैं कि वे रूबी में अधिक सामान्य हैं। लेकिन आप चाहें तो एक तरीका पास कर सकते हैं। आप methodविधि प्राप्त करने के लिए और .callइसे कॉल करने के लिए कॉल करते हैं:

def weightedknn( data, vec1, k = 5, weightf = method(:gaussian) )
  ...
  weight = weightf.call( dist )
  ...
end

3
यह दिलचस्प है। यह ध्यान देने योग्य है कि आप method( :<name> )अपने तरीके को कॉल करने योग्य प्रतीक में परिवर्तित करते समय सिर्फ एक बार कॉल करते हैं। आप उस परिणाम को एक चर या एक पैरामीटर में स्टोर कर सकते हैं और इसे तब तक किसी अन्य चर की तरह बच्चे के कार्यों में पास कर सकते हैं ...

1
या हो सकता है, तर्क सूची में विधि सिंटैक्स का उपयोग करने के बजाय , आप विधि का उपयोग करते हुए इसका उपयोग कर सकते हैं: भारितकन (डेटा, vec1, k, विधि (: गाऊसी))
याह्या

1
यह विधि किसी प्रोक या ब्लॉक के साथ घूमने से बेहतर है, क्योंकि आपको मापदंडों को संभालना नहीं है - यह सिर्फ उस विधि के साथ काम करता है जो कुछ भी चाहता है।
दानुकर

3
पूरा करने के लिए, यदि आप एक विधि को कहीं और परिभाषित करना चाहते हैं, तो करें SomewhereElse.method(:method_name)। यह बहुत मजेदार है!
मेडिक

यह अपना सवाल हो सकता है लेकिन, मैं कैसे निर्धारित कर सकता हूं कि कोई प्रतीक किसी फ़ंक्शन का संदर्भ देता है या कुछ और? मैंने कोशिश की, :func.classलेकिन वह सिर्फ एक हैsymbol
शांतचित्त

46

आप एक विधि को पैरामीटर के रूप में पारित कर सकते हैं method(:function)। नीचे एक बहुत ही सरल उदाहरण दिया गया है:

डबल (ए) को हराया
  वापसी * ​​२ 
समाप्त
=> नील

def method_with_function_as_param (कॉलबैक, संख्या) 
  callback.call (संख्या) 
समाप्त 
=> नील

method_with_function_as_param (विधि: (डबल), 10) 
=> 20

7
मैं एक अधिक जटिल गुंजाइश के साथ एक विधि के लिए एक मुद्दा का सामना करना पड़ा, और अंत में पता लगा, कैसे करना है उम्मीद है कि इस मदद कर सकते हैं किसी को: अपने विधि किसी अन्य वर्ग में उदाहरण के लिए है, तो आप की तरह कोड की अंतिम पंक्ति पर फोन करना चाहिए method_with_function_as_param(Class.method(:method_name),...)और नहींmethod(:Class.method_name)
वी .डेहा

आपके उत्तर के लिए धन्यवाद, मैंने कहा जाने वाला तरीका खोजा method। मेरा दिन बना लेकिन मुझे लगता है कि इसीलिए मैं कार्यात्मक भाषाओं को पसंद करता हूं, जो आप चाहते हैं उसे पाने के लिए ऐसी कलाबाजियां करने की जरूरत नहीं है। वैसे भी, मैं
लुडोविक क्यूट

25

ऐसा करने का सामान्य रूबी तरीका एक ब्लॉक का उपयोग करना है।

तो यह कुछ इस तरह होगा:

def weightedknn( data, vec1, k = 5 )
  foo
  weight = yield( dist )
  foo
end

और जैसे इस्तेमाल किया:

weightenknn( data, vec1 ) { |dist| gaussian( dist ) }

रूबी में इस पैटर्न का बड़े पैमाने पर उपयोग किया जाता है।


14

आप विधि को ब्लॉक करने के लिए कनवर्ट करने &के Methodलिए ऑपरेटर का उपयोग कर सकते हैं ।

उदाहरण:

def foo(arg)
  p arg
end

def bar(&block)
  p 'bar'
  block.call('foo')
end

bar(&method(:foo))

Http://weblog.raganwald.com/2008/06/what-does-do-when-used-as-unary.html पर अधिक जानकारी


1

आपको फ़ंक्शन ऑब्जेक्ट की विधि "कॉल" को कॉल करना होगा:

weight = weightf.call( dist )

EDIT: जैसा कि टिप्पणियों में बताया गया है, यह दृष्टिकोण गलत है। यदि आप सामान्य कार्यों के बजाय Procs का उपयोग कर रहे हैं तो यह काम करेगा।


1
जब वह weightf = gaussianआर्ग लिस्ट में होता है, तो वह वास्तव gaussianमें वज़न के डिफ़ॉल्ट मूल्य के रूप में परिणाम को निष्पादित करने और असाइन करने की कोशिश कर रहा है । कॉल के लिए आवश्यक नहीं है args और क्रैश। तो वेटफ़ एक कॉल विधि के साथ अभी तक एक खरीद वस्तु नहीं है।
एलेक्स वेन

1
यह (यानी यह गलत है और यह बताते हुए टिप्पणी क्यों) वास्तव में मुझे स्वीकृत उत्तर को पूरी तरह से समझने की अनुमति है, इसलिए धन्यवाद! +1
rmcsharry

1

मैं एक फ़ंक्शन के भीतर नामित ब्लॉकों तक पहुंच के लिए एम्परसेंड का उपयोग करने की सिफारिश करूंगा। इस आलेख में दी गई सिफारिशों के बाद आप कुछ इस तरह लिख सकते हैं (यह मेरे काम करने के कार्यक्रम से एक वास्तविक स्क्रैप है):

  # Returns a valid hash for html form select element, combined of all entities
  # for the given +model+, where only id and name attributes are taken as
  # values and keys correspondingly. Provide block returning boolean if you
  # need to select only specific entities.
  #
  # * *Args*    :
  #   - +model+ -> ORM interface for specific entities'
  #   - +&cond+ -> block {|x| boolean}, filtering entities upon iterations
  # * *Returns* :
  #   - hash of {entity.id => entity.name}
  #
  def make_select_list( model, &cond )
    cond ||= proc { true } # cond defaults to proc { true }
    # Entities filtered by cond, followed by filtration by (id, name)
    model.all.map do |x|
      cond.( x ) ? { x.id => x.name } : {}
    end.reduce Hash.new do |memo, e| memo.merge( e ) end
  end

Afterwerds, आप इस फ़ंक्शन को इस तरह से कॉल कर सकते हैं:

@contests = make_select_list Contest do |contest|
  logged_admin? or contest.organizer == @current_user
end

यदि आपको अपने चयन को फ़िल्टर करने की आवश्यकता नहीं है, तो आप बस ब्लॉक को छोड़ दें:

@categories = make_select_list( Category ) # selects all categories

रूबी ब्लॉक की शक्ति के लिए इतना।


-5

आप "eval" का भी उपयोग कर सकते हैं, और इस विधि को एक स्ट्रिंग तर्क के रूप में पास कर सकते हैं, और फिर इसे अन्य विधि में भी विकसित कर सकते हैं।


1
यह वास्तव में बुरा अभ्यास है, कभी मत करो!
डेवलपर

@Developer क्यों यह बुरा अभ्यास माना जाता है?
17

प्रदर्शन कारणों के बीच, evalमनमाना कोड निष्पादित कर सकता है , इसलिए यह विभिन्न हमलों के लिए बेहद असुरक्षित है।
डेवलपर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.