সাক্ষাৎ

সাক্ষাৎ

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

ভূমিকা

সিলেবাসের এই পর্যায়ে এসে আপনি ইতিমধ্যে অনেক ফাংশন ডেফিনিশন দেখে ফেলেছেন। সাধারণত এগুলো REPL-এ আউটপুট নিয়ে তেমন কোনো মন্তব্য ছাড়াই উপস্থাপন করা হতো:

julia> f(x) = x^2
f (generic function with 1 method)

কেন "generic function with 1 method"?

ধরুন আমরা আর্গুমেন্টের টাইপ নিয়ে আরও নির্দিষ্ট হতে চাই।

julia> f(x::Integer) = x^2
f (generic function with 2 methods)

julia> f(x::Real) = x^2
f (generic function with 3 methods)

অথবা আমরা আর্গুমেন্টের সংখ্যা বদলাতে পারি:

julia> f(x, y) = x * y
f (generic function with 4 methods)

দেখা যাচ্ছে, একই ফাংশনের নতুন ডেফিনিশনগুলোকে "মেথড" বলা হয়। পুরনো ডেফিনিশন মুছে দিয়ে লেখার বদলে মেথডগুলো একটি তালিকায় যোগ করা হয়।

আমরা সহজেই মেথডগুলোর তালিকা দেখতে পারি:

julia> methods(f)  # output edited for brevity
# 4 methods for generic function "f" from Main:
 [1] f(x, y)
 [2] f(x::Integer)
 [3] f(x::Real)
 [4] f(x)

আমরা যদি f ফাংশনটি কল করি, তবে জুলিয়া কোনোভাবে বুঝে নেয় কোন মেথড ব্যবহার করতে হবে:

julia> f(2.5)  # Float64
6.25

julia> f(2)  # Int64
4

julia> f(2, 3)  # Int64, Int64
6

ফাংশন কী?

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

f(2, 3)-এর ফাংশন আর্গুমেন্টগুলোকে আমরা সাধারণত দুটি আলাদা জিনিস হিসেবে ভাবি, কিন্তু কম্পাইলার সেগুলোকে একটি টাপল হিসেবে দেখে। একটি মাত্র আর্গুমেন্টের ক্ষেত্রেও এটি সত্য (যদিও বিষয়টি পরিষ্কার করতে আমাদের একটি কমা যোগ করতে হয়):

julia> typeof( (2, 3) )
Tuple{Int64, Int64}

julia> typeof( (2,) )
Tuple{Int64}

ফাংশনটিকে একটি ব্ল্যাক বক্স হিসেবে দেখলে বলা যায়, ভেতরে ঢোকে একটি টাপল আর বেরিয়ে আসে একটি রিটার্ন ভ্যালু। আরও আনুষ্ঠানিকভাবে বললে, টাপলটিকে একটি রিটার্ন ভ্যালুতে ম্যাপ করা হয়।

ফাংশন কীভাবে বিভিন্ন সংখ্যা ও টাইপের আর্গুমেন্ট সামলায়?

এটিকে সাধারণত "পলিমরফিজম" বলা হয়

কয়েক দশক ধরে এটি কম্পিউটার সায়েন্সের একটি বড় বিষয়, আর এর জন্য বিভিন্ন পদ্ধতি অবলম্বন করা হয়েছে।

জুলিয়ার পদ্ধতিটি অস্বাভাবিক, যদিও একেবারে অনন্য নয়।

  • ফাংশনগুলো "জেনেরিক", অর্থাৎ এদের সাথে একাধিক মেথড যুক্ত থাকতে পারে।
  • মেথড যুক্ত হওয়ার আগে ফাংশন আসলে নিছক একটি নাম।
  • প্রয়োজন অনুযায়ী মেথড যোগ করা যায়, প্রয়োজনে বিশাল সংখ্যায় (+ অপারেটরটি বর্তমানে ১৬১টি মেথডওয়ালা একটি ফাংশন)।
  • কোনো ফাংশন কল করা হলে জুলিয়া কল সিগনেচার পরীক্ষা করে: আর্গুমেন্টের সংখ্যা এবং প্রতিটির বর্তমান টাইপ।
  • কল সিগনেচারের সাথে সবচেয়ে বেশি মিলে যায় এমন মেথডটি বেছে নেওয়া হয়।
    • সাধারণভাবে আর্গুমেন্টের সংখ্যা হুবহু মিলতে হবে (যদিও স্প্ল্যাট/স্লার্প অপারেটর ... আংশিকভাবে এটি এড়িয়ে যেতে পারে)।
    • যেসব মেথডের টাইপ কল সিগনেচারের সাথে হুবহু মেলে, সেগুলোকে অগ্রাধিকার দেওয়া হয়।
    • প্রয়োজনে জুলিয়া কল সিগনেচারের প্রতিটি আর্গুমেন্টের সুপারটাইপ দেখে সবচেয়ে বেশি মিলে যাওয়া, সবচেয়ে নির্দিষ্ট মেথডটি খুঁজে বের করে।
  • নির্বাচিত মেথডটিকে প্রদত্ত আর্গুমেন্টসহ dispatched করা হয়, আর সেটি একটি ফলাফল রিটার্ন করে।
  • যদি উপযুক্ত কোনো মেথড খুঁজে না পাওয়া যায়, তবে একটি MethodError উত্থাপিত হয়।

