ট্র্যাক
/
Julia
Julia
/
অনুশীলনী
/
গ্রেডিং গুরু
গ্রেডিং গুরু

গ্রেডিং গুরু

লার্নিং অনুশীলনী

ভূমিকা

এখন পর্যন্ত সিলেবাসে টাইপ নিয়ে বিশেষ কিছু বলা হয়নি, তবে Julia-তে যে টাইপ আছে, তা স্পষ্টই:

julia> vals = (42, 4.3, π, "hello", 'Q')
(42, 4.3, π, "hello", 'Q')
julia> typeof(vals)
Tuple{Int64, Float64, Irrational{:π}, String, Char}

আমরা কখনও টাইপ নির্দিষ্ট করিনি, তবুও Julia সেগুলো নিজে থেকেই অ্যাসাইন করে দিয়েছে।

  1. Julia-তে types আছে, যা এর ডিজাইনের কেন্দ্রে রয়েছে।
  2. Julia সাধারণত Type Inference ব্যবহার করে টাইপটি "অনুমান" করতে পারে।

JIT কম্পাইলার (সব) কোড দেখে নেবে, একটি ভ্যারিয়েবল কীভাবে ব্যবহৃত হচ্ছে তা দেখবে, এবং সেই ব্যবহারের সঙ্গে মানানসই একটি উপযুক্ত ডিফল্ট টাইপ ইনফার করবে।

টাইপ অ্যাসাইনমেন্ট

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

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

julia> x = Int16(42)
42
julia> typeof(x)
Int16

তবে ভ্যারিয়েবলটিকে x এখনও ভিন্ন একটি টাইপে আবার অ্যাসাইন করা যায়:

julia> x = "changed"
"changed"
julia> typeof(x)
String

একে বলে type instability, যা হলো:

  • ছোট স্ক্রিপ্টে খুবই সুবিধাজনক।
  • বড় প্রোগ্রামে পারফরম্যান্স ও নির্ভরযোগ্যতার জন্য খারাপ।

বদলে, :: অপারেটর ব্যবহার করে আমরা x-এর টাইপ নির্দিষ্ট করে দিতে পারি:

julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> y = "changed"
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int16
The function `convert` exists, but no method is defined for this combination of argument types.

এখন y-এর টাইপ Int16, এবং সবসময় তাই থাকবে। ফলে কম্পাইলার জানে এর জন্য কত বাইট সংরক্ষণ করতে হবে, আর স্থির একটি টাইপের ওপর ভরসা করে বাকি কোডটি অপ্টিমাইজ করতে পারে। এই দিক থেকে ভ্যারিয়েবলটি C-এর মতো স্ট্যাটিক-টাইপড ভাষার ভ্যারিয়েবলের সঙ্গে কমবেশি একই রকম।

টাইপ হায়ারার্কি

Int64, Int16, String, Char: এই টাইপগুলো আসে কোথা থেকে।

অনেক অবজেক্ট-ওরিয়েন্টেড (OO) ভাষায় প্রতিটি টাইপ একটি ক্লাস, সাবক্লাসিং তাদের একটি ক্লাস হায়ারার্কিতে সাজায়, আর ক্লাস মেথডগুলো আচরণ নির্ধারণ করে।

Java আর Ruby এই প্যাটার্নের স্পষ্ট উদাহরণ, তবে Python-ও ভেতরে ভেতরে একই রকম।

Julia-তে কোনো ক্লাস নেই।

নথিভুক্ত কারণটি হলো, OO ফিচারগুলো JIT কম্পাইলারের কাজে বাধা দেয় এবং রানটাইম পারফরম্যান্স নষ্ট করে।

তবুও, এই কোডটি দেখুন:

julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> supertype(Int16)
Signed
julia> supertypes(Int16)
(Int16, Signed, Integer, Real, Number, Any)

কিছু বিবরণ যোগ করলে:

  • Int16 একটি টাইপ, এবং আমরা এই টাইপের ভ্যারিয়েবল বানাতে পারি।
  • Int16 হলো Signed টাইপের একটি সাবটাইপ, এবং supertype() ফাংশনটি তা আমাদের দেখায়।
  • টাইপের একটি হায়ারার্কি আছে, যা Integer, Real এবং Number হয়ে একেবারে ওপরে Any পর্যন্ত ওঠে, আর subtypes() আমাদের জন্য হায়ারার্কির এই শাখাটি তালিকাভুক্ত করবে।

