একটি সার্কুলার বাফার, সাইক্লিক বাফার বা রিং বাফার হলো এমন একটি ডেটা স্ট্রাকচার, যা একটি মাত্র নির্দিষ্ট আকারের বাফার ব্যবহার করে, যেন এর দুই প্রান্ত পরস্পরের সাথে জোড়া লাগানো।
একটি সার্কুলার বাফার শুরুতে খালি থাকে এবং এর একটি পূর্বনির্ধারিত দৈর্ঘ্য থাকে। যেমন, এটি ৭টি এলিমেন্টের একটি বাফার:
[ ][ ][ ][ ][ ][ ][ ]
ধরে নিন বাফারের মাঝখানে একটি 1 লেখা হলো (সার্কুলার বাফারে ঠিক কোথা থেকে শুরু হচ্ছে সেটি গুরুত্বপূর্ণ নয়):
[ ][ ][ ][1][ ][ ][ ]
এরপর ধরে নিন আরও দুটি এলিমেন্ট, 2 ও 3, যোগ করা হলো, যেগুলো 1-এর পরে যুক্ত হয়:
[ ][ ][ ][1][2][3][ ]
এরপর যদি বাফার থেকে দুটি এলিমেন্ট সরিয়ে ফেলা হয়, তবে বাফারের ভেতরের সবচেয়ে পুরোনো মানগুলো সরিয়ে ফেলা হয়। এক্ষেত্রে যে দুটি এলিমেন্ট সরানো হলো সেগুলো 1 ও 2, ফলে বাফারে শুধু একটি 3 পড়ে থাকে:
[ ][ ][ ][ ][ ][3][ ]
বাফারে যদি ৭টি এলিমেন্ট থাকে, তবে তা পুরোপুরি ভরে যায়:
[5][6][7][8][9][3][4]
বাফার ভরে গেলে একটি এরর উত্থাপিত হবে, যা ক্লায়েন্টকে জানাবে যে কোনো স্লট খালি না হওয়া পর্যন্ত আর লেখা যাবে না।
বাফার ভরে গেলে ক্লায়েন্ট চাইলে জোর করে লেখার মাধ্যমে সবচেয়ে পুরোনো ডেটা ওভাররাইট করতে পারে। এক্ষেত্রে আরও দুটি এলিমেন্ট, A ও B, যোগ করা হয় এবং তারা 3 ও 4 ওভাররাইট করে ফেলে:
[5][6][7][8][9][A][B]
3 ও 4-কে A ও B দিয়ে প্রতিস্থাপন করা হয়েছে, ফলে এখন বাফারের সবচেয়ে পুরোনো ডেটা হলো 5। সবশেষে, যদি দুটি এলিমেন্ট সরিয়ে ফেলা হয়, তবে ফেরত দেওয়া হবে 5 ও 6, ফলে বাফারটি হয়:
[ ][ ][7][8][9][A][B]
যেহেতু জায়গা খালি আছে, তাই ক্লায়েন্ট আবারও ওভাররাইট ব্যবহার করে C ও D রাখতে চাইলে আগে যেখানে 5 ও 6 রাখা হয়েছিল সেই জায়গাটিই ব্যবহার হবে, 7 ও 8-এর জায়গা নয়। 7 এখনও সবচেয়ে পুরোনো এলিমেন্ট এবং বাফার আবারও ভরে গেল।
[C][D][7][8][9][A][B]
T টাইপের এলিমেন্ট ধারণ করে এমন একটি প্যারামেট্রিক কম্পোজিট টাইপ CircularBuffer{T} ডিফাইন করুন এবং একটি কনস্ট্রাক্টর লিখুন
CircularBuffer{T}(capacity::Integer) where {T} -> CircularBuffer{T}
যা সর্বোচ্চ capacity সংখ্যক এলিমেন্ট সংরক্ষণ করতে পারে এমন একটি ইনস্ট্যান্স তৈরি করে।
Base থেকে নিচের ফাংশনগুলি CircularBuffer-গুলির উপর কাজ করার জন্য এক্সটেন্ড করুন:
Base.push!(cb::CircularBuffer, item; overwrite::Bool=false): cb-এর শেষে item এলিমেন্টটি যোগ করুন, তারপর cb রিটার্ন করুন। cb যদি ইতিমধ্যে পূর্ণ থাকে, তাহলে overwrite false (ডিফল্ট মান) হলে একটি BoundsError থ্রো করুন; অন্যথায় overwrite true হলে item-এর জন্য জায়গা করতে প্রথম এলিমেন্টটি সরিয়ে ফেলুন।Base.popfirst!(cb::CircularBuffer): cb-এর প্রথম এলিমেন্টটি সরিয়ে ফেলুন এবং রিটার্ন করুন।Base.empty!(cb::CircularBuffer): cb থেকে সব এলিমেন্ট সরিয়ে ফেলুন, তারপর খালি cb রিটার্ন করুন।এই অনুশীলনীটি বেশ বড় এবং সম্ভাব্য জটিল, আর তাই এটি মেন্টর করা আরও চ্যালেঞ্জিং ও সময়সাপেক্ষ করে তোলে। আপনার মেন্টরকে সহায়তা করতে, অনুশীলনীর প্রথম অংশের জন্য আপনার মেন্টর আপনার সমাধান রিভিউ না করা পর্যন্ত অনুগ্রহ করে বোনাস অনুশীলনগুলির জন্য কোড জমা দেবেন না।
DataStructures.jl প্যাকেজের CircularBuffer-এর টেস্ট পাস করতে আপনার CircularBuffer-কে এক্সটেন্ড করুন। Exercism-এর এই অনুশীলনীর জন্য প্রদত্ত টেস্টে এগুলি অন্তর্ভুক্ত আছে কিন্তু নিষ্ক্রিয়; এই টেস্টগুলি সক্রিয় করতে, আপনার ফাইল বা নোটবুকে টপ-লেভেল লাইন enable_bonus_tests = true যোগ করুন।
এই টেস্টগুলি পাস করতে আপনাকে CircularBuffer-কে AbstractVector-এর একটি সাবটাইপ হিসেবে ডিক্লেয়ার করতে হবে এবং দুটি ফাংশন ডিফাইন করতে হবে:
capacity(cb::CircularBuffer): cb-এর ক্যাপাসিটি রিটার্ন করুন।isfull(cb::CircularBuffer): cb পূর্ণ হলে true রিটার্ন করুন।এরপর আপনাকে নিশ্চিত করতে হবে যে Base-এর নিচের ফাংশনগুলি CircularBuffer-গুলির সাথে সঠিকভাবে কাজ করে: append!, empty!, pop!, pushfirst, setindex!, collect, eltype, first, getindex, isempty, iterate, last, length এবং size।
ইঙ্গিত: এই ফাংশনগুলির সবগুলি এক্সটেন্ড করার প্রয়োজন আপনার নেই, এবং করা উচিতও নয়! CircularBuffer-কে AbstractVector-এর একটি সাবটাইপ হিসেবে ডিফাইন করলে, AbstractVector-এর জন্য ডিফাইন করা জেনেরিক ফাংশনগুলি এখন ইনপুট হিসেবে CircularBuffer গ্রহণ করবে। Julia-এর ম্যানুয়ালে ইন্টারফেস বিভাগটি দেখুন:
Julia-এর অনেক শক্তি ও সম্প্রসারণযোগ্যতা এসেছে অনানুষ্ঠানিক ইন্টারফেসের একটি সংগ্রহ থেকে। একটি কাস্টম টাইপের জন্য কয়েকটি নির্দিষ্ট মেথড এক্সটেন্ড করলে, সেই টাইপের অবজেক্টগুলি কেবল ওই কার্যকারিতাগুলিই পায় না, বরং সেই আচরণের উপর সাধারণভাবে গড়ে ওঠা অন্য মেথডেও ব্যবহার করা যায়।
কোন ফাংশনগুলি এক্সটেন্ড করতে হবে তা বুঝতে এবং ফাংশন ডেফিনিশন দেখতে আপনাকে Julia-এর Base মডিউলের সোর্স কোড দেখে নিতে হবে। একটি ফাংশন কলের সংশ্লিষ্ট কোড খুঁজে বের করতে, আপনি @which ম্যাক্রো ব্যবহার করে নির্দিষ্ট মেথডটি শনাক্ত করতে পারেন, যেখানে ওই ফাংশন কলটি ডিসপ্যাচ হয়। এটি আপনাকে দেখায় কোন ফাইল ও লাইন নম্বরে ওই মেথডটি ডিফাইন করা আছে (IJulia-র মাধ্যমে একটি Jupyter Notebook-এ এটি এমনকি GitHub-এ সংশ্লিষ্ট কোডের একটি লিংকও দেয়)।
আপনি যদি REPL-এ কাজ করেন, তাহলে আপনার ডিফল্ট টেক্সট এডিটরে সংশ্লিষ্ট ফাইল ও লাইন খুলতে @edit ম্যাক্রো ব্যবহার করা পছন্দ করতে পারেন।
Exercism-এ সাইন আপ করুন, Julia ট্র্যাকের 35টি কনসেপ্ট128টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।
এই ভিডিওতে আমরা সার্কুলার বাফার দেখব: এগুলো কী, কোথায় ব্যবহৃত হয় এবং এর বিভিন্ন ইমপ্লিমেন্টেশন, যার মধ্যে রয়েছে কিউ, স্ট্যাটিক ও ডাইনামিক অ্যারে, ইমিউটেবল ডেটা স্ট্রাকচার এবং একটি মজার এজেন্ট-ভিত্তিক ইমপ্লিমেন্টেশন।