Yet Another Programming Series - Introduction to Every Programming Language

Requirements

যেহেতু গাইডটা লিখা হচ্ছে একবারে বিগিনারদের কথা মাথায় রেখে, সেহেতু আগে থেকে প্রোগ্রামিং এর কোন নলেজ লাগবে না। যদি প্রিভিয়াস আইডিয়া থাকে তাহলে হয়তো সেটা কাজে লাগবে। আর যদি কেউ এট লিস্ট একটা প্রোগ্রামিং ল্যাঙ্গুয়েজ আগে থেকে পারে তাহলে এই গাইড তাদের জন্য না।

প্রোগ্রামিং ল্যাঙ্গুয়েজ আগে থেকে না জানলেও Boolean Algebra আর Number systems (binary, decimal etc) জানলে ভালো। না জানলে খুব একটা সমস্যা নেই- কোন সেকশনে দরকার পড়লে আমি পর্যাপ্ত আইডিয়া দিয়ে নিব।

একটা কম্পিউটার লাগবে। ফোনেও কাজ চলে কিন্তু বড় স্ক্রিনে কোড লিখতে সুবিধা। কেউ যদি চায় একেবারে কোড লিখবে না, তাহলে ফোন থাকলেই যথেষ্ট। (তবে আমার মনে হয় না, নিজে কোড না লিখলে এই গাইড তার তেমন কাজে আসবে।)

কোড করলে একটা কোড ইডিটর লাগবে, তবে আপাতত কিছু ইন্সটল করার দরকার নাই। OnlineGDB দিয়েই কাজ চলবে।

How to use this guide

এই সেকশনটা না লিখলেও হতো। কিন্তু সব টিউটোরিয়ালেই দেখি এরকম একটা সেকশন থাকে এজন্য লিখলাম। বেসিক্যালি প্রতিটা সেকশন এ আমি কোন একটা আইডিয়া নিয়ে কথা বলবো। সাথে ওই আইডিয়া রিলেটেড কোড থাকবে তিনটা ল্যাঙ্গুয়েজে। কেউ চাইলে সেই কোড OnlineGDB তে টেস্ট করতে পারে। নিজের মতো মডিফাই করে এক্সপেরিমেন্ট করতে পারে। আর নাহলে পরের পার্টে যেতে পারে। ল্যাঙ্গুয়েজ তিনটা হচ্ছে Python, C আর Ruby.

গাইডের প্রতিটা কোড স্নিপেটের সাথে তিনটা বাটন পাওয়া যাবে, বাটনগুলা মূলত ল্যাঙ্গুয়েজ অপশন। ল্যাঙ্গুয়েজ সিলেক্ট করলে কোড চেঞ্জ হবে, আর কিছু ক্ষেত্রে এক্সপ্লেনেশনও চেঞ্জ হবে। চাইলে ল্যাঙ্গুয়েজ চেঞ্জ করে করে তিনটা আলাদা ল্যাঙ্গুয়েজের জন্যই প্রতিটা সেকশন ফলো করা যায়। তবে আমি সাজেস্ট করবো একবারে একটা ল্যাঙ্গুয়েজেই ফোকাস করতে। ল্যাঙ্গুয়েজ সিলেকশন বাটন দেখতে এরকম-

গাইডের যে কোন অংশে ল্যাঙ্গুয়েজ চেঞ্জ করলে পুরো গাইডের জন্যই ল্যাঙ্গুয়েজ চেঞ্জ হবে।

কোড স্নিপেটগুলা নিজে একবার OnlineGDB তে লিখে রান করলে বেটার। সেক্ষেত্রে কপিপেস্ট না করে নিজে টাইপ করলে সবকিছু দ্রুত আয়ত্তে আসবে। আর কপিপেস্ট করলেও সেটা আপটু দ্য রিডার। (আমি নিজে কপিপেস্ট করতাম, কিন্তু এরপর মডিফাই করে দেখতাম রেজাল্টে কী চেঞ্জ আসে।)

প্রতিটা সেশকশনের শেষে হয়তো ছোট কিছু টাস্ক দেয়া থাকবে, যেগুলা নিজে করা লাগবে। এই ছোট টাস্কগুলা না করলেও কিছু হবে না, করলে সবকিছু বুঝতে সুবিধা হবে, এই আরকি। যদি আগের কোড নিজে না লিখে কপিপেস্ট করে টেস্ট করা হয়, কিংবা একেবারেই টেস্ট না করা হয়, তাহলে এক্সট্রা টাস্কগুলা বেশ ইম্পর্ট্যান্ট বলা যায়। আর যেহেতু টাস্কগুলা এমনভাবে দেয়া হয়েছে যেন করতে করতে ইন্টুইশন ডেভেলপ হয়, সেহেতু সেগুলা করা উচিত বলে আমি মনে করি। টাস্ক গুলা সাধারণত 📝 ইমোজি দিয়ে লিখা হবে।

টাস্ক: এখন একটু বাহিরে গিয়ে ঘাস স্পর্শ করে আসা যাক।

তিনটা ল্যাঙ্গুয়েজ ইনক্লুড করার কারণ হচ্ছে প্রুফ অব কনসেপ্ট - যে সেইম প্রসিডিউর ফলো করে আসলেই অনেক ল্যাঙ্গুয়েজ শেখা যায়। যাই হোক, গাইড কমপ্লিট করলে ধরা যায় যে কেউ এই তিনটার মধ্যে এক বা একাধিক ল্যাঙ্গুয়েজের বেসিক শিখতে পারবে।

বারবার বেসিক বলছি কেননা, প্রতিটা ল্যাঙ্গুয়েজের নিজের কিছু স্পেসিফিক ফিচার থাকে, সেগুলা নিয়ে কোন আলোচনা করবো না। আমরা শুধু কমন গ্রাউন্ডগুলা নিয়ে আলাপ করবো। যে কোন প্রোগ্রামিং সমস্যার সল্যুশন বের করার জন্য আসলে ওই কমন গ্রাউন্ডগুলাই এনাফ।

Introduction to Every Language Programming

শুরু করার আগে আরেকটা জিনিস বলে নেই - প্রোগ্রামিং এর একটা খুবই ইম্পর্ট্যান্ট কনসেপ্ট হচ্ছে টার্মিনোলজি। “ভাই টার্ম কেন শিখবো, সল্যুশন বের করাই তো আসল গোল” মনে হতে পারে, কিন্তু টার্মিনোলজি হচ্ছে প্রোগ্রামিং ল্যাঙ্গুয়েজের একটা আনডকুমেন্টেড কনসেনসাস। দেশ-কাল ভেদে সব প্রোগ্রামার সেইম কনসেপ্টকে সেইম নামে চিনে। তাই টার্মিনোলজি জানার বিকল্প নেই বলতে গেলে। আর টার্মিনোলজি জানলে অনলাইনে সার্চ করা, অন্যরা কীভাবে সেইম জিনিস অ্যাপ্রোচ করছে এগুলা জানতে পারা সহজ।

আমার এই গাইড মেইনলি টার্মিনোলজির উপর বেইজ করে লিখা। যে কোন আইডিয়া এক্সপ্লেন করার ওয়েটা এরকম: টার্মিনোলজি -> আইডিয়া -> কোড -> এক্সপ্লেনেশন -> আরেকটু আইডিয়া

কিছু কিছু ক্ষেত্রে ল্যাঙ্গুয়েজভেদে টার্মিনোলজি আলাদা। আবার কখনো স্পেসিফিক ল্যাঙ্গুয়েজের জন্য অ্যাডিশনাল ইনফরমেশন দেয়া। সেক্ষেত্রে ল্যাঙ্গুয়েজ অপশন চেঞ্জ করলে ল্যাঙ্গুয়েজ স্পেসিফিক ইনফরম্যাশন দেখা যাবে।

প্রতিটা সেকশন শুরু হবে একটা টাইটেল দিয়ে। টাইটেলে এক বা একাধিক শব্দ থাকতে পারে। সেই সেকশনে ওই শব্দগুলা আর সেটা রিলেটেড যত কোড আছে সব এক্সপ্লেন করা হবে।

যাই হোক, এবার একটা কোড দিয়ে শুরু করি-

#include <stdio.h>

void main(){
    printf("Hi, mom!");
}
def main():
    print("Hi, mom!")
   
if __name__=="__main__":
    main()
def main
    print "Hi, mom!"
end

main

এই কোড রান করলে কী হবে সেটা চাইলে অনুমান করা যায়। কিন্তু এক্সাক্টলি কি হচ্ছে, কোডটা এরকম কেন, এইসব ব্যাপার আস্তে ধীরে আলোচনা করা যাবে!

Boilerplate, Comment and Keywords

Boilerplate হচ্ছে এমন কিছু কোড, যেগুলা লজিকালি কোন হেল্প করে না কিন্তু প্রায় প্রতিটা ল্যাঙ্গুয়েজে কোড লিখতে হলেই বয়লারপ্লেটের দরকার পরে। বয়লারপ্লেটগুলা এতো বেশি ব্যবহার করা হয় যে বারবার না লিখে কোথাও কপি করে রাখলে আরো ভালো হয়। যেমন নিচের কোডটা-

#include <stdio.h>

void main(){
    // your code here
}
def main():
    # your code here
   
if __name__=="__main__":
    main()
def main
    # your code here
end

main

অন্য যে কোন প্রোগ্রামিং গাইডের মতোই, আমি বয়লারপ্লেট কোড এক্সপ্লেন করবো না। বয়লারপ্লেটের বেশ কিছু পার্ট, “সময় হলে জানা যাবে” গোছের। সময়ের আগে সব বুঝার দরকার নাই। তবে এখানে যেটা এক্সপ্লেন করা যায়, সেটা হচ্ছে ##//. এই সিম্বলের মানে হচ্ছে, এ অংশে একটা কমেন্ট লিখা। Comment হচ্ছে এমন লাইন যেটা কম্পিউটার ইগ্নোর করবে। এটা মূলত মানুষ নিজের আর যারা কোড পড়বে তাদের জন্য লিখে। এখানে আমি যে # your code here# your code here// your code here লিখলাম, সেখানে your code here এর জায়গায় অন্য যা কিছু মন চায় লিখতে পারতাম, কিছু যায় আসতো না। বয়লারপ্লেটে সাধারণত your code here লিখা থাকে যেন প্রোগ্রামার বুঝে যে তার কোড এ জায়গায় লিখা লাগবে। তবে এ অংশেই যে সব কোড লিখা লাগবে এমন কোন নিয়ম নেই। প্রোগ্রামের উপর ডিপেন্ড করে মূলত যে কোন লাইনই চেঞ্জ করা লাগতে পারে।