মনে রাখবেন, জুলিয়া ডাইনামিকভাবে টাইপড, তাই মেথড ডিসপ্যাচ ঘটে রান টাইমে।

মাল্টিপল ডিসপ্যাচ: একটি উদাহরণ

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

জ্যামিতিক আকৃতি বোঝাতে প্রথমে কিছু কাস্টম টাইপ ডিফাইন করে নিন।

julia> abstract type Shape end

julia> struct Circle <: Shape
           radius::Float64
       end

julia> struct Square <: Shape
           side::Float64
       end

julia> struct Rectangle <: Shape
           length::Float64
           width::Float64
       end

এরপর প্রতিটি আকৃতির ক্ষেত্রফল বের করার জন্য আমরা মেথড ডিফাইন করতে পারি। প্রতিটিই একটি আর্গুমেন্ট নেয়, আর প্রতিটি ক্ষেত্রেই সেটি আলাদা একটি কংক্রিট, ব্যবহারকারী-নির্ধারিত টাইপের।

julia> area(c::Circle) = π * c.radius^2
area (generic function with 1 method)

julia> area(s::Square) = s.side^2
area (generic function with 2 methods)

julia> area(r::Rectangle) = r.length * r.width
area (generic function with 3 methods)

এখন আমরা কিছু আকৃতি তৈরি করে ক্ষেত্রফল হিসাব করতে পারি, প্রতিটির জন্য সঠিক সূত্র ব্যবহারের দায়িত্ব জুলিয়ার ডিসপ্যাচ মেকানিজমের উপর ছেড়ে দিয়ে।

julia> circle = Circle(2.3)
Circle(2.3)

julia> area(circle)  # call signature Tuple{Circle}
16.619025137490002

julia> square = Square(1.6)
Square(1.6)

julia> area(square)  # call signature Tuple{Square}
2.5600000000000005

julia> rectangle = Rectangle(1.4, 2.1)
Rectangle(1.4, 2.1)

julia> area(rectangle)  # call signature Tuple{Rectangle}
2.94

প্যারামেট্রিক মেথড

মেথড সিগনেচার প্যারামেট্রিক টাইপের মতোই মোটামুটি একইভাবে ডামি টাইপ মান নিতে পারে।

সিনট্যাক্সটি স্বতন্ত্র, এতে একটি where ক্লজ লাগে:

julia> same_type(v::Vector{T}, x::T) where {T} = true
same_type (generic function with 1 method)

julia> same_type([1, 2], 3)
true

julia> same_type([1, 2], "three")
ERROR: MethodError: no method matching same_type(::Vector{Int64}, ::String)

উপরের উদাহরণে দুটি আর্গুমেন্টকেই একই টাইপ T ব্যবহার করতে হয়, সেটি যা-ই হোক।

T-এর উপর একটি কনস্ট্রেইন্ট বসিয়ে আমরা আরও নির্দিষ্ট হতে পারি:

julia> same_type(v::Vector{T}, x::T) where {T<:Number} = true
same_type (generic function with 2 methods)

এই মেথডে কেবল অভিন্ন সংখ্যাসূচক টাইপই true রিটার্ন করে।

একাধিক ডামি ভ্যারিয়েবলও সম্ভব, যা where ক্লজে কমা দিয়ে আলাদা করা হয়।

julia> myfunc(a::S, b::T) where {S, T} = true
myfunc (generic function with 1 method)

উপসংহার

জুলিয়ায় মাল্টিপল ডিসপ্যাচ খুবই নমনীয় এবং দুর্দান্ত পারফরম্যান্স দেয়, তাই এটি ব্যাপকভাবে ব্যবহৃত হয়।

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

টাইপ যত নির্দিষ্ট হবে, কম্পাইলার তত বেশি অপটিমাইজেশন করতে পারে: Exercism-এর একটি ছোট্ট অনুশীলনীতে এর গুরুত্ব প্রান্তিক, কিন্তু জুলিয়া যে বড় বড় সংখ্যাসূচক সিমুলেশনের জন্য তৈরি, সেখানে পারফরম্যান্সের জন্য এটি অপরিহার্য।

আপনি যদি জানেন একটি মেথড কী কী ইনপুট পাবে, তবে কম্পাইলারকে জানিয়ে দিন!

নির্দেশনা

এই অনুশীলনীতে আপনি সাক্ষাতের একটি সিমুলেশন তৈরি করবেন। এতে আপনি Julia-এর প্রধান প্যারাডাইম মাল্টিপল ডিসপ্যাচের মূল বিষয়গুলোর সাথে পরিচিত হবেন।

