কেন স্ট্যান্ডার্ডটি end()
প্রকৃত প্রান্তের পরিবর্তে শেষের মতো হিসাবে সংজ্ঞায়িত হয়?
কেন স্ট্যান্ডার্ডটি end()
প্রকৃত প্রান্তের পরিবর্তে শেষের মতো হিসাবে সংজ্ঞায়িত হয়?
উত্তর:
সহজেই সর্বোত্তম যুক্তি হ'ল ডিজকস্ট্রা নিজে তৈরি করেছিলেন :
আপনি সীমাটির আকারটি একটি সাধারণ পার্থক্য সমাপ্ত হতে চান - শুরু করুন ;
নিম্ন সীমা সহ আরও "প্রাকৃতিক" যখন সিক্যুয়েন্সগুলি শূন্যগুলিতে অবনমিত হয়, এবং বিকল্পটি ( নিম্ন সীমাটি বাদ দিয়ে) একটি "এক-পূর্ব-শুরু" সেন্ডিনেল মানটির অস্তিত্ব প্রয়োজন।
আপনি এখনও একের চেয়ে শূন্যে কেন গণনা শুরু করলেন তা আপনাকে ন্যায়সঙ্গত করতে হবে, তবে এটি আপনার প্রশ্নের অংশ ছিল না।
[শুরু, শেষ) কনভেনশনের পিছনে বুদ্ধি সময় এবং আবার সময় দেয় যখন আপনার কোনও ধরণের অ্যালগরিদম থাকে যা একাধিক নেস্টেড বা পুনরাবৃত্তিযুক্ত কলগুলির সাথে পরিসীমা ভিত্তিক নির্মাণগুলিতে ডিল করে, যা প্রাকৃতিকভাবে শৃঙ্খলাবদ্ধ। বিপরীতে, দ্বিগুণ-বদ্ধ পরিসীমা ব্যবহার করে অফ-বাই-ওড এবং চূড়ান্ত অপ্রীতিকর এবং শোরগোলের কোড আসতে পারে। উদাহরণস্বরূপ, একটি পার্টিশন বিবেচনা করুন [ n 0 , n 1 ) [ n 1 , n 2 ) [ n 2 , n 3 )। আর একটি উদাহরণ স্ট্যান্ডার্ড পুনরাবৃত্তি লুপ for (it = begin; it != end; ++it)
, যা end - begin
বার চালায় । উভয় প্রান্তটি অন্তর্ভুক্ত থাকলে সংশ্লিষ্ট কোডটি খুব কম পঠনযোগ্য হবে - এবং কীভাবে খালি রেঞ্জগুলি পরিচালনা করবেন তা কল্পনা করুন।
পরিশেষে, আমরা একটি দুর্দান্ত যুক্তিও করতে পারি কেন গণনা শূন্য থেকে শুরু হওয়া উচিত: আমরা সবেমাত্র প্রতিষ্ঠিত রেঞ্জগুলির অর্ধ-খোলা কনভেনশন সহ, যদি আপনাকে এন এন উপাদানগুলির একটি পরিসর দেওয়া হয় (কোন অ্যারের সদস্যদের গণনা করতে বলুন), তবে 0 হ'ল প্রাকৃতিক "সূচনা" যাতে আপনি কোনও বিশ্রী অফসেট বা সংশোধন না করে [0, N ) হিসাবে পরিসরটি লিখতে পারেন ।
সংক্ষেপে: সত্য যে আমরা 1
পরিসর ভিত্তিক অ্যালগরিদমগুলিতে কোথাও সংখ্যাটি দেখতে পাই না , এটি [শুরু, শেষ) সম্মেলনের প্রত্যক্ষ ফলাফল এবং প্রেরণা।
begin
এবং end
যেমন int
মান গুলি 0
এবং N
যথাক্রমে, এটা পুরোপুরি ফিট। যুক্তিযুক্তভাবে, এটি এমন !=
শর্ত যা প্রচলিত than ষধের চেয়ে প্রাকৃতিক <
, তবে আমরা আরও সাধারণ সংগ্রহ সম্পর্কে চিন্তা না করা অবধি এটি কখনই আবিষ্কার করতে পারি নি।
++
এমনটি হতে চান তবে আপনার উচিত একটি বিস্ময়কর পুনরুদ্ধার টেম্পলেট step_by<3>
, যা এরপরে মূলত বিজ্ঞাপনীকৃত শব্দার্থবিজ্ঞান লেখা উচিত।
!=
তার ব্যবহার করা হয় <
, তবে এটি একটি বাগ। যাইহোক, ত্রুটির সেই রাজা ইউনিট পরীক্ষা বা দৃ or়তার সাথে সন্ধান করা সহজ।
আসলে, পুনরুক্তিকারীর সংশ্লিষ্ট কাপড় অনেকটা হঠাৎ যদি আপনি iterators প্রতি নির্দেশ না বিবেচনা অনেক বেশি জ্ঞান করে তোলে এ ক্রম উপাদান কিন্তু মাঝে এটি পরবর্তী উপাদান অধিকার অ্যাক্সেস dereferencing সঙ্গে। তারপরে "এক অতীত প্রান্ত" পুনরুক্তিকারী হঠাৎ করে তাত্ক্ষণিকভাবে তাৎপর্য দেয়:
+---+---+---+---+
| A | B | C | D |
+---+---+---+---+
^ ^
| |
begin end
স্পষ্টতই begin
সিকোয়েন্সের শুরুতে নির্দেশ করে এবং end
একই ক্রমের শেষ দিকে নির্দেশ করে। ডিফেরেন্সিং begin
উপাদানটি অ্যাক্সেস করে A
এবং ডেরেফারেন্সিং এর end
কোনও অর্থ হয় না কারণ এর সাথে কোনও উপাদান নেই right এছাড়াও, i
মাঝখানে একটি পুনরাবৃত্তি যুক্ত করে দেয়
+---+---+---+---+
| A | B | C | D |
+---+---+---+---+
^ ^ ^
| | |
begin i end
এবং আপনি অবিলম্বে দেখতে যে থেকে উপাদানগুলি পরিসীমা begin
থেকে i
উপাদানগুলি রয়েছে A
এবং B
থেকে উপাদানগুলি পরিসীমা যখন i
থেকে end
উপাদানগুলি রয়েছে C
এবং D
। ডেরেফারেন্সিং i
উপাদানটিকে ডান দেয়, এটি দ্বিতীয় অনুক্রমের প্রথম উপাদান।
এমনকি বিপরীত পুনরাবৃত্তকারীদের জন্য "অফ-বাই-ওয়ান" হঠাৎ করে সেইভাবে সুস্পষ্ট হয়ে ওঠে: সেই ক্রমটিকে বিপরীত করে দেয়:
+---+---+---+---+
| D | C | B | A |
+---+---+---+---+
^ ^ ^
| | |
rbegin ri rend
(end) (i) (begin)
আমি নীচে বন্ধনীতে সংশ্লিষ্ট অ-বিপরীত (বেস) পুনরাবৃত্তিগুলি লিখেছি। আপনি দেখুন, বিপরীত পুনরুক্তিযুক্ত i
(যা আমি নাম দিয়েছি ri
) এখনও উপাদান B
এবং এর মধ্যে পয়েন্ট করে C
। তবে ক্রমটি উল্টে যাওয়ার কারণে এখন উপাদানটি B
এর ডানদিকে রয়েছে।
foo[i]
) অবধি অবস্থানের পরে আইটেমটির জন্য একটি সংক্ষিপ্ত বিবরণ হয় তবে এটি আরও ভাল চিত্রিত হতে পারে i
। এটি সম্পর্কে ভাবতে ভাবতে আমি অবাক হয়েছি যে "ভাষার পরে আইটেমের পরে" এবং "আইটেমের ঠিক আগে" আইটেমের জন্য আলাদা অপারেটর ব্যবহার করা কোনও ভাষার পক্ষে কার্যকর হতে পারে, যেহেতু প্রচুর অ্যালগরিদম সংলগ্ন আইটেমের জোড়া নিয়ে কাজ করে এবং বলে " আই "এর উভয় পাশের আইটেমগুলি" আইটেমের আই এবং আই +1 "এর চেয়ে পরিষ্কার হতে পারে।
begin[0]
(একটি এলোমেলো অ্যাক্সেস পুনরুদ্ধারকারী ধরে নেওয়া) উপাদানটি অ্যাক্সেস করতে পারে 1
, যেহেতু 0
আমার উদাহরণ ক্রমের কোনও উপাদান নেই ।
start()
নির্দিষ্ট শ্রেণীর কোনও প্রক্রিয়া শুরু করার জন্য আপনাকে যখন আপনার ক্লাসে কোনও ক্রিয়াকলাপটি নির্ধারণ করতে হয় বা যাই হোক না কেন, এটি যদি ইতিমধ্যে বিদ্যমান কোনওের সাথে দ্বন্দ্ব হয় তবে তা বিরক্তিকর হবে)।
কেন স্ট্যান্ডার্ডটি end()
প্রকৃত প্রান্তের পরিবর্তে শেষের মতো হিসাবে সংজ্ঞায়িত হয়?
কারণ:
begin()
সমান
end()
এবং end()
না পৌঁছে যায় ততক্ষণ চালিয়ে যায় ।কারণ তখন
size() == end() - begin() // For iterators for whom subtraction is valid
এবং আপনার মতো বিশ্রী জিনিসগুলি করতে হবে না
// Never mind that this is INVALID for input iterators...
bool empty() { return begin() == end() + 1; }
এবং আপনি দুর্ঘটনাক্রমে ভুল কোড পছন্দ করবেন না
bool empty() { return begin() == end() - 1; } // a typo from the first version
// of this post
// (see, it really is confusing)
bool empty() { return end() - begin() == -1; } // Signed/unsigned mismatch
// Plus the fact that subtracting is also invalid for many iterators
এছাড়াও: কোনও বৈধ উপাদানকে নির্দেশ করলে কী find()
ফিরে আসবে end()
?
আপনি কি সত্যিই অন্য একজন সদস্য হিসাবে পরিচিত চান invalid()
যা একজন অবৈধ পুনরাবৃত্তিকে ফেরত দেয় ?!
দুটি পুনরুক্তিকারী ইতিমধ্যে যথেষ্ট বেদনাদায়ক ...
ওহ, এবং দেখতে এই সংশ্লিষ্ট পোস্টে ।
যদি end
শেষ উপাদানটির আগে ছিল, তবে আপনি কীভাবে আসবেন insert()
?!
অর্ধ-বদ্ধ রেঞ্জগুলির পুনরাবৃত্তি আইডিয়াম [begin(), end())
মূলত প্লেইন অ্যারেগুলির জন্য পয়েন্টার গাণিতিকের উপর ভিত্তি করে। অপারেশনের সেই মোডে আপনার ফাংশনগুলি হবে যা একটি অ্যারে এবং একটি আকার পেরিয়ে গেছে।
void func(int* array, size_t size)
অর্ধ-বদ্ধ রেঞ্জগুলিতে রূপান্তর করা [begin, end)
খুব সহজ যখন আপনার কাছে সেই তথ্য থাকে:
int* begin;
int* end = array + size;
for (int* it = begin; it < end; ++it) { ... }
সম্পূর্ণ-বদ্ধ রেঞ্জগুলির সাথে কাজ করা, এটি আরও শক্ত:
int* begin;
int* end = array + size - 1;
for (int* it = begin; it <= end; ++it) { ... }
যেহেতু অ্যারেগুলিতে নির্দেশকগুলি সি ++ এ পুনরাবৃত্ত হয় (এবং সিনট্যাক্স এটির অনুমতি দেওয়ার জন্য ডিজাইন করা হয়েছিল), কল করার std::find(array, array + size, some_value)
চেয়ে এটি কল করা আরও সহজ std::find(array, array + size - 1, some_value)
।
এছাড়াও, আপনি যদি অর্ধ-বন্ধ রেঞ্জগুলির সাথে কাজ করেন তবে আপনি !=
অপারেটরটি শেষ শর্তটি পরীক্ষা করতে পারবেন, কারণ (যদি আপনার অপারেটরগুলি সঠিকভাবে সংজ্ঞায়িত করা হয়) তবে <
বোঝা যায় !=
।
for (int* it = begin; it != end; ++ it) { ... }
তবে সম্পূর্ণরূপে বন্ধ রেঞ্জগুলির সাথে এটি করার সহজ উপায় নেই। তোমার সাথে আটকে আছি করছি <=
।
একমাত্র ধরণের পুনরাবৃত্তি যা সি ++ এ সমর্থন করে <
এবং >
ক্রিয়াকলাপগুলি এলোমেলো-অ্যাক্সেস পুনরুক্তি হয়। যদি আপনাকে <=
সি ++ তে প্রতিটি পুনরুক্তি ক্লাসের জন্য অপারেটর লিখতে হয় , আপনাকে আপনার সমস্ত পুনরুক্তিদাতা সম্পূর্ণরূপে তুলনামূলক করে তুলতে হবে এবং আপনি কম সক্ষম পুনরাবৃত্তকারী তৈরি করতে (যেমন দ্বিদ্বিদী পুনরুদ্ধারকারী std::list
বা ইনপুট পুনরাবৃত্তকারী) তৈরি করার জন্য আপনার পছন্দ কম ছিল কাজ যে iostreams
) যদি সি ++ সম্পূর্ণ বদ্ধ রেঞ্জ ব্যবহৃত।
সঙ্গে end()
শেষ অতীত ইশারা এক, এটা লুপ জন্য একটি সঙ্গে একটি সংগ্রহ পুনরুক্তি করতে সহজ:
for (iterator it = collection.begin(); it != collection.end(); it++)
{
DoStuff(*it);
}
end()
শেষ উপাদানটির দিকে ইঙ্গিত করার সাথে সাথে একটি লুপ আরও জটিল হবে:
iterator it = collection.begin();
while (!collection.empty())
{
DoStuff(*it);
if (it == collection.end())
break;
it++;
}
begin() == end()
,।!=
পরিবর্তে ব্যবহার করার প্রবণতা রাখে <
, সুতরাং end()
পজিশনের সমাপ্তি একটি অবস্থানের দিকে ইশারা করা সুবিধাজনক।