क्या आप रूबी में नक्शे (और: विधि) के सिंटैक्स पर तर्क दे सकते हैं?


116

आप शायद निम्नलिखित रूबी आशुलिपि ( aएक सरणी है) से परिचित हैं :

a.map(&:method)

उदाहरण के लिए, irb में निम्नलिखित प्रयास करें:

>> a=[:a, 'a', 1, 1.0]
=> [:a, "a", 1, 1.0]
>> a.map(&:class)
=> [Symbol, String, Fixnum, Float]

वाक्यविन्यास a.map(&:class)एक आशुलिपि है a.map {|x| x.class}

रूबी में " मैप (और: नाम) का क्या अर्थ है? " इस सिंटैक्स के बारे में और पढ़ें ।

वाक्य रचना के माध्यम से &:class, आप classप्रत्येक ऐरे तत्व के लिए एक विधि कॉल कर रहे हैं ।

मेरा सवाल है: क्या आप मेथड कॉल के लिए तर्क दे सकते हैं? और यदि हां, तो कैसे?

उदाहरण के लिए, आप निम्न सिंटैक्स को कैसे परिवर्तित करते हैं

a = [1,3,5,7,9]
a.map {|x| x + 2}

करने के लिए &:वाक्य रचना?

मैं सुझाव नहीं दे रहा हूं कि &:वाक्य रचना बेहतर है। मैं केवल &:तर्क के साथ वाक्यविन्यास का उपयोग करने के यांत्रिकी में दिलचस्पी रखता हूं ।

मुझे लगता है कि आप जानते हैं कि +यह इंटेगर क्लास का एक तरीका है। आप irb में निम्नलिखित प्रयास कर सकते हैं:

>> a=1
=> 1
>> a+(1)
=> 2
>> a.send(:+, 1)
=> 2

जवाबों:


139

आप Symbolइस तरह से एक साधारण पैच बना सकते हैं :

class Symbol
  def with(*args, &block)
    ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
  end
end

जो आपको ऐसा करने में सक्षम नहीं करेगा:

a = [1,3,5,7,9]
a.map(&:+.with(2))
# => [3, 5, 7, 9, 11] 

लेकिन यह भी कई अन्य मापदंडों को पारित करने की तरह, अन्य शांत सामान:

arr = ["abc", "babc", "great", "fruit"]
arr.map(&:center.with(20, '*'))
# => ["********abc*********", "********babc********", "*******great********", "*******fruit********"]
arr.map(&:[].with(1, 3))
# => ["bc", "abc", "rea", "rui"]
arr.map(&:[].with(/a(.*)/))
# => ["abc", "abc", "at", nil] 
arr.map(&:[].with(/a(.*)/, 1))
# => ["bc", "bc", "t", nil] 

और यहां तक ​​कि साथ काम करते हैं inject, जो ब्लॉक में दो तर्क देता है:

%w(abecd ab cd).inject(&:gsub.with('cde'))
# => "cdeeecde" 

या शॉर्टहैंड ब्लॉक के लिए [आशुलिपि] ब्लॉकों को पास करने के रूप में कुछ सुपर कूल :

