এই জাতীয় প্রশ্নের উত্তর দেওয়ার চেষ্টা করার সময় আপনার সমাধান হিসাবে প্রস্তাবিত কোডটির সীমাবদ্ধতা দেওয়া উচিত। এটি যদি কেবল পারফরম্যান্স সম্পর্কে হয় তবে আমি খুব বেশি কিছু মনে করব না, তবে সমাধান হিসাবে প্রস্তাবিত বেশিরভাগ কোড (স্বীকৃত উত্তর সহ) 1000 এরও বেশি গভীরতার কোনও তালিকা সমতল করতে ব্যর্থ।
যখন আমি বেশিরভাগ কোড বলি অর্থ এমন সমস্ত কোড যা পুনরাবৃত্তির কোনও ফর্ম ব্যবহার করে (বা পুনরাবৃত্ত হওয়া কোনও স্ট্যান্ডার্ড লাইব্রেরি ফাংশনকে কল করে)। এই সমস্ত কোডগুলি ব্যর্থ হয়েছে কারণ প্রত্যেকটি পুনরাবৃত্ত কল করার জন্য, (কল) স্ট্যাকটি এক ইউনিট দ্বারা বৃদ্ধি পায় এবং (ডিফল্ট) পাইথন কল স্ট্যাকের আকার 1000 থাকে।
আপনি যদি কল স্ট্যাকের সাথে খুব বেশি পরিচিত না হন, তবে নীচেরগুলি সম্ভবত সহায়তা করবে (অন্যথায় আপনি কেবল বাস্তবায়নে স্ক্রোল করতে পারেন) )।
স্ট্যাক আকার এবং পুনরাবৃত্ত প্রোগ্রামিং কল (অন্ধকূপ সাদৃশ্য)
ধন সন্ধান এবং প্রস্থান
কল্পনা করুন যে আপনি সংখ্যাযুক্ত কক্ষগুলি সহ একটি বিশাল অন্ধকার প্রবেশ করিয়া একটি ধন সন্ধান করছেন। আপনি জায়গাটি জানেন না তবে কীভাবে ধন সন্ধান করবেন তা সম্পর্কে আপনার কাছে কিছু ইঙ্গিত রয়েছে। প্রতিটি ইঙ্গিত একটি ধাঁধা (অসুবিধা পরিবর্তিত হয়, তবে তারা কতটা কঠোর হবে তা আপনি অনুমান করতে পারবেন না)। সময় বাঁচানোর কৌশল সম্পর্কে আপনি কিছুটা চিন্তা করার সিদ্ধান্ত নিয়েছেন, আপনি দুটি পর্যবেক্ষণ করেছেন:
- ধন খুঁজে পাওয়া শক্ত (দীর্ঘ) কারণ আপনাকে সেখানে পৌঁছানোর জন্য (সম্ভাব্য শক্ত) ধাঁধাগুলি সমাধান করতে হবে।
- একবার ধনটি পাওয়া গেলে প্রবেশদ্বারে ফিরে আসা সহজ হতে পারে, আপনাকে কেবল একই দিকে অন্য দিকে ব্যবহার করতে হবে (যদিও আপনার পথটি স্মরণ করতে এটির জন্য কিছুটা স্মৃতি দরকার)।
অন্ধকূপ প্রবেশ করার সময়, আপনি এখানে একটি ছোট নোটবুক লক্ষ্য করুন। আপনি একটি ধাঁধা সমাধানের পরে আপনি যে কক্ষটি বেরোবেন তার প্রতিটি ঘর লিখতে ব্যবহার করার সিদ্ধান্ত নেন (নতুন ঘরে প্রবেশের সময়), আপনি প্রবেশদ্বারে ফিরে যেতে সক্ষম হবেন। এটি একটি প্রতিভা ধারণা, আপনি নিজের কৌশল বাস্তবায়নের জন্য এক শতাংশও ব্যয় করবেন না ।
আপনি প্রথম 1001 ধাঁধাটি দুর্দান্ত সাফল্যের সাথে সমাধান করে অন্ধকূপে প্রবেশ করুন, তবে এখানে এমন কিছু আসে যা আপনি প্ল্যান করেননি, আপনার নেওয়া নোটবুকটিতে আপনার কোনও স্থান নেই। অন্ধকূপের অভ্যন্তরে চিরতরে হারিয়ে যাওয়ার চেয়ে ধন না পাওয়া পছন্দ করার কারণে আপনি আপনার অনুসন্ধান ত্যাগ করার সিদ্ধান্ত নিয়েছেন (এটি সত্যই স্মার্ট দেখাচ্ছে)।
একটি পুনরাবৃত্তি প্রোগ্রাম কার্যকর করা হচ্ছে
মূলত, এটি ধন খুঁজে পাওয়ার মতো একই জিনিস। অন্ধকূপ হয় কম্পিউটার এর মেমরি , আপনার লক্ষ্য এখন একটি ধন খুঁজে পেতে কিন্তু হয় কিছু ফাংশন গনা (খোঁজ চ (x) এর জন্য একটি প্রদত্ত এক্স )। ইঙ্গিতগুলি কেবলমাত্র সাব-রুটিন যা আপনাকে f (x) সমাধান করতে সহায়তা করবে । আপনার কৌশলটি কল স্ট্যাক কৌশল হিসাবে একই , নোটবুকটি স্ট্যাক, ঘরগুলি ফাংশনের রিটার্ন ঠিকানাগুলি:
x = ["over here", "am", "I"]
y = sorted(x) # You're about to enter a room named `sorted`, note down the current room address here so you can return back: 0x4004f4 (that room address looks weird)
# Seems like you went back from your quest using the return address 0x4004f4
# Let's see what you've collected
print(' '.join(y))
অন্ধকূপে আপনি যে সমস্যার মুখোমুখি হয়েছিলেন তা এখানে একই রকম হবে, কল স্ট্যাকের একটি সীমাবদ্ধ আকার রয়েছে (এখানে 1000) এবং তাই, যদি আপনি ফিরে না ফিরে অনেকগুলি ফাংশন প্রবেশ করেন তবে আপনি কল স্ট্যাকটি পূরণ করবেন এবং দেখতে ত্রুটিযুক্ত হবে "প্রিয় অ্যাডভেঞ্চারার, : যেমন আমি খুব দুঃখিত তবে আপনার নোটবুকটি পূর্ণ" যা একবারে নিজেকে কল করে - বারবার -) আপনি গণনা শেষ না হওয়া অবধি উপরে প্রবেশ করবেন এবং (ধন সন্ধান না হওয়া পর্যন্ত ) ফিরে আসবেন আপনি যেখানে প্রথম স্থানে ডেকেছিলেন সেখানে ফিরে যান । কল স্ট্যাক কখনই কোনও কিছুর হাত থেকে মুক্ত হবে না যেখানে এটি একের পর এক সমস্ত রিটার্ন ঠিকানা থেকে মুক্ত হবে।RecursionError: maximum recursion depth exceeded
। মনে রাখবেন যে কল স্ট্যাকটি পূরণ করার জন্য আপনার পুনরাবৃত্তি প্রয়োজন হবে না, তবে কোনও পুনঃবিবর্তনযোগ্য প্রোগ্রাম কখনও না ফিরে 1000 ফাংশনগুলিতে কল করে। এটি বুঝতেও গুরুত্বপূর্ণ যে একবার আপনি কোনও ফাংশন থেকে ফিরে আসার পরে, কল স্ট্যাকটি ব্যবহার করা ঠিকানা থেকে মুক্ত করা হবে (সুতরাং "স্ট্যাক" নামটি নাম, কোনও ফাংশনে প্রবেশের আগে ফেরতের ঠিকানাটি পুশ করা হয় এবং ফিরে আসার সময় টানিয়ে দেওয়া হয়)। একটি সাধারণ পুনরাবৃত্তি (একটি ফাংশন) এর বিশেষ ক্ষেত্রেf
f
f
f
কীভাবে এই সমস্যা এড়ানো যায়?
এটি আসলে খুব সহজ: "আপনি যদি না জানেন যে এটি কত গভীরভাবে যেতে পারে তবে পুনরাবৃত্তি ব্যবহার করবেন না"। এটি কিছু ক্ষেত্রে যেমন সর্বদা সত্য হয় না, টেল কল পুনরাবৃত্তি অপ্টিমাইজড (টিসিও) হতে পারে । কিন্তু অজগর ক্ষেত্রে, এটি কেস নয়, এমনকি "ভাল লিখিত" পুনরাবৃত্ত ফাংশন স্ট্যাকের ব্যবহারকে অনুকূলিত করবে না । এই প্রশ্নটি সম্পর্কে গিডোর একটি আকর্ষণীয় পোস্ট রয়েছে: টেল রিকার্সন ইলিমিনেশন ।
এমন একটি প্রযুক্তি রয়েছে যা আপনি কোনও পুনরাবৃত্ত ফাংশন পুনরাবৃত্তি করতে ব্যবহার করতে পারেন, এই কৌশলটি আমরা আপনার নিজস্ব নোটবুক আনতে কল করতে পারি । উদাহরণস্বরূপ, আমাদের বিশেষ ক্ষেত্রে আমরা কেবল একটি তালিকা অন্বেষণ করছি, একটি ঘরে প্রবেশ করা একটি সাবলিস্টে প্রবেশের সমতুল্য, আপনার নিজের প্রশ্নটি জিজ্ঞাসা করা উচিত আমি কীভাবে তালিকা থেকে তার পিতামাতার তালিকায় ফিরে আসতে পারি? উত্তরটি এত জটিল নয়, stack
খালি থাকা পর্যন্ত নিম্নলিখিতটি পুনরাবৃত্তি করুন :
- বর্তমান তালিকায় ধাক্কা
address
এবং index
একটি stack
একটি নতুন sublist প্রবেশ (নোট একটি তালিকা ঠিকানা + + সূচক এছাড়াও একটি ঠিকানা যে, এর ফলে আমরা শুধু কল স্ট্যাক দ্বারা ব্যবহৃত সঠিক একই কৌশল ব্যবহার);
- প্রতিবার কোনও আইটেম পাওয়া গেলে
yield
এটি (বা এগুলিকে একটি তালিকায় যুক্ত করুন);
- একবার একটি তালিকা সম্পূর্ণরূপে অন্বেষণ করা হয়, ব্যবহার পিতা বা মাতা তালিকা ফিরে যেতে
stack
প্রত্যাবর্তন address
(এবং index
) ।
আরও মনে রাখবেন যে এটি এমন গাছের ডিএফএসের সমতুল্য যেখানে কয়েকটি নোড সাবলিস্ট A = [1, 2]
এবং কিছু সাধারণ আইটেম: 0, 1, 2, 3, 4
(জন্য L = [0, [1,2], 3, 4]
)। গাছটি দেখতে এরকম দেখাচ্ছে:
L
|
-------------------
| | | |
0 --A-- 3 4
| |
1 2
ডিএফএসের ট্র্যাভারসাল প্রাক অর্ডারটি হ'ল: এল, 0, এ, 1, 2, 3, 4 Remember মনে রাখবেন, পুনরাবৃত্ত ডিএফএস বাস্তবায়নের জন্য আপনাকে একটি স্ট্যাকেরও "প্রয়োজন" রয়েছে। নীচের রাজ্যগুলির (এবং stack
এবং এর জন্য flat_list
) ফলাফলের আগে আমি প্রয়োগের প্রস্তাব দিয়েছিলাম :
init.: stack=[(L, 0)]
**0**: stack=[(L, 0)], flat_list=[0]
**A**: stack=[(L, 1), (A, 0)], flat_list=[0]
**1**: stack=[(L, 1), (A, 0)], flat_list=[0, 1]
**2**: stack=[(L, 1), (A, 1)], flat_list=[0, 1, 2]
**3**: stack=[(L, 2)], flat_list=[0, 1, 2, 3]
**3**: stack=[(L, 3)], flat_list=[0, 1, 2, 3, 4]
return: stack=[], flat_list=[0, 1, 2, 3, 4]
এই উদাহরণে, স্ট্যাকের সর্বোচ্চ আকার 2, কারণ ইনপুট তালিকার (এবং তাই গাছের) গভীরতা 2 রয়েছে।
বাস্তবায়ন
বাস্তবায়নের জন্য, পাইথনে আপনি সাধারণ তালিকার পরিবর্তে পুনরাবৃত্তি ব্যবহার করে কিছুটা সহজ করতে পারেন। (উপ) পুনরুক্তিগুলির রেফারেন্সগুলি সাবলিস্টগুলি রিটার্ন ঠিকানাগুলি (তালিকার ঠিকানা এবং সূচক উভয়ের পরিবর্তে ) সংরক্ষণ করার জন্য ব্যবহৃত হবে । এটি কোনও বড় পার্থক্য নয় তবে আমি মনে করি এটি আরও পঠনযোগ্য (এবং কিছুটা দ্রুত):
def flatten(iterable):
return list(items_from(iterable))
def items_from(iterable):
cursor_stack = [iter(iterable)]
while cursor_stack:
sub_iterable = cursor_stack[-1]
try:
item = next(sub_iterable)
except StopIteration: # post-order
cursor_stack.pop()
continue
if is_list_like(item): # pre-order
cursor_stack.append(iter(item))
elif item is not None:
yield item # in-order
def is_list_like(item):
return isinstance(item, list)
এছাড়াও, খেয়াল করুন যে is_list_like
আমার মধ্যে isinstance(item, list)
, যা আরও ইনপুট ধরণের হ্যান্ডেল করার জন্য পরিবর্তন করা যেতে পারে, এখানে আমি কেবল সাদামাটা সংস্করণটি চেয়েছিলাম যেখানে (পুনরাবৃত্ত) কেবল একটি তালিকা। তবে আপনি এটি করতে পারেন:
def is_list_like(item):
try:
iter(item)
return not isinstance(item, str) # strings are not lists (hmm...)
except TypeError:
return False
এটি স্ট্রিংগুলিকে "সরল আইটেম" হিসাবে বিবেচনা করে এবং তাই flatten_iter([["test", "a"], "b])
ফিরে আসবে ["test", "a", "b"]
এবং হবে না ["t", "e", "s", "t", "a", "b"]
। সেক্ষেত্রে মন্তব্য করুন যে, iter(item)
প্রতিটি আইটেমটিতে দু'বার ডাকা হয়, আসুন আমরা পাঠককে এই ক্লিনারটি তৈরি করার অনুশীলন করে দেখান।
অন্যান্য প্রয়োগের উপর পরীক্ষা এবং মন্তব্য
শেষ পর্যন্ত, মনে রাখবেন যে আপনি কোনও অনন্ত নেস্টেড তালিকা L
ব্যবহার করে মুদ্রণ করতে পারবেন না print(L)
কারণ অভ্যন্তরীণভাবে এটি __repr__
( RecursionError: maximum recursion depth exceeded while getting the repr of an object
) এ পুনরাবৃত্ত কলগুলি ব্যবহার করবে । একই কারণে, flatten
জড়িত থাকার সমাধানগুলি str
একই ত্রুটি বার্তার সাথে ব্যর্থ হবে।
আপনার সমাধানটি পরীক্ষা করার দরকার হলে আপনি একটি সহজ নেস্টেড তালিকা তৈরি করতে এই ফাংশনটি ব্যবহার করতে পারেন:
def build_deep_list(depth):
"""Returns a list of the form $l_{depth} = [depth-1, l_{depth-1}]$
with $depth > 1$ and $l_0 = [0]$.
"""
sub_list = [0]
for d in range(1, depth):
sub_list = [d, sub_list]
return sub_list
যা দেয়: build_deep_list(5)
>>> [4, [3, [2, [1, [0]]]]]
।