কম্পিউটার

রুবিতে বন্ধ:ব্লক, প্রক্স এবং ল্যাম্বডাস

রুবি ম্যাজিকে আমরা প্রতিদিন যে জিনিসগুলি ব্যবহার করি সেগুলি কীভাবে কাজ করে তা বোঝার জন্য আমরা যাদুতে ডুব দিতে পছন্দ করি। এই সংস্করণে, আমরা ব্লক, প্রোকস এবং ল্যাম্বডাসের মধ্যে পার্থক্যগুলি অন্বেষণ করব৷

প্রথম-শ্রেণীর ফাংশন সহ প্রোগ্রামিং ভাষায়, ফাংশনগুলি ভেরিয়েবলগুলিতে সংরক্ষণ করা যেতে পারে এবং অন্যান্য ফাংশনে আর্গুমেন্ট হিসাবে প্রেরণ করা যেতে পারে। ফাংশন এমনকি তাদের রিটার্ন মান হিসাবে অন্যান্য ফাংশন ব্যবহার করতে পারে।

একটি বন্ধ একটি পরিবেশ সহ একটি প্রথম শ্রেণীর ফাংশন. পরিবেশ হল ভেরিয়েবলের একটি ম্যাপিং যা বন্ধ করার সময় বিদ্যমান ছিল। ক্লোজার এই ভেরিয়েবলগুলিতে তার অ্যাক্সেস বজায় রাখবে, এমনকি যদি সেগুলিকে অন্য কোনো সুযোগে সংজ্ঞায়িত করা হয়।

রুবির প্রথম-শ্রেণীর ফাংশন নেই, তবে ব্লক, প্রোকস এবং ল্যাম্বডাসের আকারে এটি বন্ধ রয়েছে। ব্লকগুলি কোডের ব্লকগুলিকে পদ্ধতিতে পাস করার জন্য ব্যবহার করা হয় এবং প্রোকস এবং ল্যাম্বডা ভেরিয়েবলগুলিতে কোডের ব্লকগুলি সংরক্ষণ করার অনুমতি দেয়৷

ব্লক

রুবিতে, ব্লক কোডের স্নিপেট যা পরে কার্যকর করার জন্য তৈরি করা যেতে পারে। ব্লকগুলিকে এমন পদ্ধতিতে প্রেরণ করা হয় যা সেগুলি do এর মধ্যে দেয় এবং end কীওয়ার্ড অনেক উদাহরণের মধ্যে একটি হল #each পদ্ধতি, যা অসংখ্য বস্তুর উপর লুপ করে।

[1,2,3].each do |n|
  puts "#{n}!"
end
 
[1,2,3].each { |n| puts "#{n}!" } # the one-line equivalent.

এই উদাহরণে, Array#each-এ একটি ব্লক পাঠানো হয় পদ্ধতি, যা অ্যারের প্রতিটি আইটেমের জন্য ব্লক চালায় এবং কনসোলে প্রিন্ট করে।

def each
  i = 0
  while i < size
    yield at(i)
    i += 1
  end
end

Array#each-এর এই সরলীকৃত উদাহরণে , while লুপ, yield অ্যারের প্রতিটি আইটেমের জন্য পাস করা ব্লক কার্যকর করতে বলা হয়। মনে রাখবেন যে এই পদ্ধতিতে কোন যুক্তি নেই, কারণ ব্লকটি পদ্ধতিতে নিহিতভাবে পাস করা হয়েছে।

ইমপ্লিসিট ব্লক এবং yield কীওয়ার্ড

রুবিতে, পদ্ধতিগুলি অন্তর্নিহিত এবং স্পষ্টভাবে ব্লক নিতে পারে। অন্তর্নিহিত ব্লক পাসিং yield কল করে কাজ করে একটি পদ্ধতিতে কীওয়ার্ড। yield কীওয়ার্ড বিশেষ। এটি একটি পাস করা ব্লক খুঁজে পায় এবং কল করে, তাই পদ্ধতিটি গ্রহণ করে এমন যুক্তিগুলির তালিকায় আপনাকে ব্লক যোগ করতে হবে না৷

যেহেতু রুবি অন্তর্নিহিত ব্লক পাস করার অনুমতি দেয়, আপনি একটি ব্লক সহ সমস্ত পদ্ধতি কল করতে পারেন। যদি এটি yield কল না করে , ব্লক উপেক্ষা করা হয়।

irb> "foo bar baz".split { p "block!" }
=> ["foo", "bar", "baz"]

যদি বলা পদ্ধতি করেন yield, yield-এ পাস করা যেকোনো আর্গুমেন্টের সাথে পাস করা ব্লক খুঁজে পাওয়া যায় এবং কল করা হয়। কীওয়ার্ড।

def each
  return to_enum(:each) unless block_given?
 
  i = 0
  while i < size
    yield at(i)
    i += 1
  end
end

এই উদাহরণটি Enumerator এর একটি উদাহরণ প্রদান করে যদি না একটি ব্লক দেওয়া হয়।

