কেন পুনরায় পুনরুক্তি করার সময় সেট থেকে যুক্ত করার সময় এবং সরানোর সময় আমি এই অনেকগুলি পুনরাবৃত্তি পেতে পারি?


61

পাইথন ফর-লুপটি বোঝার চেষ্টা করে, আমি ভেবেছিলাম যে এটি {1}একটি পুনরাবৃত্তির জন্য ফলাফল দেবে , বা কেবল সি বা অন্যান্য ভাষার মতো পুনরাবৃত্তিটি করে কিনা তার উপর নির্ভর করে একটি অসীম লুপে আটকে যাবে। কিন্তু বাস্তবে এটিও হয়নি।

>>> s = {0}
>>> for i in s:
...     s.add(i + 1)
...     s.remove(i)
...
>>> print(s)
{16}

এটি কেন 16 টি পুনরাবৃত্তি করে? ফলাফল {16}কোথা থেকে আসে?

এটি পাইথন ৩.৮.২ ব্যবহার করছিল। পাইপিতে এটি প্রত্যাশিত ফলাফল করে {1}


17
আপনার যোগ করা আইটেমগুলির উপর নির্ভর করে, প্রতিটি কল s.add(i+1)(এবং সম্ভবত, কল করতে s.remove(i)) সেটটির পুনরাবৃত্তির ক্রম পরিবর্তন করতে পারে, লুপের জন্য তৈরি সেট পুনরুদ্ধারটি পরবর্তীটি কী দেখবে তা প্রভাবিত করে। আপনার একটি সক্রিয় পুনরাবৃত্তি করার সময় কোনও বস্তুর রূপান্তর করবেন না।
চিপনার

6
আমি সেটিও লক্ষ্য করেছি t = {16}এবং তারপরে t.add(15)ফলন করেছি যে সেটটি {16, 15}} আমার মনে হয় সমস্যাটা কোথাও আছে।

19
এটি একটি বাস্তবায়নের বিশদ - 16 টির চেয়ে 15 কম হ্যাশ রয়েছে (এটিই @ অনন লক্ষ্য করেছেন), তাই সেট ধরণের 16 টি যুক্ত করে এটি পুনরুক্তির "ইতিমধ্যে দেখা" অংশে যুক্ত করে, এবং এভাবে পুনরুক্তিটি ক্লান্ত হয়ে যায়।
বায়োটোসমেটেক

1
আপনি যদি ট্রট ডি ডকস পড়েন তবে একটি নোট আছে যে লুপ চলাকালীন পুনরুদ্ধার পুনরাবৃত্তি কিছু বাগ তৈরি করতে পারে। দেখুন: docs.python.org/3.7/references/…
মার্সেলো ফ্যাব্রিজিও

3
@ বায়োস্মাটেক: সিপিথন ৩.৮.২-এ, হ্যাশ (১)) == ১ and এবং হ্যাশ (১৫) == ১৫. আচরণটি হ্যাশ থেকে কম হওয়ার কারণে আসে না; উপাদানগুলিকে একটি সেটে সরাসরি হ্যাশ ক্রমে সংরক্ষণ করা হয় না।
ব্যবহারকারী 2357112 মনিকা

উত্তর:


86

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

আমি যা বলতে চাইছি তা হ'ল বাস্তবায়ন বিশদ, বিজ্ঞপ্তি ছাড়াই পরিবর্তিত হতে পারে to যদি আপনি এমন কোনও প্রোগ্রাম লিখেন যা এর কোনওটির উপর নির্ভর করে, আপনার প্রোগ্রামটি পাইথন বাস্তবায়ন এবং সিপাইথন ৩.৮.২ ব্যতীত অন্য সংস্করণে কোনও মিল ফেলতে পারে।

কেন লুপটি 16 এ শেষ হয় তার সংক্ষিপ্ত ব্যাখ্যাটি হ'ল 16 হল প্রথম উপাদান যা পূর্ববর্তী উপাদানের তুলনায় নিম্ন হ্যাশ টেবিল সূচীতে স্থাপন করা হয়। সম্পূর্ণ ব্যাখ্যা নীচে।