For this exercise, all function definitions should be a single-line declaration with no if/else logic.

সাধারণভাবে সাক্ষাতে একটি এনটিটি a আরেকটি এনটিটি b-এর মুখোমুখি হয় এবং তার প্রতি প্রতিক্রিয়া জানায়।

encounter(a, b) = "$(name(a)) meets $(name(b)) and $(meets(a, b))."

শুরুতে আমরা সিমুলেট করব কী ঘটে যখন বিড়াল ও কুকুরের দেখা হয়।

1. কাস্টম টাইপ ডিফাইন করুন

প্রথমে একটি অ্যাবস্ট্রাক্ট টাইপ Pet যোগ করুন।

এরপর Cat ও Dog টাইপকে Pet-এর সাবটাইপ হিসেবে ডিফাইন করুন। প্রতিটির একটি করে ফিল্ড থাকে: name।

2. পোষা প্রাণীর নাম বের করুন।

এমন একটি ফাংশন name() ইমপ্লিমেন্ট করুন যা পোষা প্রাণীর নাম রিটার্ন করে।

julia> fido = Dog("Fido")
Dog("Fido")

julia> name(fido)
"Fido"

3. বিড়াল ও কুকুরের দেখা হলে কী ঘটে তা ডিফাইন করুন

নিচের সাক্ষাতগুলোর জন্য meets() মেথড ইমপ্লিমেন্ট করুন:

  • দুটি কুকুরের দেখা হলে তারা একে অপরকে sniff করে।
  • বিড়ালের সাথে কুকুরের দেখা হলে বিড়াল কুকুরের দিকে hisses করে।
  • কুকুরের সাথে বিড়ালের দেখা হলে কুকুর বিড়ালকে chases করে।
  • দুটি বিড়ালের দেখা হলে তারা slink করে।
julia> ginger = Cat("Ginger")
Cat("Ginger")

julia> meets(ginger, fido)
"hisses"

4. দুটি এনটিটির মধ্যে সাক্ষাৎ ডিফাইন করুন।

নামযুক্ত দুটি এনটিটির দেখা হলে কী ঘটে তা জানানোর জন্য একটি মেথড দিয়ে encounter() ফাংশনটি ইমপ্লিমেন্ট করুন।

julia> encounter(ginger, fido)
"Ginger meets Fido and hisses."

5. পোষা প্রাণীদের মধ্যে সাক্ষাতের জন্য একটি ফলব্যাক প্রতিক্রিয়া ডিফাইন করুন

তারা হাঁটতে গিয়ে অন্য কোনো পোষা প্রাণীর মুখোমুখি হলে কী ঘটে, যেমন একটি ঘোড়া?

  • কোনো পোষা প্রাণীর সাথে এমন আরেক পোষা প্রাণীর দেখা হলে যাকে সে চেনে না, সে is cautious।
julia> bella = Horse("Bella")
Horse("Bella")

julia> encounter(fido, bella)
"Fido meets Bella and is cautious."

6. পোষা প্রাণী তার অজানা কোনো কিছুর মুখোমুখি হলে একটি ফলব্যাক ডিফাইন করুন

আরও অনেক কিছু আছে যা পোষা প্রাণীদের সামনে আসতে পারে, অথচ সেগুলো পোষা প্রাণী নয়: গাড়ি, মানুষ, গাছপালা, প্রাকৃতিক দুর্যোগ, গ্রহাণু… তাহলে কী ঘটে?

  • কোনো পোষা প্রাণী আগে কখনো দেখেনি এমন কিছুর মুখোমুখি হলে সে runs away।
julia> colnago = Bicycle("Colnago")
Bicycle("Colnago")

julia> encounter(ginger, colnago)
"Ginger meets Colnago and runs away."

7. একটি জেনেরিক ফলব্যাক ডিফাইন করুন

আমাদের সিমুলেশনে আরও নানা রকম সাক্ষাৎ ঘটতে পারে। একটি গাড়ির সাথে একটি কুকুরের দেখা হয়, দুটি ইলেকট্রন পরস্পরের মুখোমুখি হয় এবং ইন্টারঅ্যাক্ট করে…

সব সাক্ষাৎ আগে থেকে কভার করা অসম্ভব, তাই আমরা একটি জেনেরিক ফলব্যাক ইমপ্লিমেন্ট করব।

  • দুটি অজানা জিনিস বা সত্তার সাক্ষাৎ হলে nothing happens।
julia> γ1 = Photon("γ1")
Photon("γ1")

julia> γ2 = Photon("γ2")
Photon("γ2")

julia> encounter(γ1, γ2)
"γ1 meets γ2 and nothing happens." 
# This is true, photons just pass through each other.
GitHub-এর মাধ্যমে সম্পাদনা করুন লিংকটি একটি নতুন উইন্ডো বা ট্যাবে খোলে
Julia Exercism

সাক্ষাৎ শুরু করতে প্রস্তুত?

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