खोज…


वाक्य - विन्यास

  • प्रन्यू ( ब्लॉक )
  • 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_nameapp.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))))

लैम्ब्डा कैलकुलस के बारे में अधिक पढ़ने के लिए यह प्रयास करें



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow