সি ++ 11 এ নন-সদস্য কেন শুরু এবং শেষ কার্যগুলি ব্যবহার করবেন?


197

প্রতিটি ধারক ধারকটির সেই ধারকটির জন্য পুনরাবৃত্তিকারীগুলির জন্য beginএবং endপদ্ধতি রয়েছে। যাইহোক, সি ++ 11 দৃশ্যত চালু করেছে বিনামূল্যে ফাংশন বলা std::beginএবং std::endযা কল beginএবং endসদস্য ফাংশন। সুতরাং, পরিবর্তে লেখার

auto i = v.begin();
auto e = v.end();

আপনি লিখবেন

auto i = std::begin(v);
auto e = std::end(v);

মডার্ন সি ++ রচনায় তাঁর আলাপে , হার্ব সটার বলেছেন যে আপনি যখন কোনও ধারকটির জন্য শুরু বা শেষ পুনরাবৃত্তি চান তখন আপনার এখন সর্বদা ফ্রি ফাংশন ব্যবহার করা উচিত। তবে আপনি কেন চাইবেন সে সম্পর্কে তিনি বিশদে যান না । কোডটির দিকে তাকানো, এটি আপনাকে একটি চরিত্রের সমস্তটি সংরক্ষণ করে। সুতরাং, স্ট্যান্ডার্ড পাত্রে যতদূর যায়, ফ্রি ফাংশনগুলি সম্পূর্ণ অকেজো বলে মনে হয়। ভেষজ সুটার ইঙ্গিত দিয়েছিল যে অ-মানক ধারকগুলির জন্য সুবিধাগুলি ছিল, তবে আবার তিনি বিশদে যান নি।

সুতরাং, প্রশ্ন ঠিক মুক্ত ফাংশন সংস্করণ না কি std::beginএবং std::endতাদের সংশ্লিষ্ট সদস্য ফাংশন সংস্করণ কলিং পরলোক না, এবং আপনি তাদের ব্যবহার করার জন্য কেন চাইবে?


29
এটি একটি কম চরিত্র, আপনার বাচ্চাদের জন্য এই বিন্দুগুলি সংরক্ষণ করুন: xkcd.com/297
হোস্টাইলফোর্ক বলেছেন যে

আমি সেগুলি ব্যবহারে কোনওভাবেই ঘৃণা করব কারণ আমাকে std::সর্বদা পুনরাবৃত্তি করতে হবে।
মাইকেল চৌর্দাকিস

উত্তর:


162

আপনি কিভাবে সি-অ্যারে কল করবেন .begin()এবং করবেন .end()?

ফ্রি-ফাংশনগুলি আরও জেনেরিক প্রোগ্রামিংয়ের অনুমতি দেয় কারণ সেগুলি পরে যুক্ত করা যেতে পারে, এমন কোনও ডেটা-কাঠামোতে আপনি পরিবর্তন করতে পারবেন না।


7
@ জোনাথনএম ডেভিস: আপনি টেমপ্লেট প্রোগ্রামিং ট্রিকস ব্যবহার করে endস্ট্যাটিকালি ঘোষিত অ্যারে ( int foo[5]) পেতে পারেন। একবার এটি কোনও পয়েন্টারে ক্ষয় হয়ে যাওয়ার পরে অবশ্যই ভাগ্যের বাইরে of
ম্যাথিউ এম।

33
template<typename T, size_t N> T* end(T (&a)[N]) { return a + N; }
হিউজ

6
@JonathanMDavis: অন্যদের নির্দেশিত হিসাবে, এটা অবশ্যই সম্ভব পেতে beginএবং endযতদিন একটি সি অ্যারে হিসাবে আপনি ইতিমধ্যে নিজের একটি পয়েন্টার তা জীর্ণ হয় নি - @Huw এটা spells আউট। আপনি কেন চান: যেমন কল্পনা করুন যে আপনি এমন কোডটি রিফেক্টর করেছিলেন যা কোনও ভেক্টর (বা যে কোনও কারণেই হোক না কেন, উল্টোদিকে) ব্যবহার করতে অ্যারে ব্যবহার করে was আপনি যদি ব্যবহার করছেন beginএবং end, এবং সম্ভবত কিছু চালাক টাইপডেফিং, বাস্তবায়ন কোডটি একেবারেই পরিবর্তন করতে হবে না (সম্ভবত কিছু টাইপডেফ বাদে)।
কার্ল নচেটেল

