Ruby Language
ब्लॉक और प्रोक्स और लम्बदा
खोज…
वाक्य - विन्यास
- प्रन्यू ( ब्लॉक )
- lambda {| args | कोड}
- -> (arg1, arg2) {कोड}
- object.to_proc
- {| single_arg | कोड}
- do | arg, (कुंजी, मान) | कोड अंत
टिप्पणियों
जब आप कई तरीकों से पंक्तिबद्ध हों, तो ऑपरेटर की पूर्ववर्ती स्थिति के बारे में सावधान रहें, जैसे:
str = "abcdefg"
puts str.gsub(/./) do |match|
rand(2).zero? ? match.upcase : match.downcase
end
abCDeFg
जैसी किसी चीज़ को प्रिंट करने के बजाय, जैसे आप अपेक्षा करेंगे, यह कुछ ऐसा प्रिंट करता है जैसे #<Enumerator:0x00000000af42b28>
- ऐसा इसलिए है क्योंकि do ... end
में विधियों की तुलना में कम पूर्वता होती है, जिसका अर्थ है कि gsub
इसे देखता है /./
तर्क , और ब्लॉक तर्क नहीं। यह एक एन्यूमरेटर लौटाता है। ब्लॉक puts
लिए समाप्त हो जाता puts
, जो इसे अनदेखा करता है और बस gsub(/./)
का परिणाम प्रदर्शित करता है।
इसे ठीक करने के लिए, या तो कोष्ठक में gsub
कॉल लपेटें या इसके बजाय { ... }
उपयोग करें।
प्रोक
def call_the_block(&calling); calling.call; end
its_a = proc do |*args|
puts "It's a..." unless args.empty?
"beautiful day"
end
puts its_a #=> "beautiful day"
puts its_a.call #=> "beautiful day"
puts its_a[1, 2] #=> "It's a..." "beautiful day"
हमने पिछले उदाहरण से call_the_block
कॉपी किया है। यहाँ, आप देख सकते हैं कि एक proc फोन करके किया जाता है proc
एक ब्लॉक के साथ विधि। आप यह भी देख सकते हैं कि ब्लॉक, विधियों की तरह, निहितार्थ भी हैं, जिसका अर्थ है कि प्रोक्स (और लैम्ब्डा) भी करते हैं। its_a
की परिभाषा में, आप देख सकते हैं कि ब्लॉक its_a
तर्कों के साथ-साथ सामान्य वाले भी ले सकते हैं; वे डिफ़ॉल्ट तर्क लेने में भी सक्षम हैं, लेकिन मैं उस तरीके से काम करने के बारे में सोच नहीं सकता था। अंत में, आप देख सकते हैं कि एक विधि को कॉल करने के लिए कई सिंटैक्स का उपयोग करना संभव है - या तो call
विधि, या []
ऑपरेटर।
lambdas
# lambda using the arrow syntax
hello_world = -> { 'Hello World!' }
hello_world[]
# 'Hello World!'
# lambda using the arrow syntax accepting 1 argument
hello_world = ->(name) { "Hello #{name}!" }
hello_world['Sven']
# "Hello Sven!"
the_thing = lambda do |magic, ohai, dere|
puts "magic! #{magic}"
puts "ohai #{dere}"
puts "#{ohai} means hello"
end
the_thing.call(1, 2, 3)
# magic! 1
# ohai 3
# 2 means hello
the_thing.call(1, 2)
# ArgumentError: wrong number of arguments (2 for 3)
the_thing[1, 2, 3, 4]
# ArgumentError: wrong number of arguments (4 for 3)
लैम्ब्डा को कॉल करने के लिए ->
.()
to create and .()
का भी उपयोग कर सकते हैं
the_thing = ->(magic, ohai, dere) {
puts "magic! #{magic}"
puts "ohai #{dere}"
puts "#{ohai} means hello"
}
the_thing.(1, 2, 3)
# => magic! 1
# => ohai 3
# => 2 means hello
यहां आप देख सकते हैं कि एक मेमना लगभग खरीद के समान है। हालांकि, कई चेतावनी हैं:
एक लंबोदर के तर्कों को लागू किया जाता है; एक लैम्ब्डा के तर्कों की गलत संख्या गुजर, एक बढ़ा देंगे
ArgumentError
। उनके पास अभी भी डिफ़ॉल्ट पैरामीटर, स्पैट पैरामीटर आदि हो सकते हैं।return
करते हुए लैम्ब्डा से एक लैम्ब्डा रिटर्न के भीतर से ing,return
संलग्न क्षेत्र से बाहर एक proc रिटर्न से ing:def try_proc x = Proc.new { return # Return from try_proc } x.call puts "After x.call" # this line is never reached end def try_lambda y = -> { return # return from y } y.call puts "After y.call" # this line is not skipped end try_proc # No output try_lambda # Outputs "After y.call"
विधियों के रूप में ऑब्जेक्ट्स को ब्लॉक करें
एक लाना &
एक तर्क के सामने (एम्परसेंड) विधि के ब्लॉक के रूप में यह पारित करेंगे। ऑब्जेक्ट्स को to_proc
विधि का उपयोग करके Proc
कनवर्ट किया जाएगा।
class Greeter
def to_proc
Proc.new do |item|
puts "Hello, #{item}"
end
end
end
greet = Greeter.new
%w(world life).each(&greet)
यह रूबी में एक सामान्य पैटर्न है और कई मानक कक्षाएं इसे प्रदान करती हैं।
उदाहरण के लिए, Symbol
ने स्वयं को तर्क पर भेजकर to_proc
लागू किया है:
# Example implementation
class Symbol
def to_proc
Proc.new do |receiver|
receiver.send self
end
end
end
यह उपयोगी सक्षम बनाता है &:symbol
मुहावरा, आमतौर पर के साथ प्रयोग किया Enumerable
वस्तुओं:
letter_counts = %w(just some words).map(&:length) # [4, 4, 5]
ब्लाकों
ब्लॉक ब्रेसिज़ {}
(आमतौर पर सिंगल-लाइन ब्लॉक के लिए) या do..end
(मल्टी-लाइन ब्लॉक के लिए प्रयुक्त) के बीच संलग्न कोड के विखंडू हैं।
5.times { puts "Hello world" } # recommended style for single line blocks
5.times do
print "Hello "
puts "world"
end # recommended style for multi-line blocks
5.times {
print "hello "
puts "world" } # does not throw an error but is not recommended
नोट: ब्रेसिज़ की तुलना में अधिक पूर्वता do..end
उपज
ब्लॉक का उपयोग yield
तरीकों और कार्यों के अंदर किया जा सकता yield
:
def block_caller
puts "some code"
yield
puts "other code"
end
block_caller { puts "My own block" } # the block is passed as an argument to the method.
#some code
#My own block
#other code
सावधान रहें, हालांकि अगर yield
को एक ब्लॉक के बिना कहा जाता है तो यह एक LocalJumpError
को बढ़ा LocalJumpError
। इस उद्देश्य के लिए रूबी block_given?
नामक एक अन्य विधि प्रदान करता है block_given?
यह आपको यह जांचने की अनुमति देता है कि क्या उपज बुलाने से पहले एक ब्लॉक पारित किया गया था
def block_caller
puts "some code"
if block_given?
yield
else
puts "default"
end
puts "other code"
end
block_caller
# some code
# default
# other code
block_caller { puts "not defaulted"}
# some code
# not defaulted
# other code
yield
ब्लॉक को भी तर्क yield
सकता है
def yield_n(n)
p = yield n if block_given?
p || n
end
yield_n(12) {|n| n + 7 }
#=> 19
yield_n(4)
#=> 4
हालांकि यह एक सरल उदाहरण है yield
आईएनजी किसी अन्य वस्तु के संदर्भ में उदाहरण चर या मूल्यांकन के लिए सीधी पहुंच की अनुमति देने के लिए बहुत उपयोगी हो सकता है। उदाहरण के लिए:
class Application
def configuration
@configuration ||= Configuration.new
block_given? ? yield(@configuration) : @configuration
end
end
class Configuration; end
app = Application.new
app.configuration do |config|
puts config.class.name
end
# Configuration
#=> nil
app.configuration
#=> #<Configuration:0x2bf1d30>
जैसा कि आप इस तरीके से yield
का उपयोग करते हुए देख सकते हैं कि कोड लगातार कॉल करने वाले app.configuration.#method_name
तुलना में अधिक पठनीय बनाता है app.configuration.#method_name
। app.configuration.#method_name
। इसके बजाय आप कोड को समाहित रखते हुए ब्लॉक के अंदर सभी विन्यास कर सकते हैं।
चर
ब्लॉक के लिए चर ब्लॉक के लिए स्थानीय हैं (फ़ंक्शन के चर के समान), ब्लॉक निष्पादित होने पर वे मर जाते हैं।
my_variable = 8
3.times do |x|
my_variable = x
puts my_variable
end
puts my_variable
#=> 0
# 1
# 2
# 8
ब्लॉक को बचाया नहीं जा सकता है, वे एक बार निष्पादित हो जाते हैं। ब्लॉक को बचाने के लिए आपको procs
और lambdas
का उपयोग करने की आवश्यकता है।
Proc में परिवर्तित करना
to_proc
जवाब देने वाली वस्तुओं को &
ऑपरेटर (जो उन्हें ब्लॉक के रूप में भी पारित करने की अनुमति देगा) के साथ procs में परिवर्तित किया जा सकता है।
वर्ग प्रतीक #to_proc
को परिभाषित करता है इसलिए यह पैरामीटर के रूप में प्राप्त वस्तु पर संबंधित विधि को कॉल करने का प्रयास करता है।
p [ 'rabbit', 'grass' ].map( &:upcase ) # => ["RABBIT", "GRASS"]
विधि ऑब्जेक्ट भी #to_proc
को परिभाषित #to_proc
।
output = method( :p )
[ 'rabbit', 'grass' ].map( &output ) # => "rabbit\ngrass"
आंशिक आवेदन और करी
तकनीकी रूप से, रूबी के पास कार्य नहीं हैं, लेकिन विधियां हैं। हालांकि, एक रूबी विधि अन्य भाषा में कार्यों के लिए लगभग समान रूप से व्यवहार करती है:
def double(n)
n * 2
end
यह सामान्य विधि / फ़ंक्शन एक पैरामीटर n
लेता है, इसे दोगुना करता है और मान लौटाता है। अब एक उच्च क्रम फ़ंक्शन (या विधि) को परिभाषित करते हैं:
def triple(n)
lambda {3 * n}
end
संख्या वापस करने के बजाय, triple
एक विधि देता है। आप इंटरएक्टिव रूबी शेल का उपयोग करके इसका परीक्षण कर सकते हैं:
$ irb --simple-prompt
>> def double(n)
>> n * 2
>> end
=> :double
>> def triple(n)
>> lambda {3 * n}
>> end
=> :triple
>> double(2)
=> 4
>> triple(2)
=> #<Proc:0x007fd07f07bdc0@(irb):7 (lambda)>
यदि आप वास्तव में तीन गुना संख्या प्राप्त करना चाहते हैं, तो आपको लैम्ब्डा को कॉल (या "कम") करना होगा:
triple_two = triple(2)
triple_two.call # => 6
या अधिक संक्षेप में:
triple(2).call
करी और आंशिक अनुप्रयोग
यह बहुत ही बुनियादी कार्यक्षमता को परिभाषित करने के मामले में उपयोगी नहीं है, लेकिन यह उपयोगी है यदि आप ऐसे तरीके / कार्य करना चाहते हैं जो तुरंत नहीं कहे या कम किए जाएं। उदाहरण के लिए, add_one(2) = 3
लें कि आप उन विधियों को परिभाषित करना चाहते हैं जो किसी संख्या को एक विशिष्ट संख्या से add_one(2) = 3
उदाहरण के लिए add_one(2) = 3
)। यदि आपको इनमें से एक टन को परिभाषित करना था तो आप कर सकते हैं:
def add_one(n)
n + 1
end
def add_two(n)
n + 2
end
हालाँकि, आप यह भी कर सकते हैं:
add = -> (a, b) { a + b }
add_one = add.curry.(1)
add_two = add.curry.(2)
लैम्ब्डा कैलकुलस का उपयोग करके हम कह सकते हैं कि add
(λa.(λb.(a+b)))
। Currying आंशिक रूप से लागू करने का एक तरीका है add
। तो add.curry.(1)
, (λa.(λb.(a+b)))(1)
जिसे घटाया जा सकता है (λb.(1+b))
। आंशिक अनुप्रयोग का अर्थ है कि हमने add
लिए एक तर्क पारित किया लेकिन बाद में आपूर्ति किए जाने वाले अन्य तर्क को छोड़ दिया। आउटपुट एक विशेष विधि है।
करीने के अधिक उपयोगी उदाहरण
मान लीजिए कि हमारे पास वास्तव में बड़ा सामान्य सूत्र है, कि यदि हम इसके लिए कुछ तर्क निर्दिष्ट करते हैं, तो हम इससे विशिष्ट सूत्र प्राप्त कर सकते हैं। इस सूत्र पर विचार करें:
f(x, y, z) = sin(x\*y)*sin(y\*z)*sin(z\*x)
यह सूत्र तीन आयामों में काम करने के लिए बनाया गया है, लेकिन मान लें कि हम केवल इस सूत्र को y और z के संबंध में चाहते हैं। मान लीजिए कि x को अनदेखा करने के लिए, हम इसे pi / 2 पर सेट करना चाहते हैं। आइए सबसे पहले सामान्य सूत्र बनाते हैं:
f = ->(x, y, z) {Math.sin(x*y) * Math.sin(y*z) * Math.sin(z*x)}
अब, हमारे yz
सूत्र को प्राप्त करने के लिए करी का उपयोग करें:
f_yz = f.curry.(Math::PI/2)
तो f_yz
में संग्रहीत लैम्ब्डा को कॉल करने के लिए:
f_xy.call(some_value_x, some_value_y)
यह बहुत आसान है, लेकिन हम कहते हैं कि हम xz
लिए सूत्र प्राप्त करना चाहते हैं। हम कैसे निर्धारित कर सकते हैं y
के लिए Math::PI/2
अगर यह अंतिम तर्क नहीं है? खैर, यह थोड़ा और अधिक जटिल है:
f_xz = -> (x,z) {f.curry.(x, Math::PI/2, z)}
इस मामले में, हमें उस पैरामीटर के लिए प्लेसहोल्डर प्रदान करने की आवश्यकता है जिसे हम पहले से नहीं भर रहे हैं। स्थिरता के लिए हम इस तरह से f_xy
लिख सकते हैं:
f_xy = -> (x,y) {f.curry.(x, y, Math::PI/2)}
यहाँ बताया गया है कि लैम्ब्डा कैलकुलस f_yz
लिए कैसे काम करता है:
f = (λx.(λy.(λz.(sin(x*y) * sin(y*z) * sin(z*x))))
f_yz = (λx.(λy.(λz.(sin(x*y) * sin(y*z) * sin(z*x)))) (π/2) # Reduce =>
f_yz = (λy.(λz.(sin((π/2)*y) * sin(y*z) * sin(z*(π/2))))
अब f_xz
को f_xz
f = (λx.(λy.(λz.(sin(x*y) * sin(y*z) * sin(z*x))))
f_xz = (λx.(λy.(λz.(sin(x*y) * sin(y*z) * sin(z*x)))) (λt.t) (π/2) # Reduce =>
f_xz = (λt.(λz.(sin(t*(π/2)) * sin((π/2)*z) * sin(z*t))))
लैम्ब्डा कैलकुलस के बारे में अधिक पढ़ने के लिए यह प्रयास करें ।