পাইথন সেটের অভ্যন্তরীণ হ্যাশ টেবিলটিতে সর্বদা 2 আকারের শক্তি থাকে। আকার 2 ^ n এর টেবিলের জন্য, যদি কোনও সংঘর্ষ না ঘটে, উপাদানগুলি হ্যাশের টেবিলের অবস্থানগুলিতে তাদের হ্যাশের ন্যূনতম-তাত্পর্যপূর্ণ বিটগুলির সাথে সংগৃহীত থাকে। আপনি এটি প্রয়োগ করা দেখতে পারেন set_add_entry:

mask = so->mask;
i = (size_t)hash & mask;

entry = &so->table[i];
if (entry->key == NULL)
    goto found_unused;

বেশিরভাগ ছোট পাইথন ইনস হ্যাশ তাদের কাছে; বিশেষত, আপনার পরীক্ষার সমস্ত হিট তাদের কাছে। আপনি এটি প্রয়োগ করা দেখতে পারেন long_hash। যেহেতু আপনার সেটটিতে দুটি হ্যাশগুলিতে সমান কম বিটযুক্ত দুটি উপাদান থাকে না তাই কোনও সংঘর্ষ হয় না।


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

while (i <= mask && (entry[i].key == NULL || entry[i].key == dummy))
    i++;
si->si_pos = i+1;
if (i > mask)
    goto fail;
si->len--;
key = entry[i].key;
Py_INCREF(key);
return key;

আপনার সেটটি শুরুতে 8 মাপের একটি হ্যাশ টেবিল এবং হ্যাশ টেবিলের 0সূচক 0 তে কোনও ইনট অবজেক্টের পয়েন্টার দিয়ে শুরু হয় । পুনরুক্তিটি সূচক 0-এও অবস্থিত you আপনি পুনরাবৃত্তি করার সাথে সাথে পরের সূচীতে প্রতিটি উপাদানগুলিকে হ্যাশ টেবিলের সাথে যুক্ত করা হয় কারণ সেখানে তাদের হ্যাশ তাদের রাখার জন্য বলেছে, এবং এটি সর্বদা পরবর্তী সূচকটি পুনরাবৃত্তিটি দেখায়। অপসারণ উপাদানগুলির সংঘর্ষের সমাধানের উদ্দেশ্যে, তাদের পুরানো অবস্থানে একটি ডামি চিহ্নিতকারী রয়েছে। আপনি এটি প্রয়োগ করা দেখতে পারেন set_discard_entry:

entry = set_lookkey(so, key, hash);
if (entry == NULL)
    return -1;
if (entry->key == NULL)
    return DISCARD_NOTFOUND;
old_key = entry->key;
entry->key = dummy;
entry->hash = -1;
so->used--;
Py_DECREF(old_key);
return DISCARD_FOUND;

যখন 4সেটে যুক্ত করা হয়, তখন সেটে উপাদান এবং ডামির সংখ্যা পর্যাপ্ত পরিমাণে হয়ে যায় যা set_add_entryএকটি হ্যাশ টেবিল পুনর্নির্মাণকে ট্রিগার করে set_table_resize:

if ((size_t)so->fill*5 < mask*3)
    return 0;
return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4);

so->usedহ্যাশ টেবিলের জনবহুল, নন-ডামি এন্ট্রিগুলির সংখ্যা, যা 2, সুতরাং set_table_resizeএটি দ্বিতীয় আর্গুমেন্ট হিসাবে 8 পায়। এর ভিত্তিতে, নতুন হ্যাশ টেবিলের আকার 16 হওয়া উচিত set_table_resize সিদ্ধান্ত নেয় :

/* Find the smallest table size > minused. */
/* XXX speed-up with intrinsics */
size_t newsize = PySet_MINSIZE;
while (newsize <= (size_t)minused) {
    newsize <<= 1; // The largest possible value is PY_SSIZE_T_MAX + 1.
}