yield এবং block_given? কীওয়ার্ড বর্তমান সুযোগে ব্লক খুঁজে পায়। এটি পরোক্ষভাবে ব্লক পাস করার অনুমতি দেয়, কিন্তু কোডটিকে ব্লকটি সরাসরি অ্যাক্সেস করতে বাধা দেয় কারণ এটি একটি ভেরিয়েবলে সংরক্ষণ করা হয় না।

স্পষ্টভাবে ব্লক পাস করা

অ্যাম্পারস্যান্ড প্যারামিটার (সাধারণত &block বলা হয় ) যেহেতু ব্লকটি এখন স্পষ্ট, আমরা #call ব্যবহার করতে পারি yield-এর উপর নির্ভর না করে সরাসরি ফলিত বস্তুর উপর পদ্ধতি .

&block যুক্তি একটি সঠিক যুক্তি নয়, তাই এই পদ্ধতিটিকে ব্লক ছাড়া অন্য কিছু দিয়ে কল করলে একটি ArgumentError তৈরি হবে .

def each_explicit(&block)
  return to_enum(:each) unless block
 
  i = 0
  while i < size
    block.call at(i)
    i += 1
  end
end

যখন একটি ব্লক এভাবে পাস করা হয় এবং একটি ভেরিয়েবলে সংরক্ষণ করা হয়, তখন এটি স্বয়ংক্রিয়ভাবে একটি proc-এ রূপান্তরিত হয় .

প্রোক্স

একটি "proc" হল Proc এর একটি উদাহরণ ক্লাস, যা কার্যকর করার জন্য একটি কোড ব্লক ধারণ করে এবং একটি ভেরিয়েবলে সংরক্ষণ করা যেতে পারে। একটি proc তৈরি করতে, আপনি Proc.new কল করুন এবং এটি একটি ব্লক পাস করুন৷

proc = Proc.new { |n| puts "#{n}!" }

যেহেতু একটি proc একটি ভেরিয়েবলের মধ্যে সংরক্ষণ করা যেতে পারে, এটি একটি সাধারণ আর্গুমেন্টের মতো একটি পদ্ধতিতেও পাস করা যেতে পারে। সেই ক্ষেত্রে, আমরা অ্যাম্পারস্যান্ড ব্যবহার করি না, কারণ proc স্পষ্টভাবে পাস করা হয়।

def run_proc_with_random_number(proc)
  proc.call(random)
end
 
proc = Proc.new { |n| puts "#{n}!" }
run_proc_with_random_number(proc)

একটি proc তৈরি করে পদ্ধতিতে পাস করার পরিবর্তে, আপনি রুবির অ্যাম্পারস্যান্ড প্যারামিটার সিনট্যাক্স ব্যবহার করতে পারেন যা আমরা আগে দেখেছি এবং পরিবর্তে একটি ব্লক ব্যবহার করতে পারেন৷

def run_proc_with_random_number(&proc)
  proc.call(random)
end
 
run_proc_with_random_number { |n| puts "#{n}!" }

পদ্ধতিতে আর্গুমেন্টে যোগ করা অ্যাম্পারস্যান্ড নোট করুন। এটি একটি পাস ব্লককে একটি proc অবজেক্টে রূপান্তর করবে এবং এটিকে পদ্ধতির সুযোগে একটি ভেরিয়েবলে সংরক্ষণ করবে৷

টিপ :যদিও কিছু পরিস্থিতিতে পদ্ধতিতে proc থাকা দরকারী, একটি ব্লককে একটি proc-এ রূপান্তর করার ফলে একটি পারফরম্যান্স হিট তৈরি হয়। যখনই সম্ভব, এর পরিবর্তে অন্তর্নিহিত ব্লক ব্যবহার করুন৷

#to_proc

প্রতীক, হ্যাশ এবং পদ্ধতিগুলিকে তাদের #to_proc ব্যবহার করে procs-এ রূপান্তর করা যেতে পারে পদ্ধতি এটির প্রায়শই দেখা যায় একটি চিহ্ন থেকে একটি পদ্ধতিতে তৈরি একটি প্রোক পাস করছে৷

[1,2,3].map(&:to_s)
[1,2,3].map {|i| i.to_s }
[1,2,3].map {|i| i.send(:to_s) }

এই উদাহরণটি #to_s কল করার তিনটি সমতুল্য উপায় দেখায় অ্যারের প্রতিটি উপাদানের উপর। প্রথমটিতে, একটি অ্যাম্পারস্যান্ডের সাথে উপসর্গযুক্ত একটি প্রতীক পাস করা হয়, যা স্বয়ংক্রিয়ভাবে এটিকে #to_proc কল করে একটি proc-এ রূপান্তরিত করে। পদ্ধতি শেষ দুটি দেখায় যে proc দেখতে কেমন হতে পারে৷

class Symbol
  def to_proc
    Proc.new { |i| i.send(self) }
  end
end

যদিও এটি একটি সরলীকৃত উদাহরণ, Symbol#to_proc এর বাস্তবায়ন হুডের নিচে কি ঘটছে তা দেখায়। পদ্ধতিটি একটি proc ফেরত দেয় যা একটি আর্গুমেন্ট নেয় এবং self পাঠায় এটা যেহেতু self এই প্রসঙ্গে চিহ্ন, এটি Integer#to_s কল করে পদ্ধতি।