31
@ জোনাথন এম ডেভিস: অ্যারে পয়েন্টার নয়। এবং সকলের জন্য: এই সর্বকালের বিশিষ্ট বিভ্রান্তি বন্ধ করার স্বার্থে (কিছু) পয়েন্টারগুলিকে "ক্ষয়ে যাওয়া অ্যারে" হিসাবে উল্লেখ করা বন্ধ করুন। ভাষাতে এরকম কোনও পরিভাষা নেই এবং এর জন্য আসলে ব্যবহার হয় না। পয়েন্টারগুলি পয়েন্টার হয়, অ্যারেগুলি অ্যারে হয়। অ্যারেগুলিকে স্পষ্টভাবে তাদের প্রথম উপাদানটিতে পয়েন্টারে রূপান্তর করা যেতে পারে, তবে এটি এখনও কেবল একটি নিয়মিত পুরানো পয়েন্টার, অন্যের সাথে কোনও পার্থক্য ছাড়াই। অবশ্যই আপনি কোনও পয়েন্টারের "শেষ" পেতে পারবেন না, কেস বন্ধ রয়েছে।
GManNickG

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

35

আপনার যখন পাঠাগার রয়েছে এমন ক্লাসটি রয়েছে তখন কেসটি বিবেচনা করুন:

class SpecialArray;

এটির 2 টি পদ্ধতি রয়েছে:

int SpecialArray::arraySize();
int SpecialArray::valueAt(int);

পুনরুক্তি করতে এটা মান ধরে এই বর্গ থেকে উত্তরাধিকারী প্রয়োজন এবং সংজ্ঞায়িত begin()এবং end()মামলা জন্য পদ্ধতি

auto i = v.begin();
auto e = v.end();

আপনি যদি সবসময় ব্যবহার করেন

auto i = begin(v);
auto e = end(v);

তুমি এটি করতে পারো:

template <>
SpecialArrayIterator begin(SpecialArray & arr)
{
  return SpecialArrayIterator(&arr, 0);
}

template <>
SpecialArrayIterator end(SpecialArray & arr)
{
  return SpecialArrayIterator(&arr, arr.arraySize());
}

যেখানে SpecialArrayIteratorকিছু আছে:

class SpecialArrayIterator
{
   SpecialArrayIterator(SpecialArray * p, int i)
    :index(i), parray(p)
   {
   }
   SpecialArrayIterator operator ++();
   SpecialArrayIterator operator --();
   SpecialArrayIterator operator ++(int);
   SpecialArrayIterator operator --(int);
   int operator *()
   {
     return parray->valueAt(index);
   }
   bool operator ==(SpecialArray &);
   // etc
private:
   SpecialArray *parray;
   int index;
   // etc
};

এখন iএবং eস্পেশালআরির মানগুলি পুনরাবৃত্তি এবং অ্যাক্সেসের জন্য আইনত ব্যবহার করা যেতে পারে


8
এটি template<>লাইন অন্তর্ভুক্ত করা উচিত নয় । আপনি কোনও নতুন ফাংশন ওভারলোড ঘোষণা করছেন, কোনও টেম্পলেট বিশেষ করে না।
ডেভিড স্টোন

33

ব্যবহার beginএবং endবিনামূল্যে ফাংশন অপ্রত্যক্ষ্যতার এক স্তর যোগ করে। সাধারণত এটি আরও নমনীয়তার অনুমতি দেওয়ার জন্য করা হয়।

এই ক্ষেত্রে আমি কয়েকটি ব্যবহার সম্পর্কে ভাবতে পারি।

সর্বাধিক সুস্পষ্ট ব্যবহার হ'ল সি-অ্যারে (সি পয়েন্টার নয়)।

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

অন্যদিকে, পরবর্তী সি ++ রেভির ডি এর সিউডো-সদস্য স্বরলিপিটি অনুলিপি করা উচিত । যদি a.foo(b,c,d)এটি সংজ্ঞায়িত না হয় তবে পরিবর্তে চেষ্টা করে foo(a,b,c,d)। আমাদের দরিদ্র মানুষদের যারা তখন ক্রিয়া ক্রমানুসারে বিষয় পছন্দ করে তাদের সহায়তা করার জন্য এটি একটি সামান্য সিনট্যাকটিক চিনি।


5
সিউডো-সদস্য স্বরলিপি C # এর মত দেখাচ্ছে /। নেট এক্সটেনশন পদ্ধতি । তারা বিভিন্ন পরিস্থিতিতে যেমন দরকারী বৈশিষ্ট্য হিসাবে আসে - যেমন সমস্ত বৈশিষ্ট্য - 'আপত্তিজনক' প্রবণ হতে পারে।
গ্যারেথ উইলসন

