যে কেউ Python ব্যবহার করেছেন, তিনি প্রায় নিশ্চিতভাবেই লিস্ট কম্প্রিহেনশন ব্যবহার করেছেন, যা শুরুর দিকের ভার্সন থেকেই Python-এর সিনট্যাক্সের অন্যতম কেন্দ্রীয় অংশ।
এমন সুবিধাজনক জিনিস ধীরে ধীরে অন্য প্রোগ্রামিং ভাষাগুলোতেও জায়গা করে নেয়, Julia-ও তার ব্যতিক্রম নয়।
Julia প্রোগ্রামারদের কাছে কম্প্রিহেনশন বাধ্যতামূলক কিছু নয়, বরং একটি বিকল্প, কারণ সাধারণত এর বিকল্প উপায় থাকে (ব্রডকাস্টিং, হায়ার-অর্ডার ফাংশন ইত্যাদি)।
তবে একটি অ্যারে তৈরি করার সরল, পঠনযোগ্য এবং ভালো পারফরম্যান্সের একটি উপায় প্রায়ই কম্প্রিহেনশন দিয়েই পাওয়া যায়। শেষ বিচারে এটি ব্যবহার করা না করা ব্যক্তিগত পছন্দের ব্যাপার, আর তা নির্ভর করে Python আর ফাংশনাল ভাষাগুলো সম্পর্কে আপনার মনোভাবের উপর।
এর সিনট্যাক্স বেশিরভাগটাই Python-এর সরাসরি অনুকরণ, তবে উচ্চ-মাত্রিক অ্যারের জন্য কিছু এক্সটেনশন যোগ করা হয়েছে।
খুবই Python-এর মতো, ঐচ্ছিক if ক্লজ-সহ।
julia> [x^2 for x in 1:3]
3-element Vector{Int64}:
1
4
9
julia> [x^2 for x in 1:3 if isodd(x^2)]
2-element Vector{Int64}:
1
9
আউটপুটের আকার নির্ভর করে কম্প্রিহেনশনের খুঁটিনাটির উপর: সাধারণ ক্ষেত্রে সাধারণত ইনপুট কালেকশনের মতোই থাকে, তবে if ক্লজ থাকলে তা একটি Vector-এ সমতল হয়ে যায়।
julia> m
2×3 Matrix{Int64}:
1 2 3
4 5 6
julia> [x^2 for x in m]
2×3 Matrix{Int64}:
1 4 9
16 25 36
julia> [x^2 for x in m if isodd(x^2)]
3-element Vector{Int64}:
1
25
9
Python-এর মতোই আমরা একাধিক for ক্লজ রাখতে পারি, যেখানে ভ্যারিয়েবলগুলো আলাদা আর ইনপুট কালেকশন একই বা আলাদা হতে পারে।
julia> [x * y for x in 1:3 for y in 4:6]
9-element Vector{Int64}:
4
5
6
8
10
12
12
15
18
julia> [x * y for x in 1:3 for y in 4:6 if isodd(x * y)]
2-element Vector{Int64}:
5
15
এটি নেস্টেড লুপের সমতুল্য। ইনপুট ম্যাট্রিক্স হলেও আউটপুট এক-মাত্রিক হয়।
julia> m
2×3 Matrix{Int64}:
1 2 3
4 5 6
julia> [x*y for x in m for y in m]
36-element Vector{Int64}:
1
4
2
5
(truncated output...)
আগের অংশে একাধিক for ক্লজের কথা বলা হয়েছে, যেগুলো শুধু স্পেস দিয়ে আলাদা করা হয়।
এই অংশে কিন্তু একটি মাত্র for থাকে, আর ভ্যারিয়েবলগুলোকে কমা দিয়ে আলাদা করা হয়।
julia> [(x, y) for x in 1:3, y in 4:6]
3×3 Matrix{Tuple{Int64, Int64}}:
(1, 4) (1, 5) (1, 6)
(2, 4) (2, 5) (2, 6)
(3, 4) (3, 5) (3, 6)
কম্প্রিহেনশনের প্রতিটি ভ্যারিয়েবল আউটপুটে একটি করে নতুন মাত্রা তৈরি করে, যেখানে ভ্যারিয়েবলগুলোর প্রতিটি সম্ভাব্য সমন্বয়ের জন্য একটি করে এন্ট্রি থাকে।
উপরের উদাহরণে x সারি বরাবর নিচের দিকে বাড়ে, আর y কলাম বরাবর ডান দিকে বাড়ে। আরও উঁচু মাত্রাও সম্ভব, তবে আউটপুটের পঠনযোগ্যতা নিয়ে চেনা সতর্কতার কথা মাথায় রাখতে হবে।
আগের অংশগুলোতে মূলত অ্যারে আউটপুট নিয়েই আলোচনা হয়েছে, যেখানে কম্প্রিহেনশনটি তৃতীয় বন্ধনী [ ... ]-এর ভেতরে বসানো হয়।
যখন ওই বন্ধনীগুলোর জায়গায় প্রথম বন্ধনী ( ... ) বসানো হয়, তখন অন্য কিছু ঘটে: আমরা পাই একটি generator expression, অর্থাৎ একটি লেজি-ইভালুয়েটেড ইটারেটর, যা চাহিদা অনুযায়ী পরবর্তী মান দিতে পারে।
julia> g = ((x, y) for x in 1:3, y in 4:6)
Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}, var"#9#10"}(var"#9#10"(), Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:3, 4:6)))
# Indexing fails with a generator, the entries don't exist yet
julia> g[1, 2]
ERROR: MethodError: no method matching getindex(::Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{…}, UnitRange{…}}}, var"#9#10"}, ::Int64, ::Int64)
# conversion to array
julia> collect(g)
3×3 Matrix{Tuple{Int64, Int64}}:
(1, 4) (1, 5) (1, 6)
(2, 4) (2, 5) (2, 6)
(3, 4) (3, 5) (3, 6)
# generators are mainly designed for iteration
julia> [i * j for (i, j) in g]
3×3 Matrix{Int64}:
4 5 6
8 10 12
12 15 18
কম্প্রিহেনশনের ফলাফল যদি সঙ্গে সঙ্গেই পরবর্তী কোনো কাজে ব্যবহার করা হয়, তাহলে একটি জেনারেটর মেমরি-সাশ্রয়ী হতে পারে, কারণ বড় কোনো মধ্যবর্তী অ্যারে জমা রাখার দরকার পড়ে না।
ফাংশনের আর্গুমেন্ট হিসেবে ব্যবহৃত জেনারেটরের জন্য অতিরিক্ত বন্ধনীর দরকার হয় না।
# inefficient
julia> v = [x^2 for x in 1:1e6];
julia> sum(v)
3.333338333335e17
# better - use a generator
julia> sum(x^2 for x in 1:1e6)
3.3333383333312755e17
জেনারেটরের সিনট্যাক্স কম্প্রিহেনশনের মতোই, শুধু চারপাশের বন্ধনীটুকু আলাদা।
ডিকশনারি কনস্ট্রাক্টরেও জেনারেটর সুবিধাজনক হতে পারে।
julia> Dict(x => x^2 for x in 1:5)
Dict{Int64, Int64} with 5 entries:
5 => 25
4 => 16
2 => 4
3 => 9
1 => 1
আপনি একটি অনলাইন ফ্যাশন বুটিক স্টোরে কাজ করেন। ব্যবহারকারীদের কাছে পোশাকের সাজ সাজেস্ট করার একটি ওয়েবসাইট ফিচারের আইডিয়া আপনার মাথায় আসে। এখানে গুরুত্বপূর্ণ হলো সম্ভাব্য সাজগুলো, তাদের দাম, আর পরস্পরের সাথে না মেলা অপশনগুলো বাদ দেওয়া।
নিচের সমস্যাগুলো সমাধানের বিভিন্ন উপায় থাকতে পারে, তবে সবগুলোই একটি কম্প্রিহেনশন দিয়ে সমাধান করা যায়।
clothingitem() ইমপ্লিমেন্ট করুন, যা দুটি Tuple নেয়: একটি categories-এর, অন্যটি তাদের নিজ নিজ qualities-এর।
ফাংশনটি একটি Dict রিটার্ন করবে, যেখানে categories থাকবে কী (key) হিসেবে আর qualities থাকবে ভ্যালু হিসেবে।
julia> categories = ("item_name", "price", "color", "base_color");
julia> qualities = ("Descriptive Name", 99.00, "Ochre Red", "red");
julia> clothingitem(categories, qualities)
Dict{String, Any} with 4 entries:
"price" => 99.0
"item_name" => "Descriptive Name"
"base_color" => "red"
"color" => "Ochre Red"
আপনার ফাংশনটি যেকোনো সংখ্যক categories সামলাতে পারা উচিত।
get_combinations() ইমপ্লিমেন্ট করুন, যা টপসের একটি Tuple আর বটমসের একটি Tuple নেয়।
ফাংশনটি টিউপলগুলোর cartesian product একটি দ্বিমাত্রিক অ্যারে (অর্থাৎ Matrix) হিসেবে রিটার্ন করবে।
প্রতিটি এন্ট্রি একটি (top, bottom) কম্বিনেশন, যা একটি Tuple।
টপস সারি বরাবর নিচের দিকে বদলায়, বটমস কলাম বরাবর বদলায়।
julia> tops = (
Dict("item_name" => "Dress shirt"),
Dict("item_name" => "Casual shirt")
);
julia> bottoms = (
Dict("item_name" => "Jeans"),
Dict("item_name" => "Dress trousers")
);
julia> get_combinations(tops, bottoms)
2×2 Matrix{Tuple{Dict{String, String}, Dict{String, String}}}:
(Dict("item_name"=>"Dress shirt"), Dict("item_name"=>"Jeans")) (Dict("item_name"=>"Dress shirt"), Dict("item_name"=>"Dress trousers"))
(Dict("item_name"=>"Casual shirt"), Dict("item_name"=>"Jeans")) (Dict("item_name"=>"Casual shirt"), Dict("item_name"=>"Dress trousers"))
পোশাকের প্রতিটি অংশের সাথে একটি price ফিল্ড যুক্ত থাকে, তাই সাজের মোট দাম তুলনা করতে পারলে সুবিধা হয়।
get_prices() ইমপ্লিমেন্ট করুন, যা পোশাকের কম্বিনেশনগুলোর একটি অ্যারে নেয়।
ফাংশনটি একই আকারের একটি অ্যারে রিটার্ন করবে, যেখানে নিজ নিজ অবস্থানে থাকবে সাজগুলোর দাম।
julia> tops = (
Dict("item_name" => "Dress shirt", "base_color" => "blue", "price" => 35),
Dict("item_name" => "Casual shirt", "base_color" => "black", "price" => 20)
);
julia> bottoms = (
Dict("item_name" => "Jeans", "base_color" => "blue", "price" => 30),
Dict("item_name" => "Dress trousers", "base_color" => "black", "price" => 75)
);
julia> combomatrix = get_combinations(tops, bottoms);
julia> get_prices(combomatrix)
2×2 Matrix{Int}::
65 110
50 95
julia> filteredmatrix = filter_clashing(combomatrix)
1-element Vector{Tuple{Dict{String, Any}, Dict{String, Any}}}:
(Dict("price" => 20, "item_name" => "Casual shirt", "base_color" => "black"), Dict("price" => 30, "item_name" => "Jeans", "base_color" => "blue"))
julia> get_prices(filteredmatrix)
1-element Vector{Int}:
50
পোশাকের প্রতিটি অংশে একটি base_color ফিল্ড থাকে।
এই ফিল্ড ব্যবহার করে এমন সব কম্বিনেশন বাদ দিন যেখানে টপ আর বটমের বেস কালার একই।
filter_clashing() ইমপ্লিমেন্ট করুন, যা পোশাকের কম্বিনেশনগুলোর একটি অ্যারে নেয়।
শর্ত মেলা কম্বিনেশনগুলো একটি একমাত্রিক অ্যারে (অর্থাৎ Vector) হিসেবে রিটার্ন করুন।
julia> tops = (
Dict("item_name" => "Dress shirt", "base_color" => "blue", "price" => 35),
Dict("item_name" => "Casual shirt", "base_color" => "black", "price" => 20)
);
julia> bottoms = (
Dict("item_name" => "Jeans", "base_color" => "blue", "price" => 30),
Dict("item_name" => "Dress trousers", "base_color" => "black", "price" => 75)
);
julia> combos = get_combinations(tops, bottoms);
julia> filter_clashing(combos)
1-element Vector{Tuple{Dict{String, Any}, Dict{String, Any}}}:
(Dict("price" => 20, "item_name" => "Casual shirt", "base_color" => "black"), Dict("price" => 30, "item_name" => "Jeans", "base_color" => "blue"))
Exercism-এ সাইন আপ করুন, Julia ট্র্যাকের 35টি কনসেপ্ট128টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।