ল্যাম্বডাস

ল্যাম্বডাস মূলত কিছু স্বতন্ত্র কারণের সাথে প্রোকস। এগুলি দুটি উপায়ে "নিয়মিত" পদ্ধতির মতো:তারা কল করার সময় পাস করা আর্গুমেন্টের সংখ্যা প্রয়োগ করে এবং তারা "স্বাভাবিক" রিটার্ন ব্যবহার করে।

একটি ল্যাম্বডাকে কল করার সময় যেটি একটি ছাড়াই একটি যুক্তির প্রত্যাশা করে, অথবা আপনি যদি একটি ল্যাম্বডাকে একটি যুক্তি দেন যা এটি আশা করে না, রুবি একটি ArgumentError উত্থাপন করে .

irb> lambda (a) { a }.call
ArgumentError: wrong number of arguments (given 0, expected 1)
        from (irb):8:in `block in irb_binding'
        from (irb):8
        from /Users/jeff/.asdf/installs/ruby/2.3.0/bin/irb:11:in `<main>'

এছাড়াও, একটি ল্যাম্বডা রিটার্ন কীওয়ার্ডের সাথে একইভাবে আচরণ করে যেভাবে একটি পদ্ধতি করে। একটি proc কল করার সময়, প্রোগ্রামটি proc-এর কোড ব্লকে নিয়ন্ত্রণ প্রদান করে। সুতরাং, যদি proc রিটার্ন করে, বর্তমান সুযোগ ফিরে আসে। যদি একটি ফাংশনের ভিতরে একটি proc কল করা হয় এবং return কল করে , ফাংশনটি অবিলম্বে ফিরে আসে।

def return_from_proc
  a = Proc.new { return 10 }.call
  puts "This will never be printed."
end

এই ফাংশনটি proc-এ নিয়ন্ত্রণ প্রদান করবে, তাই যখন এটি ফেরত আসে, ফাংশনটি ফিরে আসে। এই উদাহরণে ফাংশনটিকে কল করলে কখনই আউটপুট প্রিন্ট হবে না এবং 10 ফেরত আসবে না।

def return_from_lambda
  a = lambda { return 10 }.call
  puts "The lambda returned #{a}, and this will be printed."
end

ল্যাম্বডা ব্যবহার করার সময়, এটি হবে মুদ্রিত করা return কল করা হচ্ছে ল্যাম্বডায় return কল করার মতো আচরণ করবে একটি পদ্ধতিতে, তাই a ভেরিয়েবল 10 দিয়ে পপুলেট করা হয় এবং লাইনটি কনসোলে প্রিন্ট করা হয়।

ব্লক, প্রক্স এবং ল্যাম্বডাস

এখন যেহেতু আমরা ব্লক, প্রোকস এবং ল্যাম্বডাস উভয় ক্ষেত্রেই চলে এসেছি, আসুন আবার জুম আউট করি এবং তুলনাটি সংক্ষিপ্ত করি।

  • কোডের বিট ফাংশনে পাঠানোর জন্য রুবিতে ব্লকগুলি ব্যাপকভাবে ব্যবহৃত হয়। yield ব্যবহার করে কীওয়ার্ড, একটি ব্লককে প্রোক-এ রূপান্তর না করেই নিহিতভাবে পাস করা যেতে পারে।
  • অ্যাম্পারস্যান্ডের সাথে উপসর্গযুক্ত প্যারামিটার ব্যবহার করার সময়, একটি পদ্ধতিতে একটি ব্লক পাস করার ফলে পদ্ধতির প্রসঙ্গে একটি proc হয়। Procs ব্লকের মত আচরণ করে, কিন্তু সেগুলি একটি ভেরিয়েবলে সংরক্ষণ করা যেতে পারে।
  • ল্যাম্বডাস হল প্রক্স যা পদ্ধতির মতো আচরণ করে, যার অর্থ তারা আর্যটি প্রয়োগ করে এবং তাদের মূল সুযোগের পরিবর্তে পদ্ধতি হিসাবে ফিরে আসে।

এটি রুবিতে বন্ধ হওয়ার বিষয়ে আমাদের দৃষ্টিভঙ্গি শেষ করে। লেকসিকাল স্কোপ এবং বাইন্ডিং এর মত ক্লোজার সম্পর্কে জানার জন্য আরও অনেক কিছু আছে, তবে আমরা ভবিষ্যতের পর্বের জন্য এটি রাখব। ইতিমধ্যে, অনুগ্রহ করে আমাদের জানান যে আপনি রুবি ম্যাজিকের ভবিষ্যতের কিস্তিতে কি পড়তে চান, বন্ধ হবে বা অন্যথায় @AppSignal-এ।


  1. RuboCop সহ রুবি কোড লিন্টিং এবং স্বয়ংক্রিয় ফর্ম্যাটিং

  2. রুবিতে ল্যাম্বডাস ব্যবহার করা

  3. কখন ফ্রিজ এবং হিমায়িত ব্যবহার করবেন? রুবিতে

  4. Logger এবং Lograge সঙ্গে রুবি লগ ইন করুন