এটি আকারের 16 এর সাথে হ্যাশ টেবিলটি পুনর্নির্মাণ করে elements সমস্ত হ্যাশ টেবিলের পুরাতন সূচীতে এখনও শেষ হয়, কারণ তাদের হ্যাশগুলিতে কোনও উচ্চ বিট সেট ছিল না।

লুপটি চলতে থাকায়, উপাদানগুলি পরবর্তী সূচকটিতে পুনরাবৃত্তকারীটি দেখতে পাবে। আরেকটি হ্যাশ টেবিল পুনর্নির্মাণটি ট্রিগার করা হয়েছে, তবে নতুন আকারটি এখনও 16।

যখন উপাদান হিসাবে লুপটি 16 যোগ করে তখন প্যাটার্নটি ভেঙে যায়। নতুন উপাদানটি রাখতে কোনও সূচক 16 নেই। 16 টির 4 টি সর্বনিম্ন বিটগুলি 0000 হয়, 16 টি সূচক 0 এ বসায় পুনরুক্তিটির সঞ্চিত সূচকটি এই মুহুর্তে 16 হয় এবং যখন লুপটি পুনরুক্তি থেকে পরবর্তী উপাদানটির জন্য জিজ্ঞাসা করে, পুনরুক্তিটি দেখতে পায় যে এটি শেষের শেষে চলে গেছে হ্যাশ টেবিল.

পুনরুক্তিকারী কেবলমাত্র 16সেটে রেখে এই সময়ে লুপটি সমাপ্ত করে ।


14

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

আপনি যখন আপনার সেটটিতে পুনরাবৃত্তি এবং আইটেম যুক্ত করেন তখন নতুন হ্যাশগুলি তৈরি করা এবং হ্যাশ টেবিলের সাথে সংযুক্ত করা হচ্ছে যতক্ষণ না আপনি 16 নম্বরে পৌঁছান this আপনি যেহেতু সারণীর প্রথম সারিতে ইতিমধ্যে পুনরাবৃত্তি করেছেন তাই পুনরাবৃত্তি লুপটি শেষ হয়।

আমার উত্তর একটি অনুরূপ প্রশ্নের এই একটি উপর ভিত্তি করে , এটি আসলে এই একই একই উদাহরণ দেখায়। আমি আরও বিশদ জন্য এটি সত্যিই পড়ার সুপারিশ।


5

পাইথন 3 নথি থেকে:

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

একটি অনুলিপি উপর Iterate

s = {0}
s2 = s.copy()
for i in s2:
     s.add(i + 1)
     s.remove(i)

যা কেবল 1 বার পুনরাবৃত্তি করা উচিত

>>> print(s)
{1}
>>> print(s2)
{0}

সম্পাদনা: এই পুনরাবৃত্তির একটি সম্ভাব্য কারণ হ'ল একটি সেট আনর্ডার্ড করা হয়েছে, যার ফলে কোনও ধরণের স্ট্যাক ট্রেস সাজানোর জিনিস রয়েছে। যদি আপনি এটি একটি তালিকা দিয়ে থাকেন এবং একটি সেট নয়, তবে এটি কেবল শেষ হবে s = [1]কারণ তালিকা অর্ডার করা হয়েছে তাই লুপের জন্য সূচক 0 দিয়ে শুরু হবে এবং তারপরে পরবর্তী সূচীতে চলে যাবে, সেখানে খুঁজে পাওয়া যাচ্ছে না যে একটি নেই, এবং লুপ থেকে প্রস্থান করা হচ্ছে।


হ্যাঁ. তবে আমার প্রশ্ন কেন এটি 16 টি পুনরাবৃত্তি করে।
নুব ওভারফ্লো