5
ছদ্ম-সদস্য স্বরলিপি ইন্টেলিসেন্সের সাথে কোডিংয়ের জন্য একটি वरदान; "এ।" মারছে প্রাসঙ্গিক ক্রিয়াগুলি দেখায়, মুখস্তের তালিকা থেকে মস্তিষ্কের শক্তি মুক্ত করে এবং প্রাসঙ্গিক এপিআই ফাংশনগুলি আবিষ্কার করতে সহায়তা করে ক্লাসে সদস্যবিহীন ক্রিয়াকলাপকে জুতোহীন না করে ডুপ্লিকেট কার্যকারিতা রোধ করতে পারে।
ম্যাট কার্টিস

এটি সি ++ এ পাওয়ার প্রস্তাব রয়েছে, যা ইউনিফাইড ফাংশন কল সিনট্যাক্স (ইউএফসিএস) শব্দটি ব্যবহার করে।
আন্ডারস্কোর_

17

আপনার প্রশ্নের উত্তর দেওয়ার জন্য, ফ্রি ফাংশনগুলি () শুরু হয় (এবং শেষ হয়) ডিফল্টরূপে ধারকটির সদস্য .begin () এবং .end () ফাংশনগুলিকে কল করা ছাড়া আর কিছুই করে না। থেকে <iterator>, স্বয়ংক্রিয়ভাবে অন্তর্ভুক্ত যখন তোমার মত মান পাত্রে কোনো একটি ব্যবহার <vector>, <list>ইত্যাদি আপনি পান:

template< class C > 
auto begin( C& c ) -> decltype(c.begin());
template< class C > 
auto begin( const C& c ) -> decltype(c.begin()); 

আপনার প্রশ্নের দ্বিতীয় অংশটি হ'ল ফ্রি ফাংশনগুলিকে কেন পছন্দ করুন যদি তারা যেভাবেই মেম্বার ফাংশনগুলিকে কল করে। এটি vআপনার উদাহরণ কোডে কোন ধরণের অবজেক্টের উপর নির্ভর করে । যদি ভি এর ধরণটি একটি স্ট্যান্ডার্ড ধারক প্রকারের মতো হয় vector<T> v;তবে আপনি ফ্রি বা সদস্য ফাংশনগুলি ব্যবহার করেন কিনা তা বিবেচ্য নয়, তারা একই কাজ করে। যদি আপনার বস্তুটি vআরও জেনেরিক হয় তবে নীচের কোডটির মতো:

template <class T>
void foo(T& v) {
  auto i = v.begin();     
  auto e = v.end(); 
  for(; i != e; i++) { /* .. do something with i .. */ } 
}

তারপরে সদস্য ফাংশনগুলি ব্যবহার করে আপনার টি = সি অ্যারে, সি স্ট্রিং, এনাম ইত্যাদির জন্য কোডটি ভেঙে দেয় সদস্যবিহীন ফাংশনগুলি ব্যবহার করে আপনি একটি আরও জেনেরিক ইন্টারফেসের বিজ্ঞাপন দেন যা লোকেরা সহজেই প্রসারিত করতে পারে। বিনামূল্যে ফাংশন ইন্টারফেস ব্যবহার করে:

template <class T>
void foo(T& v) {
  auto i = begin(v);     
  auto e = end(v); 
  for(; i != e; i++) { /* .. do something with i .. */ } 
}

কোডটি এখন টি = সি অ্যারে এবং সি স্ট্রিংয়ের সাথে কাজ করে। এখন অল্প পরিমাণে অ্যাডাপ্টার কোডটি লিখছি:

enum class color { RED, GREEN, BLUE };
static color colors[]  = { color::RED, color::GREEN, color::BLUE };
color* begin(const color& c) { return begin(colors); }
color* end(const color& c)   { return end(colors); }

আমরা আপনার কোডটি পুনরাবৃত্তীয় এনামগুলির সাথেও সামঞ্জস্যপূর্ণ হতে পারি। আমি ভেবেছি যে হার্বের মূল বক্তব্য হ'ল ফ্রি ফাংশনগুলি ব্যবহার করা যেমন সদস্য ফাংশনগুলি ব্যবহার করা ঠিক তত সহজ এবং এটি আপনার কোডটিকে সি সিকোয়েন্স প্রকারের সাথে পিছনে সামঞ্জস্য দেয় এবং নন-স্টল সিকোয়েন্স প্রকারের (এবং ভবিষ্যতের-স্টাইল প্রকারের সাথে সামঞ্জস্য) দেয়, অন্যান্য বিকাশকারীদের কম খরচে।


