Do you want to pick up from where you left of?
Take me there

ইনাম

কালেকশন এ ইনামুরেট এর সুবিধা নিবার জন্য কিছু এলগোরিদম ।

ইনাম Enum

ইনাম Enum মডিউলে প্রায় ৭০ টার উপরে ফাংশন আছে যা দিয়ে কালেকশন এর সাথে কাজ করা যায়। টাপল বাদে আগের অধ্যায়ে আমরা যতগুলি কালেকশন নিয়ে কথা বলেছি, তার সবগুলিই ইনিউমেরবল।

এই চ্যাপ্টারে আমারা শুধু অল্প কিছু ফাংশন নিয়ে আলোচনা করবো। এবং বাকি ফাংশন গুলো আমরা নিজেরা পরে দেখে নিতে পারবো।

তাহলে, আসুন আমরা IEx এ ছোট একটা পরীক্ষা শুরু করি ।

iex
iex> Enum.__info__(:functions) |> Enum.each(fn({function, arity}) ->
...>   IO.puts "#{function}/#{arity}"
...> end)
all?/1
all?/2
any?/1
any?/2
at/2
at/3
...

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

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

বহুল ব্যবহৃত কিছু ফাংশন

ইনাম এর ফাংশন গুলোর পুরো লিস্ট দেখার জন্য , আপনি যেতে পারেন ,Enum ডকুমেন্ট এ, আর lazy enumeration ব্যাবহার করে, Stream মডিউল।

all?

যখন আমরা all?/2 ব্যাবহার করি Enum এর সাথেে। আসলে এই সময় আমরা কালেকশন এর আইটেম গুলির সাথে একটা ফাংশন এপ্লাই করি । এক্ষেত্রে মূলোতো all?/2 এর জন্য কালেকশন ফলাফল দেয় , true বা false এ ।

iex> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 3 end)
false
iex> Enum.all?(["foo", "bar", "hello"], fn(s) -> String.length(s) > 1 end)
true

any?

আবার, উপরের মতন এই any?/2 ফলাফল true রিটার্ন করে যদি শুধুমাত্র একটি ও আইটেম এর ফলাফল true হয় ।

iex> Enum.any?(["foo", "bar", "hello"], fn(s) -> String.length(s) == 5 end)
true

chunk_every

যদি কালেকশনকে ভেঙ্গে ছোট গ্রুপ তৈরি করতে হয় তাহলে, chunk_every/2 ফাংশনটি ই আপনার দরকার।

iex> Enum.chunk_every([1, 2, 3, 4, 5, 6], 2)
[[1, 2], [3, 4], [5, 6]]

chunk_every এর আরও কিছু অপশন আছে, কিন্তু আমরা সেগুলোতে আমরা এখন নাই যাই, আপনারা সেগুলো দেখে নিতে পারবেন এখানে,chunk_every/4 এই অফিসিয়াল ডকুমেন্ট এ ।

chunk_by

যদি আমরা কালেকশনকে সাইজ ছাড়া অন্য কিছুর উপর ভিত্তি করে ছোট গ্রুপ এ ভাগ করতে চাই তাহলে আমরা chunk_by/2 মেথড টি ব্যাবহার করতে পারি। এটি একটি ফাংশন এবং একটি ইনিউমেরেবল ইনপুট হিসাবে নিয়ে যখন ফাংশনটির রিটার্ন বদলায় তখন একটু নতুন গ্রুপ তৈরী করে। নিচের উদাহরনটিতে একই দৈর্ঘ্যের স্ট্রিং গুলি একটি গ্রুপে আছে, কিন্তু যখনই স্ট্রিং এর দৈর্ঘ্য বদলিয়েছে, নতুন গ্রুপ তৈরী হয়েছে।