সেটটি অর্ডারযুক্ত নয়। ডিকোরিজস এবং সেটগুলি একটি অ-এলোমেলো ক্রমে পুনরাবৃত্তি হয় এবং যদি আপনি কোনও কিছু পরিবর্তন না করেন তবে পুনরাবৃত্তির এই অ্যালগরিদম কেবল ধরে রাখে। তালিকাগুলি এবং টিপলসের জন্য এটি সূচক অনুসারে পুনরাবৃত্তি করতে পারে। আমি যখন আপনার কোডটি 3.7.2 এ চেষ্টা করেছি তখন এটি 8 টি পুনরাবৃত্তি করেছিল।
এরিক জ্বিন

পুনরাবৃত্তির আদেশটি সম্ভবত হ্যাশিংয়ের সাথে করতে হবে, যেমন অন্যরা উল্লেখ করেছেন
এরিক জিন

1
এর অর্থ "কোন ধরণের স্ট্যাক ট্রেস সাজানোর জিনিসটি সৃষ্টি করে" এর অর্থ কী? কোডটি ক্র্যাশ বা ত্রুটি করে নি তাই আমি কোনও স্ট্যাক ট্রেস দেখতে পেলাম না। আমি কীভাবে পাইথনে স্ট্যাক ট্রেস সক্ষম করব?
নুব ওভারফ্লো

1

পাইথন একটি আনর্ডারড সংগ্রহ সেট করে যা উপাদান অবস্থান বা সন্নিবেশের ক্রম রেকর্ড করে না। পাইথন সেটে কোনও উপাদানের সাথে কোনও সূচক সংযুক্ত নেই। সুতরাং তারা কোনও ইনডেক্সিং বা স্লাইসিং অপারেশন সমর্থন করে না।

সুতরাং আশা করবেন না যে আপনার জন্য লুপটি একটি নির্ধারিত ক্রমে কাজ করবে।

এটি কেন 16 টি পুনরাবৃত্তি করে?

user2357112 supports Monicaইতিমধ্যে প্রধান কারণ ব্যাখ্যা। এখানে, চিন্তাভাবনার অন্য উপায়।

s = {0}
for i in s:
     s.add(i + 1)
     print(s)
     s.remove(i)
print(s)

আপনি যখন এই কোডটি চালান এটি আপনাকে এটিকে আউটপুট দেয়:

{0, 1}                                                                                                                               
{1, 2}                                                                                                                               
{2, 3}                                                                                                                               
{3, 4}                                                                                                                               
{4, 5}                                                                                                                               
{5, 6}                                                                                                                               
{6, 7}                                                                                                                               
{7, 8}
{8, 9}                                                                                                                               
{9, 10}                                                                                                                              
{10, 11}                                                                                                                             
{11, 12}                                                                                                                             
{12, 13}                                                                                                                             
{13, 14}                                                                                                                             
{14, 15}                                                                                                                             
{16, 15}                                                                                                                             
{16}       

লুপ বা সেট মুদ্রণের মতো আমরা যখন সমস্ত উপাদান একসাথে অ্যাক্সেস করি তখন পুরো সেটটি অতিক্রম করার জন্য অবশ্যই পূর্বনির্ধারিত ক্রম থাকতে হবে। সুতরাং, গত পুনরাবৃত্তির আপনি থেকে মত পরিবর্তিত হয় দেখতে হবে {i,i+1}থেকে {i+1,i}

শেষ পুনরাবৃত্তির পরে এটি ঘটেছে যা i+1ইতিমধ্যে লুপ থেকে প্রস্থান করা হয়েছে।

আকর্ষণীয় ঘটনা: 6 এবং 7 বাদে 16 এর চেয়ে কম মান ব্যবহার করুন আপনাকে সর্বদা ফলাফল 16 দেয়।


"16 এর চেয়ে কম মান ব্যবহার করা আপনাকে সর্বদা 16 ফল দেয় result" - 6 বা 7 দিয়ে এটি ব্যবহার করে দেখুন এবং এটি দেখতে পাবেন না।
ব্যবহারকারী 2357112 মনিকা

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