কমেন্টের মতো আরেকটা জিনিস আমরা এই বয়লারপ্লেটে দেখতে পাই, সেটা হচ্ছে keywords. কীওয়ার্ড মূলত এমন কিছু শব্দ বা শব্দগুচ্ছ যেগুলা প্রোগ্রামিং ল্যাঙ্গুয়েজ নিজের জন্য রেখে দেয়। এগুলা যাচ্ছেতাই ভাবে ব্যবহার করা যায় না। স্পেসিফিক নিয়মে এই শব্দগুলা ব্যবহার করতে হয়। আর প্রোগ্রাম কীভাবে কাজ করবে সেগুলা অনেকটা এই কীওয়ার্ডের উপরে ডিপেন্ড করে। এই সেকশনে যেমন def, ifdefvoid, #include কে উদাহরণ হিসাবে ব্যবহার করা যায়।

প্রতিটা ল্যাঙ্গুয়েজেই এরকম অনেক কীওয়ার্ড আছে। এগুলা আলাদা করে মনে রাখার দরকার নেই। গাইড শেষ হতে হতে আয়ত্ত হয়ে যাবে।

Console, Statement, String and Printing

যেহেতু আমাদের বয়লারপ্লেট কোড রেডি, আমরা একটা ছোট প্রোগ্রাম লিখে ট্রাই করতে পারি।

#include <stdio.h>

void main(){
    printf("Hi, mom!");
}
def main():
    print("Hi, mom!")
   
if __name__=="__main__":
    main()
def main
    print "Hi, mom!"
end

main

কোডটা যেখানেই রান করা হোক না কেন, বেসিকালি কালো স্ক্রিনে সাদা টেক্সটে Hi, mom! লিখাটা ভেসে আসবে। OnlineGDB তে সেটা দেখতে হবে এরকম-

Hi mom example

আবার নিজের কম্পিউটারে সফটওয়্যার ইন্সটল করে রান করলে অন্যরকম দেখা যাবে। তবে ইন জেনারেল ভিজ্যুয়ালটা এরকমই হবে। এই কালো স্ক্রিনটাকে বলে Console. ওয়েবসাইট বা গ্রাফিকাল সফটওয়্যার বাদে মোটামুটি সব প্রোগ্রামই কনসোলের জন্য বানানো হয়। দেখতে কাঠখোট্টা হলেও, কনসোলে প্রোগ্রাম তুলনামূলক দ্রুত রান হয় আর কম্পিউটারের মেমোরি কম খরচ হয়। এজন্য অনেকেই কনসোল বেইজড প্রোগ্রাম পছন্দ করে।

"Hi, mom!" হচ্ছে আমার প্রিয় একটা ইউটিউব চ্যানেল Fireship এর catchphrase. এমনিতে সব প্রোগ্রামিং টিউটোরিয়ালে "Hello, world!" লিখলেও আমার কাছে মা-কে হাই বলাটাই বেশি কিউট লাগে!

যাই হোক, কোডে ফিরে আসি। বয়লারপ্লেটের সাথে এখানের পার্থক্য একটা লাইন। #Your code here//Your code here এর জায়গায় print("Hi, mom!")print "Hi, mom!"printf("Hi, mom!") লিখা। যেহেতু স্ক্রিনে Hi, mom! কথাটাই লিখা উঠেছে, তার মানে printprintf হচ্ছে আমাদের ম্যাজিক ওয়ার্ড!

এখন Hi, mom! এর জায়গায় অন্য কিছু ট্রাই করে দেখা যেতে পারে আসলেই ব্যাপারটা এমন নাকি।

#include <stdio.h>

void main(){
    printf("Hi, mom!");
    printf("I am writing a program.");
    printf("And it works!");
}
def main():
    print("Hi, mom!")
    print("I am writing a program.")
    print("And it works!")
   
if __name__=="__main__":
    main()
def main
    print "Hi, mom!"
    print "I am writing a program."
    print "And it works!"
end

main

দেখা যাচ্ছে printprintf এর মধ্যে যা যা লিখলাম স্ক্রিনে সেটাই লিখা উঠছে (যদিও আমরা আলাদা আলাদা লাইনে লিখলেও স্ক্রিনে আলাদাভাবে প্রিন্ট হচ্ছে না। তবে সেটা ঠিক করা যাবে)। তার মানে আমরা স্ক্রিনে কীভাবে প্রিন্ট করা লাগে সেটা শিখে গেলাম!

এইযে আমরা প্রতি লাইনে কম্পিউটারকে একটা কিছু প্রিন্ট করতে বলছি, এরকম আরো অনেক কিছু করতে বলা যায় (যেমন দুইটা সংখ্যা যোগ করা)। এই যে কিছু করতে বলা হচ্ছে, এ জিনিসটা হলো statement.

এখন যে কোন লাইনের সেমিকোলন (;) সরিয়ে দেখা যায় প্রোগ্রামে কী চেঞ্জ আসে। Spoiler Alert: কোড রান হবে না। কারণ C আসলে লাইন বা ব্ল্যাঙ্ক স্পেস বুঝে না। প্রতিটা স্টেটমেন্টের পর সেমিকোলন বসিয়ে একে জানানো লাগে যে স্টেটমেন্ট শেষ। সেমিকোলনগুলা রেখে সব প্রিন্ট স্টেটমেন্ট এক লাইনে লিখলে দেখা যাবে কোড আগের মতো ঠিকই রান হচ্ছে।

