রুবিকে এমন একটি শক্তিশালী, গতিশীল ভাষা করে তোলে তার মূলে রয়েছে গণনাকারীরা৷ এবং অলস গণনাকারীরা আপনাকে অত্যন্ত বড় সংগ্রহের সাথে দক্ষতার সাথে কাজ করার অনুমতি দিয়ে এটিকে আরও এক ধাপ এগিয়ে নিয়ে যায়।
ফাইলগুলি - দেখা যাচ্ছে - লাইন বা অক্ষরের বড় সংগ্রহ। তাই অলস গণনাকারীরা তাদের সাথে কিছু খুব আকর্ষণীয় এবং শক্তিশালী জিনিস করা সম্ভব করে তোলে।
যাইহোক একটি গণনাকারী কি?
প্রতিবার আপনি each
এর মত একটি পদ্ধতি ব্যবহার করেন , আপনি একটি গণনাকারী তৈরি করুন। এই কারণেই আপনি [1,2,3].map { ... }.reduce { ... }
এর মতো পদ্ধতিগুলিকে একসাথে চেইন করতে পারেন . আপনি নীচের উদাহরণে আমি কি বলতে চাই তা দেখতে পারেন। each
কল করা হচ্ছে একটি গণনাকারী প্রদান করে, যা আমি অন্যান্য পুনরাবৃত্তিমূলক ক্রিয়াকলাপগুলি করতে ব্যবহার করতে পারি৷
# I swiped this code from Ruby's documentation https://ruby-doc.org/core-2.2.0/Enumerator.html
enumerator = %w(one two three).each
puts enumerator.class # => Enumerator
enumerator.each_with_object("foo") do |item, obj|
puts "#{obj}: #{item}"
end
# foo: one
# foo: two
# foo: three
অলস গণনাকারীরা বড় সংগ্রহের জন্য
সাধারণ গণনাকারীদের বড় সংগ্রহে সমস্যা হয়। কারণ হল যে প্রতিটি পদ্ধতি আপনি কল করে পুরো সংগ্রহে পুনরাবৃত্তি করতে চায়। আপনি নিম্নলিখিত কোডটি চালিয়ে এটি নিজের জন্য দেখতে পারেন:
# This code will "hang" and you'll have to ctrl-c to exit
(1..Float::INFINITY).reject { |i| i.odd? }.map { |i| i*i }.first(5)
reject
পদ্ধতিটি চিরতরে চলে, কারণ এটি কখনই একটি অসীম সংগ্রহের উপর পুনরাবৃত্তি শেষ করতে পারে না।
কিন্তু একটি ছোট সংযোজন সহ, কোডটি পুরোপুরি চলে। যদি আমি কেবল lazy
কল করি পদ্ধতি, রুবি স্মার্ট জিনিসটি করে এবং শুধুমাত্র গণনার জন্য প্রয়োজনীয় যতগুলি পুনরাবৃত্তি করে। এই ক্ষেত্রে এটি মাত্র 10টি সারি, যা অসীমের চেয়ে উল্লেখযোগ্যভাবে ছোট৷
(1..Float::INFINITY).lazy.reject { |i| i.odd? }.map { |i| i*i }.first(5)
#=> [4, 16, 36, 64, 100]
মবি ডিকের ছয় হাজার কপি
এই ফাইল কৌশলগুলি পরীক্ষা করার জন্য, আমাদের একটি বড় ফাইলের প্রয়োজন হবে। একটি এত বড় যে কোনও "অলস হতে ব্যর্থতা" স্পষ্ট হবে৷
৷আমি প্রজেক্ট গুটেনবার্গ থেকে মবি ডিক ডাউনলোড করেছি এবং তারপরে 100 কপি সম্বলিত একটি পাঠ্য ফাইল তৈরি করেছি। যে যথেষ্ট বড় ছিল না, যদিও. তাই আমি এটি প্রায় 6,000-এ উন্নীত করেছি। এর মানে হল যে এই মুহূর্তে আমি সম্ভবত বিশ্বের একমাত্র লোক যার কাছে মবি ডিকের 6,000 কপি সহ একটি টেক্সট ফাইল রয়েছে। এটা নম্র ধরনের. কিন্তু আমি বিচ্ছিন্ন হই।
আমি মবি ডিক ডাউনলোড করেছি এবং খেলার জন্য একটি বড় ফাইল পেতে এটি কয়েক হাজার বার নকল করেছি৷ সিনট্যাক্স ব্যাশ না. এটা মাছের খোসা। আমি মনে করি একমাত্র আমিই এটি ব্যবহার করি৷৷
কিভাবে একটি ফাইলের জন্য গণনাকারী পেতে হয়
এখানে একটি দুর্দান্ত রুবি কৌশল যা আপনি সম্ভবত ব্যবহার করেছেন, এমনকি যদি আপনি জানেন না যে আপনি এটি ব্যবহার করছেন। রুবিতে প্রায় যেকোনো পদ্ধতি যা একটি সংগ্রহের উপর পুনরাবৃত্তি করে তা আপনাকে একটি গণনাকারী বস্তু ফিরিয়ে দেবে যদি আপনি এটিকে একটি ব্লকের মধ্যে না দিয়ে কল করেন। এর মানে কি?
এই উদাহরণ বিবেচনা করুন. আমি একটি ফাইল খুলতে পারি, এবং প্রতিটি লাইন মুদ্রণ করতে প্রতিটি লাইন ব্যবহার করতে পারি। কিন্তু যদি আমি এটিকে ব্লক ছাড়াই কল করি, আমি একটি গণনাকারী পাই। আগ্রহের পদ্ধতি হল each_line
, each_char
এবং each_codepoint
.
File.open("moby.txt") do |f|
# Print out each line in the file
f.each_line do |l|
puts l
end
# Also prints out each line in the file. But it does it
# by calling `each` on the Enumerator returned by `each_line`
f.each_line.each do |l|
puts l
end
end
এই দুটি উদাহরণ দেখতে প্রায় অভিন্ন, কিন্তু দ্বিতীয়টিতে আশ্চর্যজনক ক্ষমতা আনলক করার চাবি রয়েছে .
একটি ফাইলের গণনাকারী ব্যবহার করা
একবার আপনার কাছে একটি গণনাকারী আছে যা একটি ফাইলের সমস্ত লাইন "ধারণ করে", আপনি সেই লাইনগুলিকে টুকরো টুকরো করে কাটতে পারেন ঠিক যেমন আপনি যেকোনো রুবি অ্যারে দিয়ে করতে পারেন। এখানে কয়েকটি উদাহরণ দেওয়া হল৷
৷file.each_line.each_with_index.map { |line, i| "Line #{ i }: #{ line }" }[3, 10]
file.each_line.select { |line| line.size == 9 }.first(10)
file.each_line.reject { |line| line.match /whale/i }
এটি সত্যিই দুর্দান্ত, তবে এই উদাহরণগুলির একটি বড় সমস্যা রয়েছে। তারা সবাই এটির উপর পুনরাবৃত্তি করার আগে পুরো ফাইলটিকে মেমরিতে লোড করে। মবি ডিকের 6,000 কপি সমন্বিত একটি ফাইলের জন্য, ব্যবধান লক্ষণীয়৷
ফাইলের লাইনগুলি অলসভাবে লোড করা হচ্ছে
যদি আমরা "তিমি" শব্দের প্রথম 10টি উদাহরণের জন্য একটি বড় টেক্সট ফাইল স্ক্যান করি তবে 10 তম ঘটনাটি দেখার জন্য সত্যিই কোন প্রয়োজন নেই। সৌভাগ্যবশত রুবির গণনাকারীদের এটা করতে বলা সহজ। আপনি শুধু "অলস" কীওয়ার্ড ব্যবহার করেন৷
৷নীচের উদাহরণগুলিতে, আমরা কিছু সুন্দর পরিশীলিত জিনিস করতে অলস লোডিংয়ের সুবিধা গ্রহণ করি৷
File.open("moby.txt") do |f|
# Get the first 3 lines with the word "whale"
f.each_line.lazy.select { |line| line.match(/whale/i) }.first(3)
# Go back to the beginning of the file.
f.rewind
# Prepend the line number to the first three lines
f.each_line.lazy.each_with_index.map do |line, i|
"LINE #{ i }: #{ line }"
end.first(3)
f.rewind
# Get the first three lines containing "whale" along with their line numbers
f.each_line.lazy.each_with_index.map { |line, i| "LINE #{ i }: #{ line }" }.select { |line| line.match(/whale/i) }.first(3)
end
এটি শুধু ফাইলের জন্য নয়
সকেট, পাইপ, সিরিয়াল পোর্ট - এগুলি আইও ক্লাস ব্যবহার করে রুবিতে উপস্থাপন করা হয়। এর মানে হল তাদের সবার each_line
আছে , each_char
এবং each_codepoint
পদ্ধতি তাই আপনি তাদের সবার জন্য এই কৌশলটি ব্যবহার করতে পারেন। বেশ ঝরঝরে!
এটি জাদু নয়
দুর্ভাগ্যবশত, অলস গণনাকারীরা তখনই জিনিসের গতি বাড়ে যদি আপনি যে কাজটি সম্পন্ন করার চেষ্টা করছেন তার জন্য পুরো ফাইলটি পড়ার প্রয়োজন না হয়। আপনি যদি এমন একটি শব্দ খুঁজছেন যা শুধুমাত্র বইয়ের শেষ পৃষ্ঠায় দেখা যায়, তাহলে সেটি খুঁজে পেতে আপনাকে পুরো বইটি পড়তে হবে। কিন্তু সেক্ষেত্রে এই পদ্ধতিটি অ-গণনাকারী পদ্ধতির চেয়ে ধীর হওয়া উচিত নয়।