সব শাখাই শেষ হয় Any-তে, যেটি অনন্য এই অর্থে যে এটিই নিজের সুপারটাইপ।

julia> supertypes(String)
(String, AbstractString, Any)
julia> supertype(Any)
Any

তাহলে, Julia-তে কোনো class হায়ারার্কি নেই, তবে type হায়ারার্কি আছে।

একসময় আমরা এটি কীভাবে কাজ করে তা খুঁটিয়ে দেখব, তবে আগে আরও অনেক কিছু ঘুরে দেখার বাকি আছে।

টাইপ পরীক্ষা করা

সাধারণ উপায়েই আমরা সমতা পরীক্ষা করতে typeof() ব্যবহার করতে পারি।

julia> typeof(11)
Int64
julia> typeof(11) == Int64
true
julia> typeof(11) == Number
false

টাইপের সমতা অবশ্যই হুবহু হতে হবে, কারণ তুলনার এই রূপটি টাইপ হায়ারার্কি সম্পর্কে কিছুই জানে না।

আরও নমনীয়ভাবে, isa আমাদের জানাবে একটি মানের টাইপ তুলনাকারী টাইপের সমান কি না, কিংবা তার সাবটাইপ কি না। এটি ফাংশন আকারে বা ইনফিক্স আকারে, দুইভাবেই ব্যবহার করা যায়।

julia> 12 isa Int64
true
julia> 12 isa Number
true
julia> isa(12, Number)
true
julia> 12 isa String
false

লক্ষ্য করুন, isa বাম দিকে একটি value আশা করে, type নয়।

এইভাবে দুটি টাইপ তুলনা করতে গেলে অপ্রত্যাশিত ফলাফল আসবে। সঠিক অপারেটরটি হলো <:, যা আমরা ভবিষ্যতের কনসেপ্টগুলোতে আরও অনেকবার দেখব।

julia> Int64 isa Number  ## Don't do this!
false
julia> Int64 <: Number
true

অ্যাবস্ট্রাক্ট বনাম কংক্রিট টাইপ

আমরা দেখেছি, টাইপ হায়ারার্কি একটি গাছের কাঠামো তৈরি করে (কম্পিউটার সায়েন্সের অর্থে, যার মূল থাকে একেবারে ওপরে)।

গাছের প্রতিটি উপাদান একটি node, এবং এগুলোকে কয়েকটি শ্রেণিতে ভাগ করা যায়:

  1. যেসব নোডের সাবটাইপ থাকে, তাদের abstract বলা হয়।
  2. যেসব পাতার নোডের কোনো সাবটাইপ নেই, তাদের concrete বলা হয়।
julia> subtypes(Integer)  # an abstract type
3-element Vector{Any}:
 Bool
 Signed
 Unsigned
julia> subtypes(Int64)  # a concrete type
Type[]

এটি একটি গুরুত্বপূর্ণ পার্থক্য, কারণ কেবল কংক্রিট টাইপকেই ভ্যারিয়েবল হিসেবে instantiated করা যায়।

julia> a::Int16 = 42
42
julia> typeof(a)
Int16
julia> b::Integer = 42
42
julia> typeof(b)
Int64

লক্ষ্য করুন, একটি অ্যাবস্ট্রাক্ট টাইপ ব্যবহার করতে গেলে (এই ক্ষেত্রে) কোনো এরর বার্তা আসে না, বরং কম্পাইলার উপযুক্ত একটি কংক্রিট টাইপ তৈরি করে: Integer-এর বদলে Int64।

নির্দেশনা

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

