রেকর্ড হলো এমন ডেটা স্ট্রাকচার, যা সম্পর্কিত তথ্যকে লেবেল দিয়ে একসাথে গুছিয়ে রাখে। এগুলো JavaScript বা Java-র অবজেক্ট, কিংবা C বা Rust-এর স্ট্রাক্টের মতো, তবে কিছু গুরুত্বপূর্ণ পার্থক্য আছে।
রেকর্ড তৈরি করা হয় দ্বিতীয় বন্ধনী দিয়ে, আর এর এলিমেন্টগুলোকে কমা দিয়ে আলাদা করা হয়।
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
রেকর্ডের টাইপও একই ধরনের সিনট্যাক্স দিয়ে ডিফাইন করা হয়, শুধু সমান চিহ্ন =-এর বদলে কোলন চিহ্ন : বসে।
firefly : { name : String, creator : String, episodes : Int }
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
এই টাইপ ডেফিনিশনগুলো বারবার লেখা ঝামেলার, তাই ডেফিনিশন শেয়ার করার জন্য টাইপ অ্যালিয়াস ব্যবহার করা একটি কমন রীতি।
type alias TvSeries =
{ name : String
, creator : String
, episodes : Int
}
firefly : TvSeries
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
টাইপ অ্যালিয়াস স্বয়ংক্রিয়ভাবে রেকর্ডটির জন্য একটি কনস্ট্রাক্টর ফাংশনও তৈরি করে দেয়। এটা মাঝে মাঝে অবাক করার মতো লাগে, কারণ Elm-এ ফাংশনের নাম ছোট হাতের অক্ষর দিয়ে শুরু হতে হয়, কিন্তু টাইপ কনস্ট্রাক্টর এই নিয়মের একটি ব্যতিক্রম।
firefly : TvSeries
firefly = TvSeries "Firefly" "Joss Whedon" 14
রেকর্ড ইনস্ট্যান্সের একটি ফিল্ডের মান অ্যাক্সেস করার প্রধান উপায় হলো ডট নোটেশন ব্যবহার করা, যেমন firefly.creator।
Elm কম্পাইলার . দিয়ে শুরু হওয়া বিশেষ অ্যাক্সেসর ফাংশনও সাপোর্ট করে, যেমন .creator, যা ওই নামের ফিল্ড আছে এমন যেকোনো রেকর্ডে কাজ করবে।
ফাংশনের নাম ছোট হাতের অক্ষর দিয়ে শুরু হতে হবে, এই নিয়মের দ্বিতীয় ব্যতিক্রম হলো এটাই।
আসলে ব্যবহারকারীর ডিফাইন করা ফাংশনের নাম অবশ্যই ছোট হাতের অক্ষর দিয়ে শুরু হতে হবে, কিন্তু আমরা এইমাত্র যে দুটি উদাহরণ পেলাম, সেগুলো কম্পাইলার তৈরি করে, প্রোগ্রামার নয়।
firefly : TvSeries
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
firefly.name
--> "Firefly"
.creator firefly
--> "Joss Whedon"
রেকর্ড প্যাটার্ন ম্যাচিং ব্যবহার করে বাইন্ডিংয়ের মধ্যে রেকর্ডকে _ডিস্ট্রাকচার_ও করা যায়। প্রাসঙ্গিক নামের ফিল্ড আছে এমন যেকোনো রেকর্ডের উপরেই ডিস্ট্রাকচারিং কাজ করে।
episodesCount : TvSeries -> Int
episodesCount { episodes } =
episodes
complicatedCopy : TvSeries -> TvSeries
complicatedCopy show =
let
{ name, creator } = show
in
{ name = name
, creator = creator
, episodes = episodesCount show
}
Elm-এ সবকিছুর মতো রেকর্ডও ইমিউটেবল, তাই একবার রেকর্ড ডিফাইন হয়ে গেলে তার ফিল্ডের মান আর কখনো বদলানো যায় না।
বিদ্যমান রেকর্ডের একটি কপি তৈরি করার, তবে এক বা একাধিক ফিল্ড বদলানোর একটি বিশেষ সিনট্যাক্স আছে।
এই সিনট্যাক্সে পাইপ চিহ্ন | ব্যবহার করে মূল রেকর্ড এবং যে ফিল্ডগুলো বদলাবে সেগুলোকে আলাদা করা হয়, যেমন { oldRecord | field1 = newValue }।
firefly : TvSeries
firefly =
{ name = "Firefly"
, creator = "Joss Whedon"
, episodes = 14
}
updatedFirefly : TvSeries
updatedFirefly =
{ firefly | creator = "J Whedon", episodes = 15 }
Elm [স্ট্রাকচারাল ইকুয়ালিটি][equality] ব্যবহার করে, তার মানে একই রেকর্ডের দুটি ইনস্ট্যান্সের মান হুবহু এক হলে সেগুলো সমান।
firefly1 = TvSeries "Firefly" "Joss Whedon" 14
firefly2 = TvSeries "Firefly" "Joss Whedon" 14
firefly1 == firefly2
--> True
Elm স্ট্রাকচারাল টাইপিংও সাপোর্ট করে, অর্থাৎ কোনো ফাংশনের যদি x আর y ফিল্ডসহ একটি রেকর্ড দরকার হয়, তাহলে ওই ফিল্ডগুলো আছে এমন যেকোনো রেকর্ড দিয়েই কাজ হবে, যেমন 2D পয়েন্ট, 3D পয়েন্ট, স্পেসশিপ ইত্যাদি।
ওই রেকর্ড টাইপগুলোর ডেফিনিশনে একটি পাইপ | থাকে, যেমন { a | x : Float, y : Float }, আর Elm-এর পরিভাষায় এগুলোকে "এক্সটেনসিবল রেকর্ড" বলা হয়।
টাইপ ডেফিনিশনের পাইপ | (যা একটি এক্সটেনসিবল রেকর্ড ডেফিনিশন, { a | x : Int }) আর প্রকৃত রেকর্ড ইনস্ট্যান্সের পাইপ |-এর পার্থক্য নিয়ে সাবধান থাকুন, কারণ পরেরটি বোঝায় যে আমরা ওই রেকর্ডের কিছু ফিল্ড আপডেট করছি।
point2d = { x = 1, y = 2 }
point3d = { x = 3, y = 4, z = 7 }
.x point2d --> 1
.x point3d --> 3
length : { a | x : Float, y : Float } -> Float
length vector =
sqrt (vector.x * vector.x + vector.y * vector.y)
length point2d --> 2.236068
length point3d --> 5
translateX : { a | x : Float } -> { a | x : Float }
translateX vector =
{ vector | x = vector.x + 1 }
translateX point2d --> { x = 2, y = 2 }
translateX point3d --> { x = 4, y = 4, z = 7 }
এই অনুশীলনীতে আপনি খেলাধুলার বড় ভক্ত, আর আপনি সবে NBA বাস্কেটবলের প্রেমে পড়েছেন। NBA বাস্কেটবলে নতুন হওয়ায় আপনি NBA-র ইতিহাসে গভীরভাবে ডুব দিচ্ছেন, দল, কোচ আর তাদের জয়-পরাজয়ের পরিসংখ্যান নজরে রাখছেন এবং একে অপরের সঙ্গে তুলনা করছেন।
যেহেতু আপনার এখনো কোনো প্রিয় দল নেই, তাই নির্দিষ্ট কোনো দলকে সমর্থন করা উচিত কি না, তা বের করার একটি অ্যালগরিদমও আপনি তৈরি করবেন। আপনার নিজস্ব দল-সমর্থন অ্যালগরিদম তৈরি করতে আপনাকে সাহায্য করবে সাতটি কাজ।
নিচের ক্রমে দুটি ফিল্ড আছে এমন একটি রেকর্ডের জন্য Coach টাইপ অ্যালিয়াস ডিফাইন করুন:
name: কোচের নাম, টাইপ String।formerPlayer: কোচ আগে খেলোয়াড় ছিলেন কি না তা নির্দেশ করে, টাইপ Bool।নিচের ক্রমে দুটি ফিল্ড আছে এমন একটি রেকর্ডের জন্য Stats টাইপ অ্যালিয়াস ডিফাইন করুন:
wins: জয়ের সংখ্যা, টাইপ Int।losses: পরাজয়ের সংখ্যা, টাইপ Int।নিচের ক্রমে তিনটি ফিল্ড আছে এমন একটি রেকর্ডের জন্য Team টাইপ অ্যালিয়াস ডিফাইন করুন:
name: দলের নাম, টাইপ String।coach: দলের কোচ, টাইপ Coach।stats: দলের পরিসংখ্যান, টাইপ Stats।আপনি এমন কিছু থার্ড-পার্টি কোডের সঙ্গে কাজ করতে চান যেখানে দলের ধারণা আছে, কিন্তু যেটি ভিন্ন ক্রমে প্যারামিটার দিয়ে আপনার কোড কল করে। নিচের সিগনেচারসহ একটি createTeam ফাংশন যোগ করুন: String -> Stats -> Coach -> Team।
NBA মালিকরা অধৈর্য হওয়ায় আপনি দেখলেন, দলের খারাপ ফলাফল প্রায়ই কোচ বদলে দেওয়ার দিকে নিয়ে যায়।
replaceCoach ফাংশনটি ইমপ্লিমেন্ট করুন, যেটি প্যারামিটার হিসেবে নতুন কোচ ও দল নেয় এবং নতুন কোচসহ দলটি রিটার্ন করে:
let
coach = { name = "Larry Bird", formerPlayer = True }
stats = { wins = 58, losses = 24 }
team = { name = "Indiana Pacers", coach = coach, stats = stats }
newCoach = { name = "Isiah Thomas", formerPlayer = True }
in
replaceCoach newCoach team
--> { name = "Indiana Pacers"
-- , coach = { name = "Isiah Thomas", formerPlayer = True }
-- , stats = { wins = 58, losses = 24 } }
ভবিষ্যতে কোনো দলকে সমর্থন করা উচিত কি না, তা নিয়ে একটি জটিল অ্যালগরিদম বানানোর পরিকল্পনা আপনার আছে, কিন্তু প্রথম প্রচেষ্টায় আপনি সিদ্ধান্ত নিয়েছেন যে জয়ের সংখ্যা পরাজয়ের চেয়ে বেশি, এমন যেকোনো দলকেই আপনি সমর্থন করবেন।
আপনি চান এই ফাংশনটি যেন অন্য প্রসঙ্গেও ব্যবহার করা যায়, তাই টাইপ সিগনেচারে একটি এক্সটেনসিবল রেকর্ড ব্যবহার করে এমন যেকোনো রেকর্ড গ্রহণ করুন যেটিতে প্রাসঙ্গিক ফিল্ডগুলো আছে।
ফাংশনটি যতটা সম্ভব সংক্ষিপ্ত রাখতে আপনি ফাংশনের প্যারামিটার থেকে stats নিতে প্যাটার্ন ম্যাচিং ব্যবহার করতে চান।
rootForTeam ফাংশনটি ইমপ্লিমেন্ট করুন, যেটি একটি দল নেয় এবং ওই দলটিকে সমর্থন করা উচিত হলে True রিটার্ন করে; অন্যথায় False রিটার্ন করে।
let
spursCoach = { name = "Gregg Popovich", formerPlayer = False }
spursStats = { wins = 56, losses = 26 }
spursTeam = { name = "San Antonio Spurs", coach = spursCoach, stats = spursStats }
in
rootForTeam spursTeam
--> True
Exercism-এ সাইন আপ করুন, Elm ট্র্যাকের 28টি কনসেপ্ট110টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।