[['0', '1'], ['2', '3']].map(&:map.with(&:to_i))
# => [[0, 1], [2, 3]]
[%w(a b), %w(c d)].map(&:inject.with(&:+))
# => ["ab", "cd"] 
[(1..5), (6..10)].map(&:map.with(&:*.with(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]] 

यहाँ एक बातचीत है जो मैंने @ArupRakshit के साथ आगे बताई थी:
क्या आप रूबी में मैप (और: मेथड) सिंटैक्स पर तर्क दे सकते हैं?


जैसा कि @amcaplan ने नीचे टिप्पणी में सुझाया है , यदि आप withविधि का नाम बदल देते हैं, तो आप एक छोटा वाक्यविन्यास बना सकते हैं call। इस मामले में, रूबी के पास इस विशेष विधि के लिए एक शॉर्टकट है .()

तो आप इस तरह से ऊपर का उपयोग कर सकते हैं:

class Symbol
  def call(*args, &block)
    ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
  end
end

a = [1,3,5,7,9]
a.map(&:+.(2))
# => [3, 5, 7, 9, 11] 

[(1..5), (6..10)].map(&:map.(&:*.(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]] 

5
महान, काश यह रूबी कोर का हिस्सा होता!
जिक्कू जोस

6
@UriAgassi सिर्फ इसलिए कि बहुत सारे पुस्तकालय ऐसा करते हैं, यह एक अच्छा अभ्यास नहीं है। हालांकि Symbol#withमुख्य पुस्तकालय में मौजूद नहीं हो सकता है और उस पद्धति को परिभाषित करना मौजूदा पद्धति को पुनर्परिभाषित करने की तुलना में कम विनाशकारी है यह अभी भी बदल रहा है (यानी अधिलेखित) रूबी पुस्तकालय के मुख्य वर्ग के कार्यान्वयन। अभ्यास बहुत संयम से और बहुत सावधानी से किया जाना चाहिए। \ n \ n कृपया मौजूदा वर्ग से विरासत पर विचार करें और नए बनाए गए वर्ग को संशोधित करें। यह आमतौर पर बदलते रूबी वर्गों के नकारात्मक दुष्प्रभावों के बिना तुलनीय परिणाम प्राप्त करता है।
रुडोल्फ

2
@ rudolph9 - मैं अलग होना चाहता हूँ - "ओवरराइट" की परिभाषा कुछ पर लिखना है, जिसका अर्थ है कि जो कोड लिखा गया था वह अब उपलब्ध नहीं है, और यह स्पष्ट रूप से ऐसा नहीं है। Symbolवर्ग को अपने सुझाव के बारे में - यह तुच्छ नहीं है (यदि संभव हो तो), क्योंकि यह एक ऐसा मूल वर्ग है (इसका कोई newतरीका नहीं है, उदाहरण के लिए), और इसका उपयोग बोझिल होगा (यदि संभव हो तो), जो इस हार को पूरा करेगा वृद्धि का उद्देश्य ... यदि आप एक कार्यान्वयन दिखा सकते हैं जो इसका उपयोग करता है, और तुलनीय परिणाम प्राप्त करता है - कृपया साझा करें!
उड़ी अगस्सी

3
मुझे यह समाधान पसंद है, लेकिन मुझे लगता है कि आप इसके साथ और भी मज़ेदार हो सकते हैं। एक withविधि को परिभाषित करने के बजाय , परिभाषित करें call। तब आप इस तरह a.map(&:+.(2))से काम कर सकते हैं जैसे object.()कि #callविधि का उपयोग करता है । और जब आप इस पर हों, तो आप मज़ेदार बातें लिख सकते हैं जैसे :+.(2).(3) #=> 5- लगता है कि LISPy, नहीं?
amcaplan

2
कोर में इसे देखना पसंद करेंगे - इसका एक सामान्य पैटर्न जो कुछ चीनी अलामा का उपयोग कर सकता है। पाम (&: foo)
स्टीफन

48

अपने उदाहरण के लिए किया जा सकता है a.map(&2.method(:+))

Arup-iMac:$ pry
[1] pry(main)> a = [1,3,5,7,9]
=> [1, 3, 5, 7, 9]
[2] pry(main)> a.map(&2.method(:+))
=> [3, 5, 7, 9, 11]
[3] pry(main)> 

यहाँ दिया गया है कि यह कैसे काम करता है :-

[3] pry(main)> 2.method(:+)
=> #<Method: Fixnum#+>
[4] pry(main)> 2.method(:+).to_proc
=> #<Proc:0x000001030cb990 (lambda)>
[5] pry(main)> 2.method(:+).to_proc.call(1)
=> 3

2.method(:+)एक Methodवस्तु देता है । फिर &, 2.method(:+)वास्तव में, एक कॉल #to_procविधि, जो इसे एक Procवस्तु बना रही है । इसके बाद रूबी में आप &: ऑपरेटर को क्या कहते हैं?


चतुर प्रयोग! क्या यह माना जाता है कि विधि आह्वान दोनों तरीकों से लागू किया जा सकता है (अर्थात गिरफ्तार [तत्व] .मिथोड (परम) === param.method (गिरफ्तार [तत्व])) या क्या मैं भ्रमित हूं?
कोस्टा रोस

@rkon मुझे आपका प्रश्न भी नहीं मिला। लेकिन अगर आप Pryऊपर दिए गए आउटपुट देखते हैं , तो आप इसे प्राप्त कर सकते हैं, यह कैसे काम कर रहा है।
अरूप रक्षित

5
@rkon यह दोनों तरीकों से काम नहीं करता है। यह इस विशेष मामले में काम करता है क्योंकि +सराहनीय है।
आरा

आप कई तर्कों की आपूर्ति कैसे कर सकते हैं? जैसा कि इस मामले में: a.map {| x | x.method (1,2,3)}
ज़ैक जू

1
यह मेरी बात है @ सावा :) कि यह समझ में आता है + लेकिन किसी अन्य विधि के लिए नहीं होगा या मान लें कि यदि आप X द्वारा प्रत्येक संख्या को विभाजित करना चाहते हैं
कोस्टा रोस

11

जैसा कि आप पुष्टि करने के लिए पोस्ट से जुड़े हैं, लेकिन इसके लिए a.map(&:class)एक आशुलिपि नहीं है ।a.map {|x| x.class}a.map(&:class.to_proc)

इसका मतलब यह है कि ऑपरेटर के to_procबाद जो कुछ भी कहा जाता है &

तो आप इसे सीधे Procबदले दे सकते हैं :

a.map(&(Proc.new {|x| x+2}))

मुझे पता है कि शायद यह आपके प्रश्न के उद्देश्य को पराजित करता है, लेकिन मैं इसके आसपास कोई अन्य तरीका नहीं देख सकता हूं - यह नहीं है कि आप निर्दिष्ट करें कि किस विधि को बुलाया जाए, आप इसे केवल कुछ ऐसा करते हैं जो उत्तर देता है to_proc


1
यह भी ध्यान रखें कि आप स्थानीय वेरिएबल्स के लिए प्रोक्स सेट कर सकते हैं और उन्हें मैप करने के लिए पास कर सकते हैं। my_proc = Proc.new{|i| i + 1},[1,2,3,4].map(&my_proc) => [2,3,4,5]
रुडोल्फ

10

संक्षिप्त उत्तर: नहीं।

@ Rkon के उत्तर के बाद, आप यह भी कर सकते हैं:

a = [1,3,5,7,9]
a.map &->(_) { _ + 2 } # => [3, 5, 7, 9, 11]

9
आप सही हैं, लेकिन मुझे नहीं लगता कि इससे &->(_){_ + 2}छोटा कोई है {|x| x + 2}
आरा

1
ऐसा नहीं है, यही @rkon अपने जवाब में कहता है इसलिए मैंने इसे दोहराया नहीं।
एगिस

2
@ हालांकि आपका जवाब छोटा नहीं है, यह बेहतर दिखता है।
जिक्कू जोस

1
यह एक भयानक समाधान है।
बेनमैर्गियो

5

अपने आप को मुख्य वर्गों में ढालने के बजाय, जैसा कि स्वीकृत उत्तर में है, यह पहलू रत्न की कार्यक्षमता का उपयोग करने के लिए छोटा और साफ है :

require 'facets'
a = [1,3,5,7,9]
a.map &:+.(2)

5

प्रगणकों के लिए एक और मूल विकल्प है जो मेरी राय में केवल दो तर्कों के लिए बहुत सुंदर है। कक्षा Enumerableमें वह विधि होती है with_objectजो फिर दूसरे को लौटाती है Enumerable

इसलिए आप &ऑपरेटर को प्रत्येक आइटम और तर्क के रूप में ऑब्जेक्ट के साथ एक विधि के लिए कॉल कर सकते हैं ।

उदाहरण:

a = [1,3,5,7,9]
a.to_enum.with_object(2).map(&:+) # => [3, 5, 7, 9, 11]

इस मामले में आप अधिक तर्क चाहते हैं कि आपको सफल को दोहराना चाहिए लेकिन यह मेरी राय में बदसूरत है:

a = [1,3,5,7,9]
a.to_enum.with_object(2).map(&:+).to_enum.with_object(5).map(&:+) # => [8, 10, 12, 14, 16]

0

मैं Symbol#withपहले से ही पोस्ट के बारे में निश्चित नहीं हूं , मैंने इसे थोड़ा सरल किया और यह अच्छी तरह से काम करता है:

class Symbol
  def with(*args, &block)
    lambda { |object| object.public_send(self, *args, &block) }
  end
end

( निजी तरीकों को कॉल करने से रोकने के public_sendबजाय भी उपयोग करता है send, callerपहले से ही रूबी द्वारा उपयोग किया जाता है इसलिए यह भ्रमित था)

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