গ্রেডিং স্কেল সাধারণত ০ থেকে ১০ পর্যন্ত হয়, তাই প্রতি গ্রেডে মাত্র আট বিট থাকে এমন একটি ডেটা টাইপ ব্যবহার করাই সবচেয়ে কার্যকর।

  • কিছু প্রফেসর বাস্তব সংখ্যা ব্যবহার করেন (কারণ তাঁরা নির্ভুলতার ব্যাপারে বড় খুঁতখুঁতে), আর এগুলোকে আপনাকে UInt8 ডেটা টাইপে কনভার্ট করতে হবে।
  • অন্য প্রফেসররা ঋণাত্মক সংখ্যাসহ ইন্টিজার ব্যবহার করেন (বিশেষত দুর্বিনীত শিক্ষার্থীদের শাস্তি দিতে), আর এসব ক্ষেত্রে এগুলোকে আপনাকে Int8 ডেটা টাইপে কনভার্ট করতে হবে।

গ্রেড কনভার্ট করার ফাংশনটি তৈরি হয়ে গেলে, গ্রেডগুলো যে কালেকশনে রাখা আছে সেগুলো সামলানোর জন্য আপনাকে আরেকটি ফাংশন লিখতে হবে, যা গ্রেডগুলো ডিমোট করে অবরোহক্রমে রিটার্ন করবে।

  • কিছু প্রফেসর তাঁদের ডেটা নিয়ে খুব খুঁটিনাটি খেয়াল রাখেন, তাই গ্রেড রাখতে ঊর্ধ্বক্রমে সাজানো Vector ব্যবহার করেন।
  • অন্য প্রফেসররা একটু বেশি অলস, তাই গ্রেড রাখতে Set ব্যবহার করেন, যেগুলো সাজানো থাকে না।

দুটি ফাংশনেই, অবৈধ ইনপুট সামলাতে আপনাকে একটি MethodError থ্রো করতে হবে।

Note

এক্সেপশন হ্যান্ডলিং পরের কোনো কনসেপ্টে আলোচনা করা হবে, তাই এই অনুশীলনীর উদ্দেশ্যে আপনি নিচের সিনট্যাক্সটি ব্যবহার করতে পারেন:

throw(MethodError(f, args))

এখানে f হলো ফাংশন, আর args হলো ফাংশনে দেওয়া আর্গুমেন্টগুলোর একটি টাপল। demote(n) ফাংশনের ক্ষেত্রে f হলো demote আর args হলো (n,):

throw(MethodError(demote, (n,)))

1. গ্রেডগুলো ডিমোট করুন

demote(n) মেথডটি ইমপ্লিমেন্ট করুন।

  • Float64 ইনপুট পেলে, এটি নিকটতম পূর্ণসংখ্যায় রাউন্ড আপ করবে এবং একটি UInt8 ডেটা টাইপ রিটার্ন করবে।
  • যেকোনো Integer ইনপুট পেলে, এটি একই ইন্টিজার একটি Int8 ডেটা টাইপে রিটার্ন করবে।
  • বাকি সব ইনপুট একটি MethodError থ্রো করবে।
julia> demote(4.2)::UInt8
5

julia> demote(4)::Int8
4

julia> demote("hi")
MethodError: no method matching demote(::String)     #output truncated

2. ডেটা প্রিপ্রসেস করুন

preprocess(coll) মেথডটি ইমপ্লিমেন্ট করুন।

  • Vector ইনপুট পেলে, এটি সব সংখ্যা ডিমোট করবে এবং ভেক্টরটি উল্টে দেবে।
  • Set ইনপুট পেলে, এটি সব সংখ্যা ডিমোট করবে এবং অবরোহক্রমে সাজানো একটি ভেক্টর রিটার্ন করবে।
  • বাকি সব ইনপুট একটি MethodError থ্রো করবে।
julia> preprocess([1, 2, 3])
3-element Vector{Int8}:
 3
 2
 1

julia> preprocess(Set([2.2, 5.8, 3.4]))
3-element Vector{UInt8}:
 6
 4
 3

julia> preprocess(42)
MethodError: no method matching preprocess(::Int64)     #output truncated
GitHub-এর মাধ্যমে সম্পাদনা করুন লিংকটি একটি নতুন উইন্ডো বা ট্যাবে খোলে
Julia Exercism

গ্রেডিং গুরু শুরু করতে প্রস্তুত?

Exercism-এ সাইন আপ করুন, Julia ট্র্যাকের 35টি কনসেপ্ট128টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।