ট্র্যাক
/
Swift
Swift
/
অনুশীলনী
/
ডাবল-নাল0111: ক্লোজার চিরকালের
ডাবল-নাল0111: ক্লোজার চিরকালের

ডাবল-নাল0111: ক্লোজার চিরকালের

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

ভূমিকা

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

ক্লোজার শুনতে অনেকটা Swift ফাংশনের মতোই লাগতে পারে; সত্যিই তাই, আর এর ভালো একটি কারণও আছে। Swift-এ ফাংশন হলো ক্লোজারেরই বিশেষ রূপ, যেগুলোর একটি নাম থাকা আবশ্যক এবং যেগুলো একটু ভিন্ন সিনট্যাক্সে ডিফাইন করা হয়।

Swift-এ ফাংশন কারিগরি দিক থেকে ক্লোজার হলেও, মানুষ যখন Swift-এ "closures" বলে, তখন তারা বোঝায় ক্লোজার এক্সপ্রেশন। ক্লোজার এক্সপ্রেশন লেখা হয় এভাবে: প্রথমে একটি প্যারামিটার তালিকা, তারপর একটি রিটার্ন টাইপ, তারপর in কিওয়ার্ড এবং সবশেষে ক্লোজারের বডি, সবটুকু একটি জোড়া দ্বিতীয় বন্ধনীর ভেতরে:

{ (a: Double, b: Double) -> Double in
  return (a + b) / 2.0
}

এটি (Double, Double) -> Double টাইপের একটি ক্লোজার এক্সপ্রেশন ডিফাইন করে, এবং এই টাইপের কোনো ফাংশনের মতোই উপযুক্ত টাইপের প্যারামিটারগুলোর উপর প্রয়োগ করে এটি চালানো যায়:

{ (a: Double, b: Double) -> Double in
  return (a + b) / 2.0
}(10.0, 15.0)
// returns 12.5

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

let mean = { (a: Double, b: Double) -> Double in
  (a + b) / 2.0
}

mean(13.0, 100.0)
// returns 56.5

উপরের উদাহরণে দেখা যাচ্ছে, সাধারণ ফাংশনের মতোই ক্লোজারও return কিওয়ার্ড বাদ দিতে পারে, যদি ক্লোজারের বডি একটি মাত্র এক্সপ্রেশন হয়।

বেশিরভাগ ক্ষেত্রেই ক্লোজার ব্যবহার করা হয় হায়ার-অর্ডার ফাংশনের সাথে, হয় প্যারামিটার হিসেবে পাস করে, নয়তো রেজাল্ট হিসেবে রিটার্ন করে:

[11, 75, 3, 99, 53].contains(where: { (x: Int) -> Bool in x > 100 })
// returns false

["apple", "ball", "carrot"].sorted(by: { (s1: String, s2: String) -> Bool in s1.count < s2.count })
// returns ["ball", "apple", "carrot"]

func makeAdder(base: Int) -> (Int) -> Int {
  { (x: Int) -> Int in base + x }
}

নির্দেশনা

আবারও স্বাগতম, Agent Double-Null0111।

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

মিনিয়নরা এতই এলোমেলো যে নিজেদের ল্যাব আর অফিসে প্রায়ই ভুলবশত দুর্গন্ধ বোমাগুলো সক্রিয় করে ফেলে। আর যেহেতু তারা দুর্গন্ধ বোমা নিষ্ক্রিয় করার নিয়ম বারবার ভুলে যায়, তাই তারা এমন একটি সিস্টেম বানিয়েছে যা বোমার ID নম্বর থেকে কোন তার কোন ক্রমে কাটতে হবে তার নির্দেশনা তৈরি করে।

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

1. দুইটি তারের ক্রম উল্টানোর জন্য একটি ক্লোজার লিখুন

প্রতিটি দুর্গন্ধ বোমায় তিনটি ভিন্ন রঙের তার থাকে। এমন একটি ক্লোজার লিখুন যা একটি (String, String, String) টিউপল নেয় এবং প্রথম দুইটি এলিমেন্টের ক্রম উল্টে দিয়ে সেই টিউপল রিটার্ন করে। এই ক্লোজারটি flip নামে অ্যাসাইন করুন।

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

flip(("red", "yellow", "blue"))
// Returns ("yellow", "red", "blue")

2. তারগুলোর ক্রম ঘোরানোর জন্য একটি ক্লোজার লিখুন

এমন একটি ক্লোজার লিখুন যা একটি (String, String, String) টিউপল নেয় এবং তারগুলোর ক্রম বাম দিকে ঘুরিয়ে সেই টিউপল রিটার্ন করে। এই ক্লোজারটি rotate নামে অ্যাসাইন করুন।

মনে রাখা জরুরি, টাইপটি অবশ্যই ChangeClosure হতে হবে (অথবা একটি সেন্ডেবল টাইপ), টাইপ ডেফিনিশনটি স্টাব ফাইলে দেওয়া আছে।

rotate(("red", "yellow", "blue"))
// Returns ("yellow", "blue", "red")

3. তার শাফল জেনারেটর ইমপ্লিমেন্ট করুন

দুর্গন্ধ বোমার তারগুলো কোন ক্রমে কাটতে হবে তা বের করার জন্য, আপনাকে এমন একটি ফাংশন ইমপ্লিমেন্ট করতে হবে যা বোমার ID নম্বরের ভিত্তিতে সঠিক শাফলিং ফাংশন তৈরি করে।

ফাংশনটি ইমপ্লিমেন্ট করুন:

makeShuffle(
  flipper: @escaping ((String, String, String)) -> (String, String, String),
  rotator: @escaping ((String, String, String)) -> (String, String, String)
) -> ([UInt8], (String, String, String)) -> (String, String, String)

যা ইনপুট হিসেবে দুইটি তার উল্টানোর একটি ক্লোজার আর তিনটি তার ঘোরানোর একটি ক্লোজার নেয় এবং একটি ক্লোজার রিটার্ন করে। এই রিটার্ন করা ক্লোজারটি দুর্গন্ধ বোমার ID নম্বর আর তিনটি তারের ক্রম নেয়, তারপর তারগুলো কোন ক্রমে কাটতে হবে তা হিসাব করে। ID নম্বরটি আটটি বিট দিয়ে তৈরি, আর প্রতিটি বিট ঠিক করে তারগুলো উল্টাতে হবে নাকি ঘোরাতে হবে।

ID নম্বরের প্রতিটি বিটের জন্য, একেবারে ডানদিকের বিট থেকে শুরু করে, বিটটি 0 হলে তারের টিউপলে flipper ক্লোজারটি প্রয়োগ করবেন আর বিটটি 1 হলে rotator ক্লোজারটি প্রয়োগ করবেন, যাতে তারগুলোর নতুন অবস্থা পাওয়া যায়। ID-র আটটি বিটের জন্য যথাযথ ক্লোজারগুলো প্রয়োগ করার পর, তারগুলোর চূড়ান্ত অবস্থাই হলো সেগুলো যে ক্রমে কাটতে হবে সেই ক্রম।

let shuffler = makeShuffle(flipper: flip, rotator: rotate)
// Returns ([UInt8], (String, String, String)) -> (String, String, String)
shuffler([1, 0, 0, 1, 1, 0, 1, 1], ("red", "yellow", "blue"))
// Returns ("red", "blue", "yellow")
GitHub-এর মাধ্যমে সম্পাদনা করুন লিংকটি একটি নতুন উইন্ডো বা ট্যাবে খোলে
Swift Exercism

ডাবল-নাল0111: ক্লোজার চিরকালের শুরু করতে প্রস্তুত?

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