আজ আমি কর্মক্ষমতা উন্নত করার জন্য আমার প্রিয় কৌশলগুলির একটি সম্পর্কে কথা বলতে চেয়েছিলাম। এটি একটি সহজ সামান্য পারফরম্যান্স জয়ের একটি উৎস যা অবশেষে যোগ করে, এবং শুধুমাত্র মাঝে মাঝে আপনার অ্যাপ্লিকেশনকে ধোঁয়াটে ধ্বংসস্তূপের স্তূপে কমিয়ে দেয়। শুধুমাত্র খুব মাঝে মাঝে।
এই কৌশলটিকে "স্মরণীয়করণ" বলা হয়। $10 কম্পিউটার-সায়েন্স শব্দ থাকা সত্ত্বেও, এর সহজ অর্থ হল, প্রতিবার যখন আপনি একটি মেথড কল করেন একই কাজ করার পরিবর্তে, আপনি রিটার্ন মানটিকে একটি ভেরিয়েবলে সংরক্ষণ করুন এবং পরিবর্তে এটি ব্যবহার করুন।
সিউডোকোডে এটি দেখতে কেমন তা এখানে:
def my_method
@memo = <work> if @memo is undefined
return @memo
end
এবং এখানে আপনি রুবি এটা কিভাবে. এটি সবচেয়ে শক্তিশালী পন্থা, কিন্তু এটি বেশ ভার্বস। অন্যান্য, আরও সংক্ষিপ্ত পদ্ধতি রয়েছে যা আমরা পরে আলোচনা করব।
class MyClass
def my_method
unless defined?(@my_method)
@my_method = begin
# Do your calculation, database query
# or other long-running thing here.
end
end
@my_method
end
end
উপরের কোডটি তিনটি জিনিস করে:
-
@my_method
নামে একটি ইনস্ট্যান্স ভেরিয়েবল আছে কিনা তা পরীক্ষা করে দেখুন . - যদি থাকে তবে এটি কিছু কাজ করে এবং ফলাফলটিকে
@my_method
এ সংরক্ষণ করে . - এটি
@my_method
প্রদান করে
আমাদের কাছে my_method
নামে একটি পদ্ধতি এবং একটি ইনস্ট্যান্স ভেরিয়েবল উভয়ই রয়েছে এই সত্যটি দ্বারা বিভ্রান্ত হবেন না . আমি আমার ভেরিয়েবলের নাম দিতে পারতাম, কিন্তু মেমোাইজ করা পদ্ধতির নামানুসারে এটির নাম রাখা প্রচলিত।
একটি শর্টহ্যান্ড সংস্করণ
উপরের কোডের সাথে একটি সমস্যা হল যে এটি কিছুটা কষ্টকর। এই কারণে, আপনি একটি শর্টহ্যান্ড সংস্করণ দেখতে পাওয়ার সম্ভাবনা অনেক বেশি যা প্রায় একই জিনিস করে:
class MyClass
def my_method1
@my_method1 ||= some_long_calculation
end
def my_method2
@my_method2 ||= begin
# The begin-end block lets you easily
# use multiple lines of code here.
end
end
end
এই দুটিই রুবির a ||= b
ব্যবহার করে অপারেটর, যা a || (a = b)
, যা নিজেই এর জন্য কম-বেশি শর্টহ্যান্ড:
# You wouldn't use return like this in real life.
# I'm just using it to express to beginners the idea
# that the conditional evaluates to whatever winds up in `a`.
if a
return a
else
a = b
return a
end
বাগের একটি উৎস
আপনি যদি খুব ঘনিষ্ঠভাবে মনোযোগ দেন তবে আপনি লক্ষ্য করেছেন যে শর্টহ্যান্ড সংস্করণগুলি মেমো ভেরিয়েবলের অস্তিত্ব পরীক্ষা করার পরিবর্তে এর "সত্যতা" মূল্যায়ন করে। এটি শর্টহ্যান্ড সংস্করণের অন্যতম প্রধান সীমাবদ্ধতার উত্স:এটি কখনও nil
মনে রাখে না অথবা false
.
অনেকগুলি ব্যবহারের ক্ষেত্রে রয়েছে যেখানে এটি গুরুত্বপূর্ণ নয়। তবে এটি সেই বিরক্তিকর তথ্যগুলির মধ্যে একটি যা আপনি যখনই স্মৃতিচারণ করছেন তখন আপনাকে আপনার মনের পিছনে রাখতে হবে।
আর্গুমেন্ট সহ মনে রাখার পদ্ধতি
এখন পর্যন্ত আমরা শুধুমাত্র একক মান মেমোইজিং নিয়ে কাজ করেছি। কিন্তু অনেক ফাংশন সব সময় একই ফলাফল দেয় না। আসুন প্রযুক্তিগত সাক্ষাত্কারের একটি পুরানো পছন্দের দিকে নজর দেওয়া যাক:ফিবোনাচি সিকোয়েন্স।
আপনি রুবিতে ফিবোনাচ্চি ক্রমকে পুনরাবৃত্তভাবে গণনা করতে পারেন এভাবে:
class Fibonacci
def self.calculate(n)
return n if n == 0 || n == 1
calculate(n - 1) + calculate(n - 2)
end
end
Fibonacci.calculate(10) # => 55
এই বাস্তবায়নের সমস্যা হল এটি অদক্ষ। এটি প্রমাণ করতে, আসুন একটি print
যোগ করি n
এর মান দেখার বিবৃতি .
class Fibonacci
def self.calculate(n)
print "#{ n } "
return n if n == 0 || n == 1
calculate(n - 1) + calculate(n - 2)
end
end
Fibonacci.calculate(4)
# Outputs: 4 3 2 1 0 1 2 1 0
আপনি দেখতে পাচ্ছেন, calculate
n
এর একই মানগুলির সাথে বারবার কল করা হচ্ছে . আসলে calculate
করতে কলের সংখ্যা n
এর সাথে দ্রুতগতিতে বৃদ্ধি পেতে চলেছে৷ .
এর চারপাশে একটি উপায় হল calculate
এর ফলাফলগুলি মনে রাখা . এটি করা আমাদের কভার করা অন্যান্য মেমোাইজেশন উদাহরণ থেকে খুব আলাদা নয়।
class Fibonacci
def self.calculate(n)
@calculate ||= {}
@calculate[n] ||= begin
print "#{ n } "
if n == 0 || n == 1
n
else
calculate(n - 1) + calculate(n - 2)
end
end
end
end
Fibonacci.calculate(4)
# Outputs: 4 3 2 1 0
এখন আমরা calculate
মুখস্থ করেছি , n
দিয়ে কলের সংখ্যা আর দ্রুতগতিতে বাড়ে না .
Fibonacci.calculate(20)
# Outputs: 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
অবৈধতা
মেমোাইজেশন অনেকটা ক্যাশিংয়ের মতোই, মেমোাইজ করা ফলাফলগুলি স্বয়ংক্রিয়ভাবে মেয়াদ শেষ হয় না, এবং সেট করার পরে সেগুলি পরিষ্কার করার সহজ উপায় নেই।
ফিবোনাচি সিকোয়েন্স জেনারেটরের মতো ব্যবহারের ক্ষেত্রে এটি খুব কমই গুরুত্বপূর্ণ। Fibonacci.calculate(10)
সবসময় একই ফলাফল ফিরে আসবে। কিন্তু অন্যান্য ব্যবহারের ক্ষেত্রে এটি গুরুত্বপূর্ণ।
উদাহরণস্বরূপ, আপনি এইরকম কোড দেখতে পারেন:
# Not the best idea
class User
def full_name
@full_name ||= [first_name, last_name].join(" ")
end
end
ব্যক্তিগতভাবে, আমি এখানে মেমোাইজেশন ব্যবহার করব না কারণ যদি প্রথম বা শেষ নাম পরিবর্তন করা হয়, তাহলে পুরো নাম আপডেট নাও হতে পারে।
একটি জায়গা যেখানে আপনি একটু বেশি শিথিল হতে পারেন তা হল রেল কন্ট্রোলারের ভিতরে। এইরকম কোড দেখা খুবই সাধারণ:
class ApplicationController
def current_user
@current_user ||= User.find(...)
end
end
এটি ঠিক আছে, কারণ প্রতিটি ওয়েব অনুরোধের পরে কন্ট্রোলার ইনস্ট্যান্সটি ধ্বংস হয়ে যায়। এটি অসম্ভাব্য যে বর্তমানে লগ-ইন করা ব্যবহারকারী যেকোনো স্বাভাবিক অনুরোধের সময় পরিবর্তিত হবে।
অ্যাকশনকেবলের মতো স্ট্রিমিং সংযোগগুলির সাথে কাজ করার সময় আপনাকে আরও সতর্ক হতে হবে। আমি জানি না আমি এটা ব্যবহার করিনি.
অতিব্যবহার
পরিশেষে আমি মনে করি যে আমার উল্লেখ করা উচিত যে যেকোন কিছুর মতো, স্মৃতিকরণকে অনেক দূরে নিয়ে যাওয়া সম্ভব। এটি এমন একটি কৌশল যা সত্যিই শুধুমাত্র ব্যয়বহুল ক্রিয়াকলাপের ক্ষেত্রে প্রয়োগ করা উচিত যা মেমো ভেরিয়েবলের সারাজীবনে কখনই পরিবর্তন হবে না।