iex> Enum.chunk_by(["one", "two", "three", "four", "five"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"]]
iex> Enum.chunk_by(["one", "two", "three", "four", "five", "six"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"], ["six"]]

map_every

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

# প্রতি তৃতীয় আইটেম এর উপর ফাংশন চালানো হয়েছে
iex> Enum.map_every([1, 2, 3, 4, 5, 6, 7, 8], 3, fn x -> x + 1000 end)
[1001, 2, 3, 1004, 5, 6, 1007, 8]

each

কখনো কোন কালেকশন এর উপর ইটারেট কবার দরকার হলে আমরা each ব্যাবহার করতে পারি এবং এতে নতুন ভ্যালু তৈরি হয় না।

iex> Enum.each(["one", "two", "three"], fn(s) -> IO.puts(s) end)
one
two
three
:ok

দ্রষ্টব্য: each মেথড একটি এটম রিটার্ন করে :ok.

map

যদি আমরা কালেকশন এর প্রতিটি আইটেম এর উপর ফাংশন প্রয়োগ করতে চাই তাহলে map মেথড ব্যাবহার করতে পারি।

iex> Enum.map([0, 1, 2, 3], fn(x) -> x - 1 end)
[-1, 0, 1, 2]

min

min/1 কালেকশন থেকে খুঁজে বের করে সবচেয়ে min ভ্যালুটি :

iex> Enum.min([5, 3, 0, -1])
-1

min/2 ও একই কাজ করে, কিন্তু এটি আমাদের একটি সুযোগ দেয় Enum এ একটি ডিফল্ট ভ্যালু দিবার একটি এনয়নিমাস ফাংশন এর ভিতর ।

iex> Enum.min([], fn -> :foo end)
:foo

max

max/1 একটা কালেকশন থেকে max ভ্যালু রিটার্ন করে।

iex> Enum.max([5, 3, 0, -1])
5

আবার max/2 ও একই রকম কাজ করে কিন্তু এটি min/2 এর মতন আচরণ করে। এই ফাংশনে আমরা একটা এনয়নিমাস ফাংশন পাঠাতে পারি এবং এতে একটা ডিফল্ট ভ্যালু পাঠাতে পারি।

iex> Enum.max([], fn -> :bar end)
:bar

filter

filter/2 একটি ইনিউমেরবল এর পাশাপাশি একটি ফাংশন ইনপুট নেয় এবং আউটপুট হিসাবে শুধুমাত্র সেসকল ভ্যালু এর ইনিউমেরবল রিটার্ন করে যেগুলির জন্য ফাংশনের আউটপুট true হয়।

iex> Enum.filter([1, 2, 3, 4], fn(x) -> rem(x, 2) == 0 end)
[2, 4]

reduce

reduce ফাংশন ব্যাবহার করে আমরা কালেকশনকে শুধু মাত্র একটি সিঙ্গেল ভ্যালু বানাতে পারি। এটি করবার জন্য, আমারা একটি অপশনাল একুমেলটর ( 10 এই উদাহরণের জন্য ) পাঠাতে হয়। আর যদি কোন একুমেলটর পাঠানো না হয় তাহলে প্রথম ভ্যালু কে নিয়ে কাজ করা হয়।

iex> Enum.reduce([1, 2, 3], 10, fn(x, acc) -> x + acc end)
16
iex> Enum.reduce([1, 2, 3], fn(x, acc) -> x + acc end)
6
iex> Enum.reduce(["a","b","c"], "1", fn(x,acc)-> x <> acc end)
"cba1"

sort

কালেকশন সাজানো আমাদের জন্য খুবই সহজ হয়ে গেছে। একটি না বরং দুটি ফাংশন রয়েছে কালেকশন সাজানো এর জন্য।

sort/1 আরল্যাঙ্গ এর টার্ম অর্ডারিং ব্যবহার করে সঠিক ক্রম জানার জন্য।

iex> Enum.sort([5, 6, 1, 3, -1, 4])
[-1, 1, 3, 4, 5, 6]

iex> Enum.sort([:foo, "bar", Enum, -1, 4])
[-1, 4, Enum, :foo, "bar"]

sort/2 তে আমরা ক্রম ঠিক করার জন্য নিজেদের ফাংশন দিতে পারি:

# with our function
iex> Enum.sort([%{:val => 4}, %{:val => 1}], fn(x, y) -> x[:val] > y[:val] end)
[%{val: 4}, %{val: 1}]

# without
iex> Enum.sort([%{:count => 4}, %{:count => 1}])
[%{count: 1}, %{count: 4}]

কাজের সুবিধার্থে, sort/2 আমাদেরকে :asc (উর্ধ্যক্রম) অথবা :desc (অধক্রম) দিতে দেয় সাজানোর ফাংশন হিসাবে:

Enum.sort([2, 3, 1], :desc)
[3, 2, 1]

uniq

ইনিউমেরবল থেকে একাধিক বার থাকা একই ভ্যালু বাদ দেবার জন্য uniq/1 ফাংশন রয়েছে:

iex> Enum.uniq([1, 2, 3, 2, 1, 1, 1, 1, 1])
[1, 2, 3]

uniq_by

আমরা uniq_by/2 ব্যাবহার করে কালেকশন থেকে ডুপ্লিকেশন দুর করতে পারি।

iex> Enum.uniq_by([1, 2, 3, 2, 1, 1, 1, 1, 1], fn x -> x end)
[1, 2, 3]

ক্যাপচার অপারেটর (&) ব্যাবহার করে ইনাম

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

এই অ্যানোনিমাস ফাংশন গুলি অনেকসময় সংক্ষেপে ক্যাপচার অপারেটর (&) ব্যবহার করে লিখা হয়।

কিছু উদাহরন থাকল কিভাবে ক্যাপচার অপারেটর সহ ইনাম মডিউল ব্যবহার করতে হয়। সবধরনই একইরকম ভাবে কাজ করে।

অ্যানোনিমাস ফাংশনের সাথে ক্যাপচার অপারেটর

Enum.map/2 এর সাধারণ ফাংশন ব্যবহার করে:

iex> Enum.map([1,2,3], fn number -> number + 3 end)
[4, 5, 6]

এখন আমরা দেখব কিভাবে একই কাজ ক্যাপচার অপারেটর ব্যবহার করে করতে হয়। ক্যাপচার অপারেটর মূলত নাম্বার লিস্ট ([1,2,3]) এর ভ্যালুগুলি নেয় এবং প্রতিটি ভ্যালু এক এক করে &1 ভ্যারিয়েবলে অ্যাসাইন করে। &1 এর ভ্যালুটি ম্যাপ ফাংশনের মধ্যে পাঠানো হয়।

iex> Enum.map([1,2,3], &(&1 + 3))
[4, 5, 6]

অ্যানোনিমাস ফাংশনটি একটি ভ্যারিয়েবল এ নিয়ে এটিকে আরও রিফ্যাক্টর করা যায়। এক্ষেত্রেও ক্যাপচার অপারেটর ব্যবহার করা যায়।

iex> plus_three = &(&1 + 3)
iex> Enum.map([1,2,3], plus_three)
[4, 5, 6]

সাধারণ ফাংশনে ক্যাপচার অপারেটর এর ব্যবহার

প্রথমত আমরা একটি ফাংশন বানাবো এবং সেটি Enum.map/2 এর সাথে ব্যবহার করব।

defmodule Adding do
  def plus_three(number), do: number + 3
end

iex>  Enum.map([1,2,3], fn number -> Adding.plus_three(number) end)
[4, 5, 6]

এখন আমরা উপরের কোড রিফ্যাক্টর করব ক্যাপচার অপারেটর ব্যবহার করে।

iex> Enum.map([1,2,3], &Adding.plus_three(&1))
[4, 5, 6]

আরও সংক্ষিপ্ত কোডের জন্য আমরা ক্যাপচার ভ্যারিয়েবল ছাড়াই ফাংশনটি কল করতে পারি।

iex> Enum.map([1,2,3], &Adding.plus_three/1)
[4, 5, 6]
Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!