আরেকটা কাজ করা যায়। "Hi, mom!" এর ডাবল কোট (") দুইটা সরিয়ে শুধু Hi, mom! লিখে প্রোগ্রাম রান করে দেখা যায়। অবশ্যই কোড রান হবে না। মানুষের ভাষা যেমন গ্রামার মেনে চলে, তেমনি প্রোগ্রামিং ল্যাঙ্গুয়েজেরও গ্রামার আছে। সেই গ্রামারের বেশিরভাগ অংশ হচ্ছে কী-ওয়ার্ড আর স্পেসিফিক সিম্বল। যেমন ; হচ্ছে, C এর গ্রামারের একটা পার্ট। এখন Hi, mom! লিখাটা তো আমরা নিজেরা বানিয়ে লিখলাম, এটা কম্পিউটার বুঝার কথা না, কেননা এটা ল্যাঙ্গুয়েজ গ্রামারের পার্ট না। আদতে প্রতিটা বাক্যকে আলাদা করে গ্রামারের অংশ বানানো সম্ভব না। কিন্তু কম্পিউটারকে তো বুঝানো লাগবে যে আমার একটা বাক্য প্রিন্ট করা দরকার। এই বুঝানোর জন্যই মূলত ডাবল কোট দেয়া। ডাবল কোটের মধ্যে যা থাকে, কম্পিউটার সেগুলা নিয়ে মাথা ঘামায় না, বরং কোটসহ পুরো জিনিসটাকে একটা একক সত্ত্বা হিসাবে ধরে নেয়। এই একক সত্ত্বাটাকে বলা হয় string. আমাদের, "Hi, mom!", "I am writing a program." আর "And it works!" এর প্রতিটাই এক একটা স্ট্রিং।

গ্রামারের কথা যেহেতু বললাম, সেহেতু আরেকটা ছোট টেস্ট করা যায়। প্রতিটা print এর আগে যে চারটা করে স্পেস আছে, সেগুলা রিমুভ করে রান করা যাক। কোড রান হবে না। তার মানে এই চারটা স্পেস হলো পাইথন গ্রামার এর পার্ট। এই স্পেসিং টা কে বলে indentation. কোড আগের মতোই রান হবে। তার মানে এই স্পেসগুলা রুবির গ্রামার এর মধ্যে নেই। একটু আগে আমরা যে সব প্রিন্ট স্টেটমেন্ট এক লাইনে লিখলাম, সেরকম সব pritnf এর আগের যে চার লাইন করে আছে, সেগুলা রিমুভ করে এরপর কোড রান করতে পারি। একই জিনিস প্রিন্ট হবে। তার মানে স্পেসগুলা কিংবা লাইনব্রেক C গ্রামারের পার্ট না। কোড যেন অর্গানাইজড থাকে, সেজন্য এই স্পেস দেয়া হয়েছে।

#include <stdio.h>

void main(){
    printf("Hi, mom!"); printf("I am writing a program."); // works
printf("And it works!"); // works
    printf("Hi, mom!") //doesn't work (no semicolon)
    printf(Hi, mom!) // doesn't work (not a string)
}
def main():
print("Hi, mom!") # doesn't work (no indentation)
	print("I am writing a program") # works
    print(And it works!) # doesn't work (not a string)
   
if __name__=="__main__":
    main()
def main
    print "Hi, mom!" # works
print "I am writing a program." # works
	print And it works! # doesn't work (not a string)
end

main

আমি এতক্ষণ ‘গ্রামার’ শব্দটা লিখেছি, আসলে এটা গ্রামার হবে না। প্রোগ্রামিং ল্যাঙ্গুয়েজ এর গ্রামার এর অনেকগুলা পার্ট থাকে। আপাতত আমরা ডিস্কাস করছি “কীভাবে লিখতে হবে” ব্যাপারটা। এটাকে বলে Syntax.

আমি উপরের কোড স্নিপেটটায় কমেন্ট আকারে কোড কাজ করবে নাকি করবে না সেটা লিখেছি। এই জিনিসটা পরের কয়েকটা সেকশনে আরো বেশি দেখা যাবে, স্পেশালি যখন ম্যাথমেটিকাল ক্যালকুলেশন নিয়ে ডিস্কাস করবো। কেউ নিজে কোড রান করলে কমেন্টের সাথে মিলিয়ে দেখতে পারে তার কোড ঠিকভাবে রান হচ্ছে নাকি।

Escape Characters

স্ক্রিনে তিনটা বাক্য প্রিন্ট করেছিলাম যেখানে, সেখানে ফিরে যাই। তিনটা আলাদা লাইনে প্রিন্ট স্টেটমেন্ট দিলেও পুরো লেখাটা কিন্তু একসাথে প্রিন্ট হয়েছে। Hi, mom!I am writing a program.And it works! পুরোটা একসাথে দেখতে একটু কেমন যেন লাগে। এর কারণ হচ্ছে, কম্পিউটার যখন একটা স্ট্রিং প্রিন্ট করে, সে তখন শুধু স্ট্রিং-টাকেই কন্সিডার করে। তাই স্টেটমেন্ট কয় লাইন লাগিয়ে লিখছি, এটা কম্পিউটার বিবেচনায় নেয় না। আলাদা করে একে বলে দিতে হবে যে পরের লাইনে যেতে হবে। এই “পরের লাইনে যেতে হবে” জিনিসটা \n দিয়ে লিখা যায় (n এর আগের সিম্বলটা ব্যাকস্ল্যাশ। রেগুলার স্ল্যাশ / দিলে কাজ হবে না)। \n দিয়ে বুঝায় newline. কোন স্ট্রিং এর শেষে নিউলাইন জুড়ে দিলেই কম্পিউটার বুঝে যাবে যে নতুন লাইনে যাওয়া লাগবে। এটাকে লাইন ব্রেকও বলে অনেকে।

#include <stdio.h>

void main(){
    printf("Hi, mom!\n");
    printf("I am writing a program.\n");
    printf("And it works!");
}
 def main
     print "Hi, mom!\n"
     print "I am writing a program.\n"
     print "And it works!"
 end
 
 main

শেষ প্রিন্ট স্টেটমেন্টটায় \n দেয়া হয় নি। কারণ শেষ বাক্যটার পরে লাইন ব্রেক না দিলেও কিছু হবে না। কিন্তু এমনিতে বেস্ট প্র্যাক্টিস হলো একটা লাইন ব্রেক দেয়া। তাহলে নতুন আরেকটা প্রিন্ট স্টেটমেন্ট লিখা লাগলে আগেরটায় আবার নতুন করে \n লিখা লাগবে না। Ruby তে এই বারবার \n লিখার ঝামেলায় যেন না পড়তে হয় সেজন্য print এর জায়গায় puts লিখা যায়। puts এর পর স্ট্রিং-টা লিখলেই হয়, আলাদা করে নিউলাইন লিখা লাগে না। এই গাইডে এখন থেকে আমরা print এর জায়গায় puts ব্যবহার করবো। 

def main
    puts "Hi, mom!\n"
    puts "I am writing a program.\n"
    puts "And it works!"
end

main

আগে যেরকম তিন লাইনে তিনটা বাক্য লিখলাম, সেরকম আরেকটা কাজ করা যায়। যেমন আমরা একটা ছোট টেবিল তৈরি করতে পারি। টেবিলটা হবে এরকম-

Name Age Language
Alice 18 Ruby
Bob 20 C
Charlie 22 Python
Alice, Bob আর Charlie প্রোগ্রামিং জগতে বেশ কমন নাম। মোস্ট কেইস এ কোন নামের দরকার পড়লে এইগুলা ব্যবহার করা হয়। এছাড়া John Doe আর Jane Doe নাম দুইটাও ব্যবহার করা হয় কখনো কখনো।

এখন আমরা এই টেবিলটা প্রিন্ট করার প্রোগ্রাম লিখে ফেলি। আপাতদৃষ্টিতে বেশ সহজ একটা কাজ - প্রতিটা রো একটা করে প্রিন্ট স্টেটমেন্ট দিয়ে লিখব। আর কলাম লিখার জন্য প্রতিটা শব্দের পর ৪টা স্পেস দেয়া যায়।

#include <stdio.h>

void main(){
    printf("NAME    AGE    LANGUAGE\n");
    printf("Alice    18    Ruby\n");
    printf("Bob    20    C\n");
    printf("Charlie    22    Python\");
}
def main
    puts "NAME    AGE    LANGUAGE"
    puts "Alice    18    Ruby"
    puts "Bob    20    C"
    puts "Charlie    22    Python"
end

main
def main():
    print("NAME    AGE    LANGUAGE")
    print("Alice    18    Ruby")
    print("Bob    20    C")
    print("Charlie    22    Python")

if __name__=="__main__":
    main()

সব ঠিক থাকলে এরকম একটা আউটপুট আসবে-

Output

দেখতে খুব কাট্টাখোট্টা লাগে। কারণ হচ্ছে আমরা কলাম লিখার জন্য চারটা করে স্পেস দিয়েছি। যেহেতু এলিস, বব আর চার্লির নামে একই সংখ্যক বর্ণ নেই, সেহেতু এলাইনমেন্টটা নষ্ট হয়ে গেছে (কাকতালীয়ভাবে প্রথম দুইটা রো কিন্তু সুন্দরভাবে এলাইন হয়েছে)। এই সমস্যার সমাধান করার জন্য আমরা ব্যবহার করতে পারি tab বা \t. ট্যাবের কাজ হচ্ছে একটা নির্দিষ্ট পরিমাণ ফাঁকা রেখে এলাইনমেন্ট আনা। এখন আমরা চারটা স্পেসের জায়গায় ট্যাব ব্যবহার করে দেখতে পারি।

#include <stdio.h>

void main(){
    printf("NAME\tAGE\tLANGUAGE\n");
    printf("Alice\t18\tRuby\n");
    printf("Bob\t20\tC\n");
    printf("Charlie\t22\tPython\");
}
def main
    puts "NAME\tAGE\tLANGUAGE"
    puts "Alice\t18\tRuby"
    puts "Bob\t20\tC"
    puts "Charlie\t22\tPython"
end

main
def main():
    print("NAME\tAGE\tLANGUAGE")
    print("Alice\t18\tRuby")
    print("Bob\t20\tC")
    print("Charlie\t22\tPython")

if __name__=="__main__":
    main()

এবার কোড দেখতে কাট্টাখোট্টা, কিন্তু আউটপুটটা বেশ ভালো-

Output

এবার ছোট্ট একটা টাস্ক। Bob এর জায়গায় আরো লম্বা একটা নাম ব্যবহার করে দেখতে হবে আউটপুট কী হয়। এরকম আউটপুট আসার কারণ কী?

আচ্ছা এই টেবিলটা কিন্তু একটা প্রিন্ট স্টেটমেন্ট ইউজ করেই লিখা যায়। স্ট্রিংটা হবে "NAME\tAGE\tLANGUAGE\nAlice\t18\tRuby\nBob\t20\tC\nCharlie\t22\tPython"! \t দিয়ে যেরকম tab বুঝায়, n দিয়ে বুঝায় newline. তার মানে পরের লাইনে যাওয়া লাগবে, এটা বুঝানোর জন্য \n ইউজ করা লাগবে।

\t, \n এর মতো ক্যারেক্টারকে বলে escape character (\ আর এরপরের বর্ণ, দুইটা মিলিয়ে একটা ক্যারেক্টার কিন্তু!). ইস্কেইপ ক্যারেক্টার দিয়ে কনসোলে অনেককিছু করা যায়। এরকম আরো কিছু ক্যারেক্টার হচ্ছে \b বা backspace, \v বা vertical tab ইত্যাদি।

বাকি escape character গুলার কাজ কী, সেটা নিজে ট্রাই করে দেখা লাগবে।

String Formatting - The Idea

আবার একটা স্ট্রিং দিয়ে শুরু করি। "5 points will be awarded to each of you for sheer dumb luck."

Harry Potter and the Philosopher's Stone এ হ্যারিরা একটা troll-কে নাস্তানাবুদ করার পর সেটা দেখে Minerva McGonagall এই কথাটা বলে।

এখন এই স্ট্রিংটা চটজলদি প্রিন্ট করে ফেলা যাক।

এখন এমন হতে পারে যে মিনার্ভা এর জায়গায় প্রফেসর ডাম্বলডোর দেখলো ব্যাপারটা। তাহলে তো সে মিনিমাম ৫০ পয়েন্ট দিতোই। এক্ষেত্রে স্ট্রিংটা হয়ে যেত - "50 points will be awarded to each of you for absolute bravery."

এখন, পার্সন চেঞ্জ হলেই যেহেতু পুরো কোটটাই চেঞ্জ হয়ে যাচ্ছে, সেহেতু আলাদা আলাদা কোট প্রিন্ট করতে হলে বারবার স্ট্রিং ইডিট করতে হবে। যদি স্ট্রিংটা আরো বড় হয় কিংবা অনেক কিছু চেঞ্জ করা লাগে তাহলে বেশ ঝামেলার ব্যাপার। এই সমস্যা সমাধানের জন্য একটা ছাঁচ বানানো দরকার, যেন সেই ছাঁচে ফেলে আলাদা আলাদা বাক্য বানানো যায়। আগের দুইটা কোট চিন্তা করলে ছাঁচটা এরকম- "<number> points will be awarded to each of you for <reason>." এরপর <number> আর <reason> দুইটার জায়গায় যখন যে ভ্যালু দরকার সেটা বসিয়ে দিলেই হলো। এখানে <number> হবে একটা সংখ্যা আর <reason> হচ্ছে আরেকটা স্ট্রিং।

এখন দেখা যাক এটা কোড করলে কেমন হয়।

#include <stdio.h>

void main(){
    printf("%d points will be awarded to each of you for %s.\n", 5, "sheer dumb luck");
    printf("%d points will be awarded to each of you for %s.\n", 50, "absolute bravery");
}
def main
    puts "%d points will be awarded to each of you for %s." % [5, "sheer dumb luck"]
    puts "%d points will be awarded to each of you for %s." % [50, "absolute bravery"]
end

main
def main():
    print("{} points will be awarded to each of you for {}.".format(5, "sheer dumb luck"))
    print("{} points will be awarded to each of you for {}.".format(50, "absolute bravery"))
    
if __name__ == "__main__":
    main()

এখানে আমরা <number> এর জায়গায় %d আর <reason> এর জায়গায় %s লিখে কম্পিউটারকে বুঝালাম যে কোনটা কোথায় বসবে। এরপর স্ট্রিং এর পর কমা দিয়ে দিয়ে কী কী ভ্যালু বসবে সেগুলা লিখা হয়েছে। স্ট্রিং এর শেষে % দিয়ে বুঝালাম যে ক্যারেক্টারগুলা রিপ্লেস করা লাগবে। কোন কোন ভ্যালু দিয়ে রিপ্লেস হবে সেগুলা [ ] এর মধ্যে কমা দিয়ে দিয়ে লিখা হয়েছে। %s আর %d দিয়ে কী বুঝায় সেটা পরের সেকশনে ডিসকাস করবো। তবে আপাতত %s দিয়ে স্ট্রিং আর %d দিয়ে ডেসিম্যাল বুঝায়, এটা মাথায় রাখা যায়। এখানে আমরা স্ট্রিং এর যেসব জায়গায় ভ্যালু বসাতে হবে সেসব জায়গায় {} লিখেছি। এরপর স্ট্রিং এর শেষে .format() লিখে কম্পিউটারকে বুঝালাম যে {} গুলাকে রিপ্লেস করা লাগবে। ( ) এর মধ্যে কমা দিয়ে দিয়ে কোন কোন ভ্যালু বসবে সেগুলা লিখা হয়েছে।

এবার একটা ছোট্ট টাস্ক। %s আর %d এর জায়গা চেঞ্জ করে আউটপুট কী আসে সেটা চেক করতে হবে। তারপর  বাকি দুইটা ভ্যালুর পর কমা দিয়ে আরেকটা সংখ্যা বা স্ট্রিং লিখে দেখতে হবে প্রোগ্রাম রান হয় নাকি (নতুন করে কোথাও %s বা %d যোগ করা যাবে না)।

এই হলো, স্ট্রিং ফরম্যাটিং এর একটা বেসিক আইডিয়া। যদিও আপাতত এটা দিয়ে খুব হেল্পফুল কিছু হয় নি, কিন্তু খুব শীঘ্রই স্ট্রিং ফরম্যাটিং এর উপরকারিতা আমরা বুঝতে পারব।

এখন নিজের মতো একটা ছাঁচ বানিয়ে স্ট্রিং ফরম্যাটিং নিয়ে এক্সপেরিমেন্ট করা যেতে পারে। আরেকটা কাজ করা যায়- যতগুলা ভ্যালু বসাতে হবে, তার থেকে কম ভ্যালু দিয়ে দেখা যায় প্রোগ্রাম রান হয় নাকি…

Variables

একটু আগে যদিও বললাম যে বারবার যেন স্ট্রিং ইডিট না করতে হয় এজন্য স্ট্রিং ফরম্যাটিং ইউজ করছি, তবুও দেখা যাচ্ছে যে বিশাল বিশাল স্টেটমেন্ট লিখতে হচ্ছে। এরপর কোন ভ্যালু চেঞ্জ করা লাগলে সেটা যে স্টেটমেন্টে লিখা সেটায় গিয়ে চেঞ্জ করতে হবে। ঘুরে ফিরে একই জিনিস করা লাগবে। তার মানে, স্ট্রিং ফরম্যাটিং থেকে খুব বেশি সুবিধা পাওয়া গেল না। একটা উদাহরণ দিয়ে শুরু করি-

#include <stdio.h>

void main(){
    printf("Yesterday I touched grass %d times.\n", 5);
    printf("I also drank %d glasses of water. That is around %d litres.\n", 8, 2);
    printf("I also slept %d hours.\n", 8);
}
def main
    puts "Yesterday I touched grass %d times." % [5]
    puts "I also drank %d glasses of water. That is around %d litres." % [8, 2]
    puts "I also slept %d hours." % [8]
end

main
def main():
    print("Yesterday I touched grass {} times".formate(5))
    print("I also drank {} glasses of water. That is around {} litres.".format(8, 2))
    print("I also slept {} hours.".format(8))
    
if __name__ == "__main__":
    main()

এখন, আমরা যদি পানি খাওয়ার পরিমাণ চেঞ্জ করতে চাই, তাহলে সেকেন্ড প্রিন্ট স্টেটমেন্টে গিয়ে নাম্বার চেঞ্জ করা লাগবে। এরপর আবার কোন কারণে ভুল ভ্যালু চেঞ্জ করলে আরেক ঝামেলা। এই সমস্যা সমাধানের জন্য যেটা লাগবে সেটা হচ্ছে variable. ভ্যারিয়েবল হলো কোন ভ্যালু ফিউচারে ইউজ করার জন্য সেভ করে রাখার একটা উপায়।

ভ্যারিয়েবলকে একটা বক্সের সাথে তুলনা করা যেতে পারে। বক্সের একটা নাম থাকবে, আর বক্সের মধ্যে কিছু একটা থাকা লাগবে। সেই ‘কিছু একটা’ আমরা পরে সময় হলে ব্যবহার করতে পারব। এছাড়া বক্সের সাইজ থাকে, যেটার মধ্যে আমাদের যে জিনিসটা রাখতে চাই সেটা আঁটতে হবে। আর কী ধরণের জিনিস রাখবো, সেটা ঠিক করে দেয়া থাকে। যদিও এই ধরণটা ইচ্ছামতো চেঞ্জ করা যায়। একবার বলা হয়ে গেলে এটা আর চেঞ্জ করা যায় না। মানে কাপড়ের বক্সে আর খেলনা রাখা যাবে না আরকি।

যা বললাম, ভ্যারিয়েবলের একটা নাম থাকবে আর ভ্যালু থাকবে। ভ্যালুটা কোন এক ধরণের ভ্যালু হবে। যেমন একটু আগে যে কোড লিখলাম সবগুলা ভ্যালু হচ্ছে integer. এই তিনটা জিনিস মাথায় রেখে আমরা কোডে ভ্যারিয়েবল অ্যাড করতে পারি। এরপর দেখা যায় কোডে কীরকম চেঞ্জ আসে।

#include <stdio.h>

void main(){
    int touches, glasses, litres, hours;
    touches = 5;
    glasses = 8;
    litres = 2;
    hours = 8;
    
    printf("Yesterday I touched grass %d times.\n", touches);
    printf("I also drank %d glasses of water. That is around %d litres.\n", glasses, litres);
    printf("I also slept %d hours.\n", hours);
}
def main
    touches = 5
    glasses = 8
    litres = 2
    hours = 8
    
    puts "Yesterday I touched grass %d times" % [touches]
    puts "I also drank %d glasses of water. That is around %d litres." % [glasses, litres]
    puts "I also slept %d hours." % [hours]
end

main
def main():
    touches = 5
    glasses = 8
    litres = 2
    hours = 8
    
    print("Yesterday I touched grass {} times".format(touches))
    print("I also drank {} glasses of water. That is around {} litres.".format(glasses, litres))
    print("I also slept {} hours.".format(hours))
    
if __name__ == "__main__":
    main()

আমরা প্রথমে int touches, glasses, litres, hours দিয়ে কম্পিউটারকে জানিয়ে নিয়েছি যে আমার চারটা ভ্যারিয়েবল আছে, যাদের নাম যথাক্রমে এগুলা আর এরা হচ্ছে integer ভ্যালু রাখার ভ্যারিয়েবল। প্রতিটা স্টেটমেন্ট এ আমরা ভ্যারিয়েবলগুলায় একটা করে মান বসিয়েছি। এখন আমরা যদি কোন ভ্যালু চেঞ্জ করতে চাই, তাহলে শুধু সে রিলেটেড ভ্যারিয়েবলের মান চেঞ্জ করলেই হবে। কষ্ট করে প্রিন্ট স্টেটমেন্টগুলায় হাত দেয়া লাগবে না। এইযে কম্পিউটারকে ভ্যারিয়েবল থাকার কথা জানিয়ে দেয়া, এই ব্যাপারটাকে বলে Variable Declaration.

বারবার এভাবে .format() লিখে এরপর এর মধ্যে ভ্যালু লিখা বেশ ঝামেলার। এ ঝামেলা থেকে বাঁচার জন্য f-string নামের একটা ফিচার আছে পাইথনে। স্ট্রিং এর শুরুর "-এর আগে f ক্যারেক্টারটা বসিয়ে দিলেই হয়ে গেল এফ-স্ট্রিং। এটা ইউজ করলে, format লিখা লাগে না, সরাসরি {} এর মধ্যে ভ্যারিয়েবলের নাম লিখলেই হয়।

def main():
    touches = 5
    glasses = 8
    litres = 2
    hours = 8
    
    print(f"Yesterday I touched grass {touches} times")
    print(f"I also drank {glasses} glasses of water. That is around {litres} litres.")
    print(f"I also slept {hours} hours.")

if __name__ == "__main__":
    main()

ভ্যারিয়েবলের নাম সবসময় যে কোড লিখে তার উপর নির্ভর করে। যেমন glasses এর জায়গায় শুধু g লিখলে কিংবা abcd লিখলেও কিছু যায় আসতো না। অন্য কেউ যেন কোড পড়লে বুঝে, এজন্য ভ্যারিয়েবলের নাম এর কাজের সাথে মিল রেখে দেয়া হয়।

এখন একই ভ্যারিয়েবলে দুইটা আলাদা মান লিখে দেখতে হবে যে প্রিন্ট করলে কোন ভ্যালুটা আসে। অর্থাৎ প্রথম স্টেটমেন্ট x=2 হলে তৃতীয় স্টেটমেন্টে x=5 লিখে দেখতে হবে স্ক্রিনে কী প্রিন্ট হয়।

যখন আমরা একই ভ্যারিয়েবলে দুইটা আলাদা মান বসাই, তখন যে মানটা পরে বসানো হয়েছে, সে মানটা ভ্যারিয়েবলে থাকবে।

এখানে একটা জিনিস লক্ষ করা দরকার- x = 5 দিয়ে কিন্তু এটা বুঝায় না যে x এর মান 5 এর সমান। বরং বুঝায় যে, 5 এর যে মান, সেটা x নামক ভ্যারিয়েবলে রাখতে হবে। যেহেতু আমরা ভ্যারিয়েবলের জন্য একটা ভ্যালু অ্যাসাইন করে দিচ্ছি, সেজন্য = চিহ্নকে বলে assignment operator.

এবার নিজের মতো একটা বা কয়েকটা স্ট্রিং বানানো যায়, যেখানে অনেক সংখ্যা থাকবে। সংখ্যাগুলা বিভিন্ন ভ্যারিয়েবলে রেখে এরপর প্রিন্ট করে দেখতে হবে। এক্ষেত্রে বেশ কিছু এক্সপেরিমেন্ট করা সম্ভব- একই ভ্যারিয়েবল কয়েক জায়গায় ইউজ করা, একটা প্রিন্ট স্টেটমেন্টের পর ভ্যালু চেঞ্জ করে আরেকটা প্রিন্ট স্টেটমেন্ট দেয়া ইত্যাদি। যদি কোন ভ্যারিয়েবল এর কথা কম্পিউটারকে না বলেই প্রিন্ট করা হয়, তাহলে কী প্রিন্ট হবে?

#include <stdio.h>

void main(){
    int var1, var2;
    var1 = 2;
    var2 = 3;
    
    printf("Var1: %d\n", var1); // Var1: 2
    printf("Var2: %d\n", var1); // Var2: 2
    
    var1 = 5;
    printf("Var1 (Changed): %d\n", var1); // Var1 (Changed): 2
    printf("Var2: %d\n", var2); // Var2: 3
    printf("Var3: %d\n", var3); // doesn't work
}
def main():
    var1 = 2
    var2 = 3
    
    print(f"Var1: {var1}") # Var1: 2
    print(f"Var2: {var1}") # Var2: 2
    
    var1 = 5
    print(f"Var1 (Changed): {var1}") # Var1 (Changed): 5
    print(f"Var2: {var2}") # Var2: 3
    print(f"Var3: {var3}") # doesn't work
    
if __name__ == "__main__":
    main()
def main
    var1 = 2
    var2 = 3
    
    puts "Var1: %d" % [var1] # Var1: 2
    puts "Var2: %d" % [var1] # Var2: 2
    
    var1 = 5
    puts "Var1 (Changed): {var1}" % [var1] # Var1 (Changed): 5
    puts "Var2: %d" % [var2] # Var2: 3
    puts "Var3: %d" % [var3] # doesn't work
end

main

এতক্ষণ আমরা শুধু integer ভ্যারিয়েবল নিয়ে কাজ করেছি। অন্য ধরণের ভ্যারিয়েবল নিয়ে পরের সেকশনে ডিসকাস করবো।

Data Types

আগের সেকশনে বলেছিলাম যে ভ্যারিয়েবল নামক বক্সের একটা বৈশিষ্ট হচ্ছে এর মধ্যে কোন ধরণের জিনিস রাখা যাবে সেটা। এই বৈশিষ্ট এর অফিসিয়াল নাম হচ্ছে Data Type. সি-তে যে কোন ভ্যারিয়েবল ডিক্লেয়ার করার আগে এর ডেটা টাইপ জানিয়ে নিতে হয়। যদিও ডেটা টাইপ খুবই ইম্পর্ট্যান্ট একটা বৈশিষ্ট, কম্পিউটারকে এর ব্যাপারে আলাদা করে জানানোর প্রয়োজন পড়ে না। ভ্যারিয়েবল ডিক্লেয়ার করলে কম্পিউটার নিজেই বুঝে নেয় ডেটা টাইপ কী হবে। তবে প্রোগ্রামারের নিজের মনে রাখা লাগে কোন ভ্যারিয়েবলের টাইপ কী? 

সব প্রোগ্রামিং ল্যাঙ্গুয়েজই কিছু ডেটা টাইপ ইমপ্লিমেন্ট করে রাখে। প্রতিটা ডেটা টাইপ ইউনিক এবং বেশিরভাগ ক্ষেত্রে একটার কাজ অন্যটা দিয়ে করা যায় না। সাধারণভাবে ডেটা টাইপগুলাকে আমরা দুই ভাগে ভাগ করতে পারি-

  • Primitive Data Types - এগুলা সাধারণত একটা মান রাখতে পারে (যেমন integer, character)
  • Composite Data Types - এগুলা হয় অনেকগুলা মান রাখতে পারে (যেমন ArrayList) নাহলে কয়েকটা প্রিমিটিভ ডেটা টাইপ নিয়ে একটা টাইপ হয় (যেমন StructHashDictionary)।

এই সেকশনে মূলত প্রিমিটিভ ডেটা টাইপগুলা নিয়ে আলোচনা করা হবে। কম্পোজিট ডেটা টাইপগুলা নিয়ে আলাদা সেকশন থাকবে।

ল্যাঙ্গুয়েজের সাথে ডেটা টাইপগুলা দিয়ে দেয়ার উদ্দেশ্য হচ্ছে যেন আমরা বেসিক সবকিছু নিজেদের আলাদা ইমপ্লিমেন্টেশন ছাড়াই করতে পারি। ধরা যাক, আমরা দুইটা সংখ্যা যোগ করে যোগফল প্রিন্ট করবো। সেক্ষেত্রে, আমাদের শুধু দুই ধরণের ভ্যারিয়েবল লাগবে- number আর string. দেখা যায়, এভাবে প্রায় সব ধরণের কাজই আমরা প্রিমিটিভ আর মাঝেমধ্যে কম্পোজিট ডেটা টাইপ দিয়ে করে ফেলতে পারি।

প্রতিটা ডেটা টাইপকে ল্যাঙ্গুয়েজভেদে আলাদাভাবে প্রকাশ করা হয়। ইউজকেসের উপর ভিত্তি করে যেসব ডেটা টাইপ আমরা পাই সেগুলা হচ্ছে এরকম-

  • Numerical: যাবতীয় সংখ্যা সংক্রান্ত ডেটা টাইপ। Numerical data type অনেকরকমের হতে পারে। এগুলা হচ্ছে- integer, float, short, long, double, complex.
  • StringCharacter: কোন অক্ষর বা বাক্য সংক্রান্ত ডেটা টাইপ। স্ট্রিং হচ্ছে অনেকগুলা character এর সমষ্টি। 
  • Boolean: বুলিয়ান true বা false এর জন্য এই টাইপের ভ্যারিয়েবল ব্যবহার করা হয়। C তে সাধারণত int টাইপ দিয়ে বুলিয়ান ভ্যারিয়েবল ডিক্লেয়ার করা হয়। সেক্ষেত্রে 0 কে false আর অন্য যে কোন সংখ্যাকে true ধরা হয়।
  • Special types: বিশেষ কিছু ডেটা টাইপকে এই ক্যাটেগরিতে ফেলা যায়। যেমন void নামক একটা টাইপ কোন নির্দিষ্ট টাইপ না এমন বুঝায়! এ টাইপটা function রিলেটেড সেকশনে আলোচনা করা হবে। nilNone এমন একটা ডেটা টাইপ যেটা দিয়ে কিছুই নেই এমন বুঝায়। কোন ভ্যারিয়েবল ডিক্লেয়ার করা লাগবে কিন্তু আপাতত কোন মান বসাতে চাই না, এমন পরিস্থিতি সৃষ্টি হলে এই ডেটা টাইপ ইউজ করা যায়। কিংবা কোন ভ্যারিয়েবল খালি করে দিতে চাইলে এটা ব্যবহার করা যায়।

String formatting এর সময় প্রতিটা ডেটা টাইপের জন্য % চিহ্নের পর আলাদা আলাদা বর্ণ লিখা লাগে। থিওরেটিকাল আলোচনা অনেক হলো। এবার কোড দেখে বুঝার চেষ্টা করা যাক!

#include <stdio.h>

void main(){
    int var1;
    var1 = 25;
    printf("%d is an integer variable.\n", var1);
    
    long var2 = 922337203685477;
    printf("%ld is also an integer variable but can have larger values.\n", var2);
    
    unsigned int var3 = 255; // or unsinged long var3 = 255;
    printf("%u is an integer variable but can have positive numbers only.\n", var3);
    // printf("%lu is a positive integer variable but can have larger values.\n", var3);
    
    float var4 = 2.54;
    printf("%f is a float variable. It is accurate for upto 6 decimal places.\n", var4);
    
    double var5 = 22.452354435;
    printf("%g is a float variable. It is accurate for upto 15 decimal places.\n", var5);
    
    char var7 = 'c';
    printf("%c is a character variable.\n", var7);
}
def main():
    varInt = 123123123123
    varFloat = 122.123123
    varStr = "Test string"
    varBool = False
    varComplex = 3+2j
    varNone = None

    print(f"{varInt} is an int variable.")
    print(f"{varFloat} is a float variable.")
    print(f"{varStr} is a str variable.")
    print(f"{varComplex} is a complex variable.")
    print(f"{varBool} is a boolean variable.")
    print(f"{varNone} is a nonetype variable.")
   
if __name__=="__main__":
    main()
def main
    varInt = 123123123123
    varFloat = 122.123123
    varStr = "Test string"
    varBool = false
    varNil = nil

    puts "%d is an Integer variable" % [varInt]
    puts "%f is a Float variable" % [varFloat]
    puts "%s is a String variable" % [varStr]
    puts "%s is a Boolean variable" % [varBool]
    puts "%s is a nil variable" % [varNil]
end

main

যদিও আগের সেকশন পর্যন্ত আমরা আগে ভ্যারিয়েবল ডিক্লেয়ার করতাম আর তারপর ভ্যালু অ্যাসাইন করতাম, তবে চাইলে আমরা এক লাইনেই সেটা করতে পারি। সেক্ষেত্রে datatype name = value; এই ফরম্যাট ফলো করা লাগে। সবার শেষ প্রিন্ট স্টেটমেন্টে কোন মান প্রিন্ট হয় নি। কারণ nil এর কোন মান নেই! যদিও পাইথন কোড দেখে ডেটা টাইপ বুঝার কোন দরকার মনে হচ্ছে না, কিন্তু প্রোগ্রামে ভুল কমানোর জন্য ডেটা টাইপ বুঝা অত্যন্ত জরুরী। 

সি-তে একই ধরণের ডেটা এর জন্য অনেকগুলা ডেটা টাইপ। এর কারণ মেমোরি সংক্রান্ত। যেমন int ভ্যারিয়েবলে -2147483648 থেকে 2147483647 পর্যন্ত রাখা যায় (কম্পিউটারভেদে এই মান আলাদা)। আর long দিয়ে এর কয়েকগুণ পর্যন্ত মান রাখা যায়। আরেকটা জিনিস- যদিও কোডে লিখেছি যে double এর একুরেসি দশমিকের পর ১৫ ডিজিট পর্যন্ত, সে লাইনে কিন্তু দশমিকের পর মাত্র কয়েকঘর প্রিন্ট হয়েছে। এটা নিয়ে চিন্তার কোন কারণ নেই। দশমিকের পর কয় ঘর দেখাতে হবে, সেটা string formatting এর অংশ। গাণিতিক হিসাবনিকাশে ঠিকই দশমিকের পর ১৫ ঘর পর্যন্ত বিবেচনা করা হবে। বারবার এভাবে ডেটা টাইপ দেখে স্ট্রিং ফরম্যাট করা বেশ ঝামেলার। এ সমস্যা সমাধানের জন্য #{} এর মধ্যে ভ্যারিয়েবল লিখা যায়। এই গাইডে এরপর থেকে এই ফরম্যাটেই রুবি কোড লিখা হবে। পাইথনে মান না বসিয়ে শুধু ভ্যারিয়েবল ডিক্লেয়ার করতে চাইলে None ব্যবহার করা যায়। কিন্তু কিছু ক্ষেত্রে ভ্যারিয়েবল ডিক্লেয়ার না করে কোডের মধ্যে বলে রাখা যায় যে কী টাইপের ভ্যারিয়েবল থাকবে। এক্ষেত্রে variableName: datatype এই ফরম্যাট ইউজ করতে হয়।

def main():
    x: int
    y: float

    x = 22
    y = 2.5

    print(x)
    print(y)

    z: bool
    print(z) # doesn't work
    
if __name__ == "__main__":
    main()
def main
    varInt = 123123123123
    varFloat = 122.123123
    varStr = "Test string"
    varBool = false
    varNil = nil

    puts "#{varInt} is an Integer variable"
    puts "#{varFloat} is a Float variable"
    puts "#{varStr} is a String variable"
    puts "#{varBool} is a Boolean variable"
    puts "#{varNil} is a nil variable"
end

main

একটা জিনিস লক্ষ করা দরকার- সি-তে " " এর মাঝে যা লিখা হয়, সেটা হচ্ছে string ডেটা টাইপ। আর ' ' এর মাঝে যা লেখা হয় সেটা character. দুইটা আলাদা ডেটা টাইপ এবং একটার কাজ আরেকটা দিয়ে করা যায় না। এই সেকশনে string নিয়ে কোন আলোচনা হয় নি কারণ স্ট্রিং বেশ কমপ্লিকেটেড একটা আইডিয়া, এটার জন্য আলাদা সেকশন বরাদ্দ রাখাহবে। " " আর ' ' এর মধ্যে কোন পার্থক্য নেই। দুইটার মধ্যেই যা লিখা হবে সেটাই স্ট্রিং হিসাবে ধরা হবে।

এবার কিছু ছোটখাটো এক্সপেরিমেন্ট করা যাক!

বিভিন্ন ডেটা টাইপের জন্য% এর পরের ক্যারেক্টারটা চেঞ্জ করলে কী আসে? যেমন char এর জন্য %c এর বদলে %d ব্যবহার করলে কী হয়? কিংবা int এর জন্য %f% দিয়ে যেই স্ট্রিং ফরম্যাটিং করেছি সেখানে ডেটা টাইপগুলার জন্য ভুল সিম্বল ইউজ করলে কী হবে? যেমন Integer টাইপের জন্য %f ইউজ  করলে কী হয়? f-string এ {} এর ভেতর varName এর জায়গায় type(varName) রেজাল্ট কী আসে? একই ভ্যারিয়েবলে একবার এক টাইপ, আরেকবার অন্য টাইপ - এমন কিছু করা কি সম্ভব?

যেহেতু বিভিন্ন ডেটা টাইপের ক্ষেত্রে মানের লিমিট আছে , সেক্ষেত্রে এই মানগুলা অতিক্রম করলে কী হবে? যেমন long এর উদাহরণে যেই মান, সেটা int তে দিয়ে দেখা যায়। #{} এর মধ্যে varName এর পর .class ইউজ করলে (যেমন varInt.class) আউটপুট কী আসে? আমরা যেখানে varName: datatype এ একটা ডেটা টাইপের কথা লিখে পরে অন্য ডেটা টাইপ অ্যাসাইন করলে কি কোড রান হবে?

একটা float আর একটা integer ভ্যারিয়েবল নিয়ে যোগ করলে নতুন ডেটা টাইপ কী আসে? (যোগফল কোন ভ্যারিয়েবলে রাখা যাবে না) এক্ষেত্রে printf("%d\n", var1 + var2) print(f"{var1 + var2}") puts "#{var1 + var2}" এমন কিছু ব্যবহার করা যেতে পারে। char string আর int কী যোগ করা সম্ভব?

এই টাস্কগুলা এক্সপেরিমেন্টাল। টাস্কের বাইরে গিয়ে আরো কিছু (যেমন যোগ করার জায়গায় ভাগ করা) করতে মন চাইলে feel free to do so! যত বেশি এক্সপেরিমেন্ট করা হবে, ল্যাঙ্গুয়েজ বোঝা তত সহজ হবে।

Arithmetic Operators

এতদূর পর্যন্ত আমরা শুধু বিভিন্ন উপায়ে বিভিন্ন জিনিস প্রিন্ট করা দেখলাম। কিন্তু কম্পিউটারের আসল কাজ তো হিসাবনিকাশ করা। আমার মনে হয় এখন আমরাও হিসাবনিকাশ করার প্রোগ্রাম লিখা শুরু করতে পারি। একটা ছোট প্রোগ্রাম দিয়ে শুরু করা যাক-

#include <stdio.h>

void main(){
    int var1 = 20;
    int var2 = 35;
    int var3 = var1 + var2;
    
    printf("We get %d by adding %d and %d.\n", var3, var1, var2);
    printf("We get %d by subtracting %d from %d.\n", var3 - var2, var2, var3);
}
def main():
    var1 = 20
    var2 = 35
    var3 = var1 + var2
    
    print(f"We get {var3} by adding {var1} and {var2}.")
    print(f"We get {var3 - var2} by subtracting {var2} from {var3}.")
    
if __name__ == "__name__":
    main()
def main
    var1 = 20
    var2 = 35
    var3 = var1 + var2
    
    puts "We get #{var3} by adding #{var1} and #{var2}."
    puts "We get #{var3 - var2} by subtracting #{var2} from #{var3}."
end

main

প্রোগ্রামটায় আমরা দুই ধরণের গাণিতিক হিসাব করলাম। প্রথম ক্ষেত্রে var1 আর var2 এর যোগফল var3 তে রেখে এরপর var3 এর মান প্রিন্ট করলাম। আর দ্বিতীয় ক্ষেত্রে var3 আর var2 বিয়োগ করলাম কিন্তু এবার বিয়োগফল কোন ভ্যারিয়েবলে না রেখে সরাসরি প্রিন্ট করা হয়েছে।

দুই ক্ষেত্রেই যে গাণিতিক হিসাব করা হলো, এ ব্যাপারটাকে বলে arithmetic operation. আর যে চিহ্নগুলা ব্যবহার করে (এক্ষেত্রে + আর -) অ্যারিথমেটিক অপারেশন করা হয়, তাদেরকে বলে arithmetic operator.

সব প্রোগ্রামিং ল্যাঙ্গুয়েজে ৫টা অ্যারিথমেটিক অপারেটর থাকেই-

NAME SYNTAX USAGE
Addition x + y x আর y যোগ করা
Subtraction x - y x থেকে y বিয়োগ করা
Multiplication x * y x আর y গুণ করা
Division x / y y দিয়ে x কে ভাগ করা
Modulus x % y y দিয়ে x কে ভাগ করলে ভাগশেষ কতো হয় তা বের করা

এছাড়া C তে incremenet (++) আর decrement (--) নামে দুইটা অপারেটর আছে। Ruby তে Python এ x**y লিখা যায়, যার কাজ হলো x এর y তম power বের করা। আবার x // y লিখে দশমিক বাদে ভাগফল বের করা যায়। এসব অপারেটরের উদাহরণ আমরা জলদিই কোড করে দেখবো।

এর আগে একটা জিনিস বোঝা জরুরি। সেটার জন্য আমরা আবার একটু আগের উদাহরণে যাই। যখন আমরা যোগ করলাম, তখন যোগফলটা var3 তে রেখেছি। কিন্তু বিয়োগের বেলায় সেটা করিনি কিন্তু তারপরও ঠিকভাবে প্রিন্ট হয়েছে। এটা কীভাবে হলো?

সহজ ভাষায়, কম্পিউটার একটা অ্যারিথমেটিক অপারেশন করে সেই রেজাল্টটা মেমোরিতে রাখে। তারপর সেটা যদি ইউজ বা সেভ করা না হয় তাহলে রেজাল্টটা মেমোরি থেকে মুছে ফেলে। তাহলে ধরা যায় যে যখন আমরা var3 - var2 প্রিন্ট করেছি, তখন সে রেজাল্টটা একটা কাল্পনিক ভ্যারিয়েবলে সেভ হয়েছে। এরপর var3 এর মতো কাল্পনিক ভ্যারিয়েবলটা প্রিন্ট করেছি। একই জিনিস হয়েছে যখন আমরা var3 = var1 + var2 লিখেছি। প্রথমে যোগফলটা কাল্পনিক ভ্যারিয়েবলে সেভ হয়েছে, এরপর সেখান থেকে মান নিয়ে var3 তে সেভ করেছি।

এই কাল্পনিক ভ্যারিয়েবল-কে বলে Register. রেজিস্টার খুব স্বল্পস্থায়ী। যে স্টেটমেন্টে রেজিস্টারে একটা ভ্যালু সেভ হয়, সেই স্টেটমেন্টেই ইউজ করা না হলে রেজিস্টার থেকে সেই ভ্যালু মুছে যায়।

যেমন আমরা কোথাও প্রিন্ট না করে বা কোন ভ্যারিয়েবলে সেভ না করে যে কোন একটা লাইনে var1 * var2 * var3 লিখতে পারি।

এই মান আর কোথাও খুঁজে পাওয়া যাবে না। প্রিন্ট করতে হলে বা কোন নতুন ভ্যারিয়েবল var4 এ সেভ করতে হলে নতুন করে গুণ করা লাগবে।

যদিও আমরা সবগুলো অ্যারিথমেটিক অপারেটরের কথা জানলাম তারপরও এগুলা দিয়ে সব ধরণের গাণিতিক কাজ করা যাবে না। আমাদের দরকার পড়বে আরেকটা জিনিস - parentheses বা bracket এর। এমনিতে ম্যাথ করার সময় আমরা first bracket (), second bracket {}, third bracket [] ইউজ করি। কিন্তু প্রোগ্রামিং এ ম্যাথ করার জন্য শুধু () ব্যবহার করা যায়। আমরা যেটাকে $[a+{b \times (c-d)}+(e\times f)]$ লিখতে পারি, সেটা লিখতে হবে (a + (b * (c-d)) + (e * f)).

যদিও আমরা এখন পর্যন্ত দুইটা ভ্যারিয়েবল নিয়ে অ্যারিথমেটিক অপারেশন করেছি, ভ্যারিয়েবলই লাগবে এমন কোন নিয়ম নেই। আমরা চাইলেই `var2 = var1 * 2` লিখতে পারবো।

এখন একটা কুইক টাস্ক। টাস্কটা ঠিক প্রোগ্রামিং টাস্ক না, বরং খাতা কলম নিয়ে করতে হবে এমন।

(var1 + var2 - ((var3 * var4) % (var5 - 3)) / (12 + 25) - (var1 - var3 * var4) % var5) এই ক্যালকুলেশনটা করার সময় রেজিস্টারে কী কী ভ্যালু সেভ হয়েছে? কোনটার পর কোনটা? ধরা যাক varX এর ভ্যালু হচ্ছে X.

এখন আবার একটু কোড লিখা যাক-

#include <stdio.h>

void main(){
    int var1 = 15;
    int var2 = 5;
    int var3;
    
    var3 = var1 + var2; // 20
    var3 = var1 - var2; // 10
    var3 = var1 * var2; // 75
    var3 = var1 / var2; // 3
    var3 = var1 % var2; // 0
    var3 = var1 % 4; // 3
    
    var1 = var1 + 10; // 25
    var3 = var1 + 20; // 45
    
    var3 = var3 - 10; // 35
    var2 = var3 + var1; // 60
    
    var2 *= 2; // 120
    var3 -= 10; // 25
    var3 /= 5; // 5
    
    printf("Total sum of %d, %d and %d is: %d\n", var1, var2, var3, var1 + var2 + var3);
}
def main():
    var1 = 15
    var2 = 5
    
    var3 = var1 + var2 # 20
    var3 = var1 - var2 # 10
    var3 = var1 * var2 # 75
    var3 = var1 // var2 # 3
    var3 = var1 % var2 # 0
    var3 = var1 % 4 # 3
    
    var1 = var1 + 10 # 25
    var3 = var1 + 20 # 45
    
    var3 = var3 - 10 # 35
    var2 = var3 + var1 # 60
    
    var2 *= 2 # 120
    var3 -= 10 # 25
    var3 /= 5 # 5.0
    
    var4 = 15 / 4 # 3.75
    var4 = 2 ** 3 # 8
    
    print(f"Total sum of {var1}, {var2} and {var3} is: {var1 + var2 + var3}")

if __name__ == "__main__":
    main()
def main
    var1 = 15
    var2 = 5
    
    var3 = var1 + var2 # 20
    var3 = var1 - var2 # 10
    var3 = var1 * var2 # 75
    var3 = var1 / var2 # 3
    var3 = var1 % var2 # 0
    var3 = var1 % 4 # 3
    
    var1 = var1 + 10 # 25
    var3 = var1 + 20 # 45
    
    var3 = var3 - 10 # 35
    var2 = var3 + var1 # 60
    
    var2 *= 2 # 120
    var3 -= 10 # 25
    var3 /= 5 # 5
    
    var4 = 2 ** 3 # 8
    
    puts "Total sum of #{var1}, #{var2} and #{var3} is: #{var1 + var2 + var3}"
end

main

++ আর -- অপারেটর বাদে সবগুলা অপারেটরের ব্যবহারই কোডে আছে। তবে কয়েকটা স্টেটমেন্ট একটু বেশি লক্ষণীয় (যেমন var2 *= 2)। x *= 2 আর x = x * 2 একই জিনিস। দুইবার যেন একই জিনিস না লিখতে হয় এজন্য এভাবে লিখা হয়। এ ধরণের ব্যবস্থাকে বলে syntatic sugar. একইভাবে var3 /= 5 দিয়ে var3 = var3 / 5 বুঝাচ্ছে। একই কাজ আমরা বাকি অপারেটরগুলার জন্যেও করতে পারি। প্রোগ্রামিং এর সময় প্রায়ই দেখা যায় যে কোন ভ্যারিয়েবলের মান বারবার এক করে বাড়ানো লাগছে বা এক করে কমানো লাগছে। এক্ষেত্রে C তে বারবার x += 1 না লিখে x++ লিখা যায়। একইভাবে x -= 1 এর জায়গায় x-- লিখা যায়। ++ আর -- কে যথাক্রমে increment operator আর decrement operator বলে। x++ আর x-- এর মতো ++x বা --x ও লিখা যায়। তবে পার্থক্য হলো, যে স্টেটমেন্টে x++ লিখা হয়, সে স্টেটমেন্ট শেষ হলে x এর মান বাড়ে। আর ++x এর বেলায় আগে x এর মান বাড়ে আর তারপর স্টেটমেন্ট শেষ হয়।

#include <stdio.h>

void main(){
    int var1 = 15;
    printf("Value of var1: %d\n", var1++);
    printf("Value of var1: %d\n", var1);
    printf("Value of var1: %d\n", ++var1);
    printf("Value of var1: %d\n", var1);
    printf("Value of var1: %d\n", var1--);
    printf("Value of var1: %d\n", var1);
    printf("Value of var1: %d\n", --var1);
    printf("Value of var1: %d\n", var1);
    
    // let's make it more complicated
    int var2 = 12 + var1++;
    var2 = --var2 + ++var1;
    // what would be the value of var1 and var2?
}

Variables সেকশনে আমরা জেনেছিলাম কেন = কে assignment operator বলা হয়। এ জিনিসটা আবার রিপিট করতে চাই। x = y দিয়ে বুঝায় যে y ভ্যারিয়েবলের ভ্যালু-টা নিয়ে x ভ্যারিয়েবলে রাখতে হবে। ভ্যালু assign করে দেয় বিধায় = এর এমন নামকরণ।

৫টা integerভ্যারিয়েবল নিয়ে অনেকগুলা অ্যারিথমেটিক অপারেশনের কম্বিনেশন বানাতে হবে। এরপর প্রিন্ট না করে গেস করে দেখতে হবে প্রতিটা অপারেশনের পর কোন ভ্যারিয়েবলের মান কী হবে? এরপর প্রিন্ট করে মিলিয়ে দেখতে হবে সব ঠিক আছে নাকি।

এতক্ষণ আমরা integer ভ্যারিয়েবল দিয়ে সব অপারেশন করেছিলাম। একই প্রোগ্রামে কয়েকটা ভ্যারিয়েবল integer আর কয়েকটা ভ্যারিয়েবল float টাইপ এ নিয়ে রান করে দেখতে হবে প্রোগ্রাম কাজ করে নাকি। কাজ করলে আউটপুট কী আসে?

Type Casting and Constant

যেহেতু এতক্ষণে আমরা গাণিতিক ক্যালকুলেশন করতে পারি, সেহেতু একটা ছোট প্রোগ্রাম লিখে ফেলা যাক। এই প্রোগ্রামের কাজ হবে কোন একটা প্রোডাক্টের ভ্যাট নির্ণয় করা। এক্ষেত্রে আমরা পণ্যের দাম একটা ভ্যারিয়েবলে রাখবো, এরপর সেই ভ্যারিয়েবলের উপর গাণিতিক ক্যালকুলেশন করবো।

এতক্ষণে হয়তো প্রশ্ন জাগতে পারে যে আমি কীবোর্ডে টাইপ করে প্রোগ্রামে ইনপুট নেই না, শুধু বিভিন্ন ধরণের আউটপুটই কেন করছি? এক্ষেত্রে আমি আমার ভার্সিটির এক কোর্সের কথা স্মরণ করবো। ইউজারের থেকে ইনপুট নেয়া আর সে অনুযায়ী আউটপুট দেয়ার ব্যাপারটাকে বলে I/O. সেই কোর্সে আমাদেরকে বুঝানো হয়েছিল যে প্রোগ্রাম করার ক্ষেত্রে সবচেয়ে বেশি অনিশ্চয়তার সৃষ্টি এই I/O এর কারণেই হয়। যেহেতু ইউজার যে কোন ইনপুটই দিতে পারে, সেহেতু I/O এর সাইড ইফেক্ট থাকতে পারে। ইউজার ইনপুট নেয়ার আগে তাই প্রোগ্রামিং নিয়ে যথেষ্ট জ্ঞান থাকা উচিত (আমি জানি অনেক প্রোগ্রামিং গাইডেই শুরু থেকে ইউজার ইনপুট নেয়া হয়। সেগুলোকে খারাপ বলছি না)। তাই যখন সময় হবে তখন I/O এর কথা আমি অবশ্যই বলবো।

এখন ভ্যাট নির্ণয়ের সময় আমাদের কী কী তথ্য জানা লাগবে? প্রথমত, পণ্যের দাম জানা লাগবে। আর দ্বিতীয়ত, ভ্যাট কত পার্সেন্ট সেটা জানা লাগবে। পণ্যের দাম price আর ভ্যাটের পার্সেন্টেজ vat ভ্যারিয়েবলে রাখলে, টোটাল প্রাইস হবে $price(1+\frac{vat}{100}) $.

আমরা কিছু জিনিস ধারণা করে নিব। পণ্যের দাম সবসময় পূর্ণসংখ্যা (integer) হবে. ভ্যাটের পরিমাণও পূর্ণসংখ্যা (integer) হবে। অর্থাৎ ভ্যাট 10%, 15% হবে কিন্তু 2.5% হবে না। যেহেতু ভ্যাট সহ দাম ধরতে গেলে ভগ্নাংশ আসতে পারে সেহেতু মোট দামের জন্য ডেটা টাইপ হবে float. আরেকটা জিনিস আমরা ধারণা করে নিব, সেটা হচ্ছে- ভ্যাটের মান প্রোগ্রাম শুরুর পর আর চেঞ্জ করা যাবে না।

এবার এটা কোড করে ফেলা যাক-

#include <stdio.h>

const int VAT = 15;

void main(){
    int price = 125;
    float total = price * (1 + VAT / 100);
    printf("Original price: %d\nVat: %d%%\nTotal price: %f\n", price, VAT, total);
}
VAT = 15

def main():
    price = 125
    total = price * (1 + VAT / 100)
    print(f"Original price: {price}\nVat: {VAT}\nTotal price: {total}")

if __name__ == "__main__":
    main()
VAT = 15

def main()
    price = 125
    total = price * (1 + VAT / 100)
    puts "Original price: #{price}\nVat: #{VAT}%\nTotal price: #{total}"
end

main

শুরুতেই আমরা লিখেছি const int VAT = 15; VAT = 15 এমনভাবে লেখার কারণ হচ্ছে, যেন প্রোগ্রামের মাঝে ভ্যাটের মান চেঞ্জ করা না যায়। const হচ্ছে একটা কী-ওয়ার্ড, যেটা ভ্যারিয়েবল ডিক্লারেশনের সময় বসালে সেই ভ্যারিয়েবলের মান চেঞ্জ করা যাবে না। অর্থাৎ ভ্যারিয়েবলটা তখন constant এ পরিণত হয়। রুবি-তে যখন কোন ভ্যারিয়েবলের নাম সম্পূর্ণ বড় হাতের অক্ষরে লেখা হয়, তখন কম্পিউটার ধরে নেয় যে এই ভ্যারিয়েবলের মান পরিবর্তন করা যাবে না। অর্থাৎ ভ্যারিয়েবলটা constant এ পরিণত হয়। পাইথনে আসলে কম্পিউটারকে আলাদাভাবে বলা যায় না যে কোন একটা ভ্যারিয়েবলের মান পরিবর্তন করা যাবে না। বড় হাতের অক্ষরে লেখা হয় যেন কোড করার সময় কেউ ভুলে মান পরিবর্তন করে না ফেলে। অর্থাৎ সাইকোলজিকালি ভ্যারিয়েবলটাকে constant বানানো হয়।

main এর পরের লাইনেই VAT এর মান পরিবর্তন করে দেখা যেতে পারে প্রোগ্রাম চলে নাকি?

আচ্ছা আমরা কিন্তু এই প্রথম বয়লারপ্লেটের যেখানে কোড লিখতে বলা হয়েছিল, তার বাইর কোড লিখলাম! যখন আমরা প্রোগ্রামে constant গুলা ডিক্লেয়ার করি, তখন এভাবে প্রোগ্রামের শুরুতেই সেটা করে ফেলা হয়। আরেকটা ব্যাপার লক্ষ করা দরকার- স্ট্রিং ফরম্যাটিং এর সময় আমরা যেখানে % প্রিন্ট করার কথা সেখানে %% লিখেছি। এর কারণ, % এর পর বিভিন্ন অক্ষর দিয়ে আমরা বিভিন্ন ডেটা টাইপ বুঝাই। তাই যখন আমাদের %-ই প্রিন্ট করা লাগবে, তখন %% লিখা লাগে।

যাই হোক, কোডে কী লেখা হয়েছে সব বুঝা গেল। এবার আউটপুট দেখা যাক। কোন কারণে টোটাল প্রাইস 143.75 এর জায়গায় 125 আসছে। এর কারণ কী?

কারণ বের করতে হলে আমাদের আবার রেজিস্টারে ফিরে যেতে হবে। total বের করার সময় রেজিস্টারে প্রথম যে ভ্যালু সেভ হলো সেটা হচ্ছে VAT / 100. এখন VAT আর 100 দুইটার ডেটা টাইপই হলো integer. যখন আমরা দুইটা ইন্টিজারের মধ্যে অপারেশন করি, তখন রেজাল্টও পূর্ণসংখ্যা-ই হবে। যখন আমাদের কাছে একটা float মান থাকে, কিন্তু কম্পিউটারের সেটা integer হিসাবে সেভ করতে হয়, তখন সে দশমিকের পরের অংশটা বাদ দিয়ে দেয়। তাই 15/100 এর মান 0.15 হলেও রেজিস্টারে সেভ হবে 0. এরপর বাকি অংক তো সহজ। সবার শেষে রেজাল্ট আসলো 125.
যেহেতু মানটা একটা float ভ্যারিয়েবলে সেভ করা হয়েছে, সেজন্য প্রিন্ট হয়েছে 125.0000রেজাল্ট আসছে 143.75. যেমনটা হওয়ার কথা তেমনটাই হয়েছে। তবে এখানে একটা সমস্যা যোগ করা যায়… যেহেতু দোকানে ভগ্নাংশে টাকা নেয়া হয় না, ধরা যাক 0.75 টাকা ডিস্কাউন্ট দেয়া হবে। সেক্ষেত্রে total ভ্যারিয়েবলে 143 সেভ করা লাগবে। সেটা কীভাবে করা যায়? (Hint: যখন আমাদের কাছে একটা float মান থাকে, কিন্তু কম্পিউটারের সেটা integer হিসাবে সেভ করতে হয়, তখন সে দশমিকের পরের অংশটা বাদ দিয়ে দেয়) 

এই সমস্যা সমাধানের জন্য আমাদের যেটা লাগবে, সেটা হচ্ছে Type Casting. টাইপ কাস্টিং হলো যখন এক ধরণের ডেটা টাইপকে আরেক ধরণের ডেটা টাইপে নেয়া হয়। টাইপ কাস্টিং সবচেয়ে বেশি দরকার পরে বিভিন্ন ক্যালকুলেশনের জন্য যখন পূর্ণসংখ্যা থেকে দশমিকে কিংবা দশমিক থেকে পূর্ণসংখ্যায় পরিবর্তন করা লাগে।

টাইপ কাস্টিং করে VAT-কে float এ নিলে total-কে int তে নিলে আমাদের কোড হবে এরকম-

#include <stdio.h>

const int VAT = 15;

void main(){
    int price = 125;
    float total = price * (1 + (float) VAT / 100);
    printf("Original price: %d\nVat: %d%%\nTotal price: %f\n", price, VAT, total);
}
VAT = 15

def main()
    price = 125
    total = price * (1 + VAT.to_f / 100);
    puts "Original price: #{price}\nVat: #{VAT}%\nTotal price: #{total}"
end

main
VAT = 15

def main():
    price = 125
    total = price * (1 + VAT / 100)
    total = int(total)
    print(f"Original price: {price}\nVat: {VAT}\nTotal price: {total}")

if __name__ == "__main__":
    main()

এখন আমরা যে মান চাচ্ছিলাম সেটাই পাচ্ছি। আমরা VAT এর আগে (float) লিখে বুঝিয়েছি যে VAT এর যে মান আছে, সেটাকে float এ পরিবর্তন করতে হবে। একইভাবে আমরা কোন একটা float ভ্যালুর আগে (int) লিখে বুঝাতে পারি যে একে ইন্টিজারে নিতে হবে। একই জিনিস অন্য ডেটা টাইপ যেমন long বা double এর জন্য করা যায়। খেয়াল রাখা দরকার যে (type) variable ও একটা অপারেশন, আর এর রেজাল্ট রেজিস্টারে সেভ হয়। তাই আসল ভ্যারিয়েবলের টাইপ চেঞ্জ হবে না। এখানে VAT এর পর .to_f দিয়ে বুঝানো হয়েছে যে VAT এর মান-টাকে float এ পরিবর্তন করতে হবে। একইভাবে আমরা কোন float ভ্যারিয়েবলের পর .to_i লিখে বুঝাতে পারি যে মানটাকে integer এ কনভার্ট করতে হবে। এরকম .to_s দিয়ে স্ট্রিং এ মান পরিবর্তন করা যায়। খেয়াল রাখা দরকার যে, এভাবে করে ডেটা টাইপ যে চেঞ্জ করছি, সেটা কেবল রেজিস্টারে সেভ হচ্ছে। আসল ভ্যারিয়েবল পরিবর্তন হচ্ছে না। এখানে VAT বাদে অন্য কোন মানের ডেটা টাইপ চেঞ্জ করতে হয় নি, কেননা integer আর float এর মধ্যে ম্যাথেমেটিকাল অপারেশন করতে হলে রেজাল্ট সবসময় float হিসাবে সেভ হয়। এখানে int(total) দিয়ে বুঝানো হয়েছে যে total ভ্যারিয়েবলের যে মান সেটাকে int ডেটা টাইপে পরিবর্তন করতে হবে। একইভাবে আমরা float( ) এর মধ্যে কোন int ভ্যারিয়েবল রেখে সেটাকে float এ পরিবর্তন করতে পারি। একই জিনিস অন্য ডেটা টাইপ যেমন str বা bool এর জন্যও করা যায়।

এবার বিভিন্ন ডেটা টাইপ নিয়ে টাইপকাস্টিং করে দেখা যাক কোন কোন ক্ষেত্রে ডেটা টাইপ পরিবর্তন করা যায়। যেমন char string থেকে কি integer এ পরিবর্তন করা সম্ভব? অথবা উল্টোটা?

ভ্যাট ক্যালকুলেটর প্রোগ্রামটাকে আরেকটু বাড়ানো যাক। পাঁচটা প্রোডাক্টের দামের উপর ভ্যাট ক্যালকুলেট করা হবে। প্রতিটা প্রোডাক্টের দাম হবে float আর VAT হবে integer. সাথে সার্ভিস চার্জ হবে 2.5%. ভ্যাট আর সার্ভিস চার্জ চেঞ্জ করা যাবে না। এরপর নিচের কাজগুলো করা লাগবে-

  • ভ্যাট আর সার্ভিস চার্জ হিসাব করে টোটাল কত খরচ হবে সেটা বের করা লাগবে। (এক্ষেত্রে ৫টা পণ্যের মোট দাম বের করে গুণ করা যেতে পারে, কিংবা প্রতিটার জন্য আলাদা চার্জ বের করে যোগ করা যেতে পারে)
  • চার্জ যা আসবে, সেটায় দশমিকের পর যা আছে সে অংশটুকু বাদ দেয়া লাগবে।
  • যেহেতু অনেক কিছু কেনা হয়েছে, সেহেতু ডিস্কাউন্ট হিসাবে এককের ঘরে যা আছে তা বাদ যাবে (যেমন 125 বিল আসলে পে করতে হবে 120 বা 288 বিল আসলে আসলে পে করতে হবে 280)।
  • সবশেষে কত ডিস্কাউন্ট আসলো, আর কত বিল আসলো, সেটা প্রিন্ট করতে হবে। Hint: এককের ঘরে যা আছে তা বাদ দিতে হলে 10 দিয়ে ভাগ করে আবার 10 দিয়ে গুণ করা যায়। ডিস্কাউন্ট কত টাকা সেটা বের করতে হলে modulus operator ব্যবহার করতে হবে।
  • আউটপুট স্ট্রিং অনেকটা এরকম হবে- Total Bill: 128\nPayable: 120\nDiscount:8

Boolean Expressions and Conditional

To be continued