Darsliklar Ruby Block, Proc va Lambda

Ruby Ruby Block, Proc va Lambda

Block, Proc va Lambda — Ruby’ning eng o’ziga xos xususiyatlaridan biri: kodning o’zini qiymat sifatida metodga uzatish. Oldingi darslarda each do |x| ... end kabi yozuvlarni ko’p ishlatdik — ular aslida block edi. Endi bu mexanizmni chuqurroq tushunamiz.

Block / Proc / Lambdauchalasi ham kod bo’lagini qiymat sifatida uzatish vositasi. Block — metodga bevosita biriktiriladigan, obyekt bo’lmagan kod bo’lagi. Proc va Lambda esa block’ning o’zgaruvchida saqlanadigan, mustaqil obyekt shakli (Proc klassi).

Block va yield

Har qanday metod yield kalit so’zi orqali o’ziga berilgan blokni ishga tushirishi mumkin:

def ikki_marta
  yield
  yield
end

ikki_marta { puts "Salom, UzbekDevs!" }
Salom, UzbekDevs!
Salom, UzbekDevs!

ikki_marta metodi o’zi hech qanday parametr olmaydi, lekin unga { ... } orqali berilgan blok yield chaqirilganda ishga tushadi — bunda ikki marta. Aynan shu mexanizm asosida each, map kabi barcha standart metodlar ishlaydi.

block_given? va blokka qiymat uzatish

Blok berilgan-berilmaganini oldindan tekshirish, va unga argument uzatish mumkin:

def sonni_qayta_ishla(son)
  if block_given?
    yield(son)
  else
    son
  end
end

puts sonni_qayta_ishla(5) { |x| x * 10 }
puts sonni_qayta_ishla(5)
50
5

yield(son) — blokka son qiymatini argument sifatida uzatadi, blok ichida u x nomi bilan qabul qilinadi. block_given? blok berilmagan holatda metodning xatosiz ishlashini ta’minlaydi.

Proc — blokni obyekt sifatida saqlash

Blokni keyinroq ishlatish yoki boshqa metodga uzatish uchun Proc obyektiga aylantirish mumkin:

ikkilantir = Proc.new { |son| son * 2 }

puts ikkilantir.call(5)
puts ikkilantir.(10)
puts ikkilantir[15]
10
20
30

Proc.new blokni o’zgaruvchida saqlanadigan obyektga aylantiradi. Uni chaqirishning uch usuli bor: .call(5), .(10) yoki [15] — barchasi bir xil natija beradi, tanlov faqat uslub masalasi.

Lambda — qat’iyroq Proc

Lambda — Proc’ning maxsus turi, lekin ikki muhim jihatda farq qiladi: argumentlar sonini qat’iy tekshiradi va return boshqacha ishlaydi:

kvadrat = lambda { |son| son ** 2 }
kvadrat2 = ->(son) { son ** 2 }

puts kvadrat.call(4)
puts kvadrat2.call(5)
16
25

->(son) { ... } — lambda yaratishning qisqa (“stabby lambda”) shakli. Ikkalasi ham lambda { |son| ... } bilan bir xil natija beradi.

Proc va Lambda — muhim farq

Eng nozik, lekin muhim farq — return xatti-harakati va argument qat’iyligida:

def proc_sinov
  ichki_proc = Proc.new { return 100 }
  ichki_proc.call
  puts "Bu qator bajarilmaydi"
end

def lambda_sinov
  ichki_lambda = lambda { return 100 }
  ichki_lambda.call
  puts "Bu qator bajariladi"
end

puts proc_sinov
lambda_sinov
100
Bu qator bajariladi

Proc ichidagi return butun metoddan chiqib ketadi — shuning uchun proc_sinov da “Bu qator bajarilmaydi” hech qachon chop etilmaydi. Lambda ichidagi return esa faqat lambdaning o’zidan chiqadi, metod ijrosi davom etadi. Shu sababli amaliyotda alohida, mustaqil mantiq uchun ko’pincha lambda afzal ko’riladi.