সুন্দর উদাহরণ। যদিও আমি enumরেফারেন্স দ্বারা কোনও বা অন্য কোনও মৌলিক ধরণ গ্রহণ করব না ; অপ্রত্যক্ষের চেয়ে তারা অনুলিপি করার জন্য সস্তা হবে।
আন্ডারস্কোর_২৪

6

এর একটি সুবিধা std::beginএবং std::endএটি হ'ল তারা বাহ্যিক শ্রেণীর জন্য স্ট্যান্ডার্ড ইন্টারফেস প্রয়োগের জন্য এক্সটেনশন পয়েন্ট হিসাবে পরিবেশন করে।

আপনি যদি CustomContainerলুপ বা টেম্পলেট ফাংশনটির প্রত্যাশা .begin()এবং .end()পদ্ধতিগুলির জন্য পরিসীমা ভিত্তিক শ্রেণি ব্যবহার করতে চান তবে আপনাকে অবশ্যই এই পদ্ধতিগুলি প্রয়োগ করতে হবে।

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

এটি সর্বদা সম্ভব হয় না, উদাহরণস্বরূপ, বহিরাগত গ্রন্থাগার ব্যবহার করার সময়, প্রচ্ছন্নভাবে বাণিজ্যিক এবং বদ্ধ উত্সটি।

এই ধরনের পরিস্থিতিতে, std::beginএবং std::endউপকারে আসুন, যেহেতু কেউ ক্লাস নিজেই পরিবর্তন না করে পুনরাবৃত্তিকারী এপিআই সরবরাহ করতে পারে, বরং ফ্রি ফাংশনগুলিকে ওভারলোড করে।

উদাহরণ: ধরুন যে আপনি এমন count_ifফাংশন বাস্তবায়ন করতে চান যা একজোড়া পুনরাবৃত্তির পরিবর্তে একটি ধারক গ্রহণ করে। এই জাতীয় কোডটি এর মতো দেখতে পারে:

template<typename ContainerType, typename PredicateType>
std::size_t count_if(const ContainerType& container, PredicateType&& predicate)
{
    using std::begin;
    using std::end;

    return std::count_if(begin(container), end(container),
                         std::forward<PredicateType&&>(predicate));
}

এখন, আপনি যে কোনও শ্রেণীর জন্য এই রীতিনীতিটি ব্যবহার করতে চান count_if, সেই ক্লাসগুলি সংশোধন করার পরিবর্তে আপনাকে কেবল দুটি ফ্রি ফাংশন যুক্ত করতে হবে।

এখন, সি ++ এর আর্গুমেন্ট ডিপেন্ডেন্ট লুকআপ (এডিএল) নামে একটি মেকানিসিম রয়েছে, যা এই ধরণের পদ্ধতিকে আরও নমনীয় করে তোলে।

সংক্ষেপে, এডিএলটির অর্থ, যখন কোনও সংকলক একটি অযোগ্য ফাংশন (অর্থাত্ নেমস্পেস ব্যতীত ফাংশন, beginতার পরিবর্তে std::begin) সমাধান করে, তখন তার যুক্তিগুলির নামস্থানগুলিতে ঘোষিত ফাংশনগুলিও বিবেচনা করবে। উদাহরণ স্বরূপ:

namesapce some_lib
{
    // let's assume that CustomContainer stores elements sequentially,
    // and has data() and size() methods, but not begin() and end() methods:

    class CustomContainer
    {
        ...
    };
}

namespace some_lib
{    
    const Element* begin(const CustomContainer& c)
    {
        return c.data();
    }

    const Element* end(const CustomContainer& c)
    {
        return c.data() + c.size();
    }
}

// somewhere else:
CustomContainer c;
std::size_t n = count_if(c, somePredicate);

এই ক্ষেত্রে, এটা কোন ব্যাপার না যে যোগ্যতাসম্পন্ন নাম some_lib::beginএবং some_lib::end - যেহেতু CustomContainerহয় some_lib::খুব, কম্পাইলার ঐ overloads ব্যবহার করবে count_if

এটি থাকার using std::begin;এবং using std::end;ভিতরে থাকার কারণও count_if। এটা আমাদের অযোগ্য ব্যবহার করার অনুমতি দেয় beginএবং endসেইজন্য ADL জন্য, যার ফলে এবং কম্পাইলার বাছাই করতে সক্ষম হবেন std::beginএবং std::endযখন অন্য কোন বিকল্প পাওয়া যায়।

আমরা কুকিটি খেতে পারি এবং কুকি রাখতে পারি - যেমন begin/ endএর সংযোজনকারীটি স্ট্যান্ডার্ডগুলিতে ফিরে যেতে পারে তার কাস্টম বাস্তবায়ন করার একটি উপায় আছে ।

কিছু নোট:

  • একই কারণে, অন্যান্য অনুরূপ ফাংশন রয়েছে: std::rbegin/ rend, std::sizeএবং std::data

  • অন্যান্য উত্তরের হিসাবে উল্লেখ করা হয়েছে, std::সংস্করণগুলিতে নগ্ন অ্যারেগুলির জন্য অতিরিক্ত পরিমাণ রয়েছে। এটি দরকারী, তবে কেবল আমি উপরে বর্ণিত একটি বিশেষ বিষয়।

  • std::beginটেমপ্লেট কোড লেখার সময় ব্যবহার এবং বন্ধুরা বিশেষত ভাল ধারণা, কারণ এটি সেই টেমপ্লেটগুলিকে আরও জেনেরিক করে তোলে। অ-টেম্পলেট জন্য আপনি প্রযোজ্য সময়ে ঠিক একই পদ্ধতি ব্যবহার করতে পারেন।

পিএস আমি জানি যে এই পোস্টটি প্রায় 7 বছরের পুরানো। আমি এটি পেরিয়ে এসেছি কারণ আমি এমন একটি প্রশ্নের উত্তর দিতে চেয়েছিলাম যা সদৃশ হিসাবে চিহ্নিত হয়েছিল এবং আবিষ্কার হয়েছিল যে এখানে কোনও উত্তর এডিএল উল্লেখ করে না।


ভাল উত্তর, বিশেষত এডিএলকে স্পষ্টভাবে ব্যাখ্যা করার পরিবর্তে একে একে অন্যের মতো কল্পনার উপরে রেখে দেওয়া - এমনকি তারা
আন্ডারস্কোর_

5

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

তবে অবশ্যই এটি সর্বদা যথাযথভাবে ওজন করা উচিত এবং বিমূর্ত বিস্তৃতি খুব ভাল হয় না। যদিও ফ্রি ফাংশনগুলি ব্যবহার করা অত্যধিক বিমূর্ততার খুব বেশি নয়, তবুও এটি সি ++ 03 কোডের সাথে সামঞ্জস্যতা ভঙ্গ করে, যা সি ++ 11 এর এই অল্প বয়সে এখনও আপনার পক্ষে একটি সমস্যা হতে পারে।


3
সি ++ তে আপনি কেবল boost::begin()/ ব্যবহার করতে পারেন end(), তাই কোনও বাস্তব অসঙ্গতি নেই :)
মার্ক মট্জ - মিমুতজ

1
@ মার্কমুটজ-মিমুটজ ওয়েল, নির্ভরতা বাড়ানো সর্বদা একটি বিকল্প নয় (এবং এটি কেবলমাত্র ব্যবহৃত হলে যথেষ্ট ওভারকিল begin/end)। সুতরাং আমি বিবেচনা করব যে খাঁটি সি +++ এর সাথেও একটি বেমানান। তবে যেমনটি বলেছেন, এটি বরং একটি ছোট (এবং আরও ছোট হওয়া) অসঙ্গতি, কারণ সি ++ 11 (কমপক্ষে begin/endবিশেষত) যেভাবেই হোক, আরও বেশি গ্রহণ করা হচ্ছে।
খ্রিস্টান রাউ

0

শেষ পর্যন্ত সুবিধাটি এমন কোডে হয় যা সাধারণীকরণ হয় যেমন এটি ধারক অজ্ঞাব্য। এটি std::vectorকোড, নিজেই কোনও পরিবর্তন ছাড়াই একটি , একটি অ্যারে বা একটি ব্যাপ্তিতে পরিচালনা করতে পারে।

অতিরিক্তভাবে, ধারকগুলি, এমনকি অ-মালিকানাধীন পাত্রেও এমন পুনরুদ্ধার করা যেতে পারে যে সেগুলি অ-সদস্য বিস্তৃত ভিত্তিক অ্যাকসেসরগুলি ব্যবহার করে কোড দ্বারা বৈজ্ঞানিকভাবে ব্যবহার করা যেতে পারে।

আরও বিশদ জন্য এখানে দেখুন ।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.