হ্যাঁ, তার একটি পরিষ্কার ডেস্ক ছিল। কিন্তু এর কারণ ছিল সে সমস্ত কাগজপত্র ফেলে দিচ্ছিল।
-টেরি প্র্যাচেট, “পঞ্চম হাতি”
কোডিং সহজ, কিন্তু প্রোগ্রামিং কঠিন – অন্তত, যদি আপনি এটি সঠিকভাবে করছেন। এটি কারণ ভাল প্রোগ্রামাররা শুধুমাত্র একটি নির্দিষ্ট সমস্যা সমাধান করার চেষ্টা করছেন না: তারা একটি তৈরি করার চেষ্টা করছেন
বিমূর্ততা যা একটি সাধারণ শ্রেণীর সমস্যার সমাধান করে (এটি সহ)।
লাইন গণনা
বিমূর্ততা তৈরি করা—অর্থাৎ, আপনার সমাধানকে আরও সাধারণ করা—সফ্টওয়্যার ডিজাইনের সম্পূর্ণ শিল্প। এবং সে কারণেই এটি প্রথমে কিছুটা চ্যালেঞ্জের। যখন লোকেরা প্রশ্ন করে “আমার মরিচা প্রকল্পটি কীভাবে গঠন করা উচিত?” অথবা “কখন আমি আমার প্রোগ্রামকে একাধিক মডিউল বা ক্রেটে বিভক্ত করব?”, তারা সত্যিই জিজ্ঞাসা করছে: আমার কী বিমূর্ততা তৈরি করা উচিত এবং কীভাবে তাদের কাজ করা উচিত?
আমার নতুন বই দ্য সিক্রেটস অফ রাস্ট: টুলস-এ, আমরা মরিচা-এ ব্যবহারকারী-বান্ধব API এবং কমান্ড-লাইন সরঞ্জামগুলি তৈরি করতে ভাল বিমূর্ত নকশা ব্যবহার করার উপর ফোকাস করব। আমি একজন সাধারণ মানুষ যে সহজ প্রোগ্রাম পছন্দ করে, এবং এই ধরনের আমরা একসাথে লিখব।
তাই বই থেকে এই উদ্ধৃতিতে, আসুন মরিচা-এ একটি ছোট কিন্তু দরকারী কমান্ড-লাইন টুল ডিজাইন করে আমাদের বিমূর্ততা দক্ষতার একটু অনুশীলন করি: যেটি তার ইনপুটে লাইন গণনা করতে পারে। আপনি হয়তো ইউনিক্স প্রোগ্রাম ব্যবহার করেছেন wc
উদাহরণস্বরূপ, যার একটি লাইন-গণনা মোড রয়েছে:
echo hello | wc -l
1
একটি প্রোটোটাইপ লাইন কাউন্টার
আমরা কি মরিচাতে এমন কিছু তৈরি করতে পারি যা কমবেশি একই জিনিস করে? এর চেষ্টা করা যাক.
cargo new count
আমরা সবকিছু নির্বাণ দ্বারা শুরু করব src/main.rs
ফাইল যদিও আমরা একটি আমদানিযোগ্য লাইব্রেরি ক্রেট তৈরি করার পরিকল্পনা করছি, তবে এটি কী করা উচিত তা জানার আগে আমরা তা করতে পারি না। সুতরাং আসুন প্রথমে কিছু তৈরি করি এবং চলমান করি এবং তারপরে কীভাবে এটিকে পুনরায় ব্যবহারযোগ্য বিমূর্ততায় পরিণত করা যায় সে সম্পর্কে চিন্তা করি।
এখানে একটি মোটামুটি প্রোটোটাইপ যা কাজ করতে পারে:
use std::io::stdin, BufRead;
fn main()
let input = stdin().lock();
let lines = input.lines().count();
println!("lines");
প্রথম কাটা
এর এটা ভেঙ্গে দেওয়া যাক. প্রথম, আমরা কল stdin()
স্ট্যান্ডার্ড ইনপুট পড়ার জন্য একটি হ্যান্ডেল পেতে। তারপর আমরা কল lock()
ফলাফলের উপর, একটি এক্সক্লুসিভ লক পেতে Stdin
বস্তু
এটি এখানে কঠোরভাবে প্রয়োজনীয় নয়, তবে এটি ভাল অনুশীলন। নীতিগতভাবে, একটি প্রোগ্রাম একাধিক থাকতে পারে থ্রেড এক্সিকিউশন যে স্ট্যান্ডার্ড ইনপুট থেকে পড়তে চান, এবং এই সমবর্তী
পড়া বিরোধ হতে পারে। এই প্রাপ্তি মিউটেক্স (“পারস্পরিক একচেটিয়া”) লক নিশ্চিত করে যে যতক্ষণ আমরা লকটি ধরে রাখি ততক্ষণ অন্য কোনও থ্রেড ইনপুট থেকে পড়তে পারে না।
দ StdinLock
অবজেক্ট আমরা সুবিধাজনকভাবে প্রয়োগ করি BufRead
বৈশিষ্ট্য, যা আমাদের দেয় একটি বাফার
ইনপুটে পাঠক। এটি এত বেশি নয় যে আমাদের এই ক্ষেত্রে ইনপুটটি বাফার করতে হবে, যদিও এটি সাধারণত কার্য সম্পাদনে সহায়তা করে। এটা আরো যে
BufRead
বৈশিষ্ট্য আমাদের দেয় lines()
পদ্ধতি, যা একবারে এক লাইনে ইনপুট ডেটার উপর পুনরাবৃত্তি করবে।
এবং যে আমরা কি: আমরা কল lines()
যে পুনরাবৃত্তিকারী পেতে, এবং তারপর আমরা মান ব্যবহার count()
পুনরাবৃত্ত উত্পাদনকারী আইটেম সংখ্যা গণনা করার পদ্ধতি। যে উত্তর আমরা চাই, তাই আমরা এটি প্রিন্ট আউট.
যেহেতু এটি ঘটে, এটি লাইন গণনা করার একটি অপেক্ষাকৃত অদক্ষ উপায়। দ lines()
পুনরাবৃত্তিকারী দেয় আমাদের ফাইলের প্রতিটি লাইনকে একটি স্ট্রিং হিসাবে, যার অর্থ মেমরি বরাদ্দ করা, কিন্তু আমরা কেবল স্ট্রিংটি ফেলে দিই। আমরা সম্ভবত এটি করার আরও ভাল উপায় নিয়ে আসতে পারি যদি আমরা সত্যিই কঠিন চিন্তা করি, তবে এই মুহুর্তে এটি বিন্দু নয়।
ব্যবহারকারীরা কি চান?
মোদ্দা কথা হল lines().count()
লাইন গণনা করার জন্য এটি একটি খুব সহজবোধ্য এবং স্বজ্ঞাত উপায়, এবং আমরা API এর মতো বাস্তবায়নে সত্যিই আগ্রহী নই। একটি লাইব্রেরি ক্রেটের জন্য একটি চমৎকার API কি হবে যা লাইন গণনা প্রদান করে? এবং এটা কি পরীক্ষার জন্য সুবিধাজনক হবে?
আমরা এটিতে আসব, তবে প্রথমে দেখা যাক এটি আমাদের একই উত্তর দেয় কিনা wc
দ্রুত সুস্থতা যাচাইয়ের জন্য:
echo hello | cargo run
1
ভালো শুরু। অবশ্যই, প্রোগ্রামটিতে একটি বাগ থাকতে পারে যা এটি ঘটায়
সর্বদা উত্তর “1”, তাই এটি একটি খুব ভাল পরীক্ষা নয়. আমার এখানে একটি টেক্সট ফাইল আছে যা আমি আগে প্রস্তুত করেছিলাম এবং আমি জানি যে এতে 8,582 লাইন রয়েছে। তাহলে দেখা যাক প্রোগ্রামটি সম্মত হয় কিনা:
cat bigfile.txt | cargo run
8582
দেখে মনে হচ্ছে না যে আমরা এখন পর্যন্ত জিনিসগুলি খুব খারাপভাবে পেয়েছি। অবশ্যই, আমাদের এখনও একটি লাইব্রেরি নেই, তাই এটি পরবর্তী কাজ।
“জাদু ফাংশন” পদ্ধতি
আমরা এই কোডটিকে একটি পুনঃব্যবহারযোগ্য লাইব্রেরি প্যাকেজে পরিণত করতে চাই যা অন্য লোকেরা তাদের নিজস্ব প্রোগ্রামে আমদানি এবং ব্যবহার করতে পারে: মরিচা-এ, একে বলা হয় ক্রেট. আমরা সমস্ত “আচরণ” কোড সরাতে যাচ্ছি – যে সমস্ত কোড করে স্টাফ, শুধুমাত্র ফলাফল উপস্থাপনের বিপরীতে—একটি লাইব্রেরি ফাংশনে।
এবং সরাসরি আমাদের কাছে একটি চ্যালেঞ্জ আছে, কারণ আমরা সেই ফাংশনের API লিখতে পারি এমন অনেকগুলি উপায় রয়েছে: এর নাম থেকে এর পরামিতি এবং এটি কী দেয়। কিভাবে আমরা এই নকশা সমস্যা যোগাযোগ করা উচিত?
আমরা অনুমান করার চেষ্টা করতে পারি ব্যবহারকারীদের জন্য কি সুবিধাজনক হতে পারে, এবং আমরা সঠিক হতে পারি-কিন্তু সম্ভবত আমরা তা করব না। আমাদের জন্য যা সুবিধাজনক তা দ্বারা সাইডট্র্যাক করা খুব সহজ বাস্তবায়নকারী. তাই যে এড়াতে, এর আমাদের পুনর্লিখন করা যাক main
এই কাল্পনিক লাইন-গণনা ফাংশনকে কল করার জন্য ফাংশন, যেন এটি ইতিমধ্যেই বিদ্যমান ছিল।
আমি এটিকে “জাদু ফাংশন” পদ্ধতি বলি: ভান করুন আপনার কাছে কিছু বিস্ময়কর, জাদুকরী ফাংশন আছে যা আপনার যা করার প্রয়োজন তা করে। এবং তারপর এটি কল!
আপনি যেভাবে এটিকে কল করতে চান তা থেকে দেখতে পাবেন ফাংশনের API কী হতে হবে। তারপরে আপনাকে যা করতে হবে তা লিখতে হবে: আপনি ইতিমধ্যেই প্রয়োজনীয় ডিজাইন চিন্তা করে ফেলেছেন।
সুতরাং, যদি আমরা একটি উপযুক্ত জাদু ছিল count_lines
ফাংশন, এটিকে কল করার জন্য আমরা কী লিখতে চাই? এই মত কিছু, সম্ভবত:
use std::io::stdin;
use count::count_lines;
fn main()
let lines = count_lines(stdin().lock());
println!("lines");
আপনি কি মনে করেন? আমরা কীভাবে লিখতে পারি তা কল্পনা করা কঠিন
কম এখানে এবং এখনও আমরা যা চাই তা করতে সক্ষম হবেন। আমি একটি লাইব্রেরি ক্রেট থেকে এটাই চাই: একটি API যেখানে এটি ব্যবহার করার জন্য আমাকে সর্বনিম্ন সম্ভাব্য কাগজপত্র করতে হবে।
এখন এর একটি কটাক্ষপাত করা যাক lib.rs
আমাদের এই কাজটি বাস্তবায়ন করতে হবে।
টেস্টিং count_lines
প্রথমত, আসুন একটি পরীক্ষা লিখি, কারণ এটি কীভাবে আরও শক্তিশালী সীমাবদ্ধতা রাখে count_lines
বাস্তবায়ন করতে হবে। এটি প্রকৃত ব্যবহারকারী উভয়কেই সন্তুষ্ট করতে হবে (এই ক্ষেত্রে main
),
এবং পরীক্ষা (যা কোন ধরণের পাঠকের মধ্যে পাস করা হবে যা স্ট্যান্ডার্ড ইনপুট নয়)।
তাই, পরীক্ষা করার কি দরকার? আচ্ছা, ডাক
count_lines
অগত্যা, এবং ফলাফল পরীক্ষা করুন. তাই আমরা কি করতে পারি পাস থেকে count_lines
এখানে “জাল” ইনপুট হিসাবে?
আমাদের এমন কিছু দরকার যা বাস্তবায়ন করে BufRead
কারণ আমরা তার কল করতে যাচ্ছি lines
পদ্ধতি কিন্তু আমরা একটি স্ট্রিং থেকে এটি তৈরি করতে সক্ষম হতে চাই যাতে আমরা এটিতে কতগুলি লাইন রয়েছে তা নিয়ন্ত্রণ করতে পারি (আসুন 2 বলি)।
নামক একটি চমৎকার টাইপ আছে std::io::Cursor
যে এই জন্য নিখুঁত. এটি একটি বাইট অ্যারে (একটি স্ট্রিং সূক্ষ্ম) মত দেখায় এবং এটি একটি পাঠক (অর্থাৎ, প্রয়োগ করে এমন কিছু)তে পরিণত করে Read
) আমাদের উদ্দেশ্যে সহজে,
Cursor
এছাড়াও হয় BufRead
যা ঠিক কি
count_line
অপেক্ষা করছে।
এখানে আমরা যাই, তারপর:
use std::io::Cursor;
#(test)
fn count_lines_fn_counts_lines_in_input()
let input = Cursor::new("line 1\nline 2\n");
let lines = count_lines(input);
assert_eq!(lines, 2, "wrong line count");
যুক্তিসঙ্গত মনে হচ্ছে, তাই না? আমরা একটি তৈরি করি Cursor
টেক্সট দুটি লাইন ধারণকারী, এবং এটি পাস count_lines
. আমরা দাবি করি যে উত্তরটি 2, এবং অন্যথায় পরীক্ষায় ব্যর্থ হব।
পরবর্তী পোস্টে, আমরা ইচ্ছাকৃতভাবে একটি লিখে এই পরীক্ষাটি যাচাই করব
ভয়ানক এর বাস্তবায়ন count_lines
যে সব কাজ করে না. তাহলে দেখা হবে!