كيفية التحقق من أن السلسلة متناظرة باستخدام التعبيرات العادية؟[مغلق]

StackOverflow https://stackoverflow.com/questions/233243

  •  04-07-2019
  •  | 
  •  

سؤال

كان هذا سؤال المقابلة الذي لم أتمكن من الإجابة عليه:

كيفية التحقق من أن السلسلة متناظرة باستخدام التعبيرات العادية؟

ملاحظة.هناك بالفعل سؤال "كيفية التحقق مما إذا كانت السلسلة المعطاة متناظرة؟"ويعطي الكثير من الإجابات بلغات مختلفة، ولكن لا توجد إجابة تستخدم التعبيرات العادية.

هل كانت مفيدة؟

المحلول

والجواب على هذا السؤال هو أنه "من المستحيل". وبشكل أكثر تحديدا، المقابلة يتساءل إذا كنت تدفع الانتباه في الصف النظرية الحسابية الخاصة بك.

في الدرجة نظرية الحسابية الخاص الذي تعلمته عن أجهزة الدولة محدودة. وتتكون آلة الدولة محدود من العقد والحواف. والمشروح كل حافة بحرف من الحروف الأبجدية محدودة. واحد أو أكثر من العقد هي خاصة "قبول" العقد وعقدة واحدة هي عقدة "بداية". كما هو قراءة كل حرف من كلمة تعطى لنا اجتياز حافة معينة في الجهاز. إذا نحن في نهاية المطاف في حالة قبول ثم نقول أن الجهاز "تقبل" هذه الكلمة.

ويمكن دائما ترجمة لتعبير عادي إلى آلة الدولة المحدودة ما يعادلها. وهذا هو، واحد التي تقبل وترفض نفس الكلمات كما التعبير العادي (في العالم الحقيقي، وبعض اللغات التعبير العادي تسمح لوظائف التعسفية، وهذه لا تعول).

ومن المستحيل بناء آلة الدولة المحدودة التي تقبل كل قلب مستو. يعتمد الدليل على الحقائق التي يمكننا بسهولة بناء السلسلة التي تتطلب عددا كبيرا بشكل تعسفي من العقد، وهي سلسلة

وو^ س ب أ ^ س (على سبيل المثال، ابا، aabaa، aaabaaa، aaaabaaaa، ....)

وحيث ^ x غير مرة وس المتكررة. وهذا يتطلب على الأقل العقد العاشر، لأنه بعد رؤية 'ب' علينا أن نعتمد ظهر العاشر مرات للتأكد من أنه هو سياق متناظر.

وأخيرا، والحصول على العودة إلى السؤال الأصلي، هل يمكن أن نقول المقابلة أنه يمكنك كتابة التعبير العادي أن يقبل كل قلب مستو التي تكون أصغر من بعض طول ثابت محدود. إذا كان هناك أي وقت مضى تطبيق في العالم الحقيقي الذي يتطلب تحديد قلب مستو فإنه من شبه المؤكد أن لا تشمل تلك طويلة بشكل تعسفي، وبالتالي هذه الإجابة سوف تظهر أنك يمكن أن تفرق المستحيلات النظرية من التطبيقات في العالم الحقيقي. ومع ذلك، من شأنه أن التعبير العادي الفعلي تكون طويلة جدا، أطول بكثير من برنامج 4-خط يعادل (ممارسة سهلة للقارئ: كتابة برنامج يحدد قلب مستو).

نصائح أخرى

وعلى الرغم من أن PCRE المحرك لا تدعم التعابير العادية متكررة (انظر <لأ href = "HTTPS: // ستاكوفيرفلوو كوم / أ / 48608623/18017 "> الجواب بيتر كراوس )، لا يمكنك استخدام التعابير المنطقية على في محرك ICU (وكما هو مستخدم، على سبيل المثال، من قبل شركة آبل) لتحقيق هذا دون رمز إضافي. سوف تحتاج إلى القيام بشيء من هذا القبيل:

وهذا يكشف أي سياق متناظر، ولكن لا تتطلب حلقة (والتي سوف تكون هناك حاجة لأن التعابير العادية لا يمكن الاعتماد).

$a = "teststring";
while(length $a > 1)
{
   $a =~ /(.)(.*)(.)/;
   die "Not a palindrome: $a" unless $1 eq $3;
   $a = $2;
}
print "Palindrome";

وليس من الممكن. لم يتم تعريف قلب مستو عن طريق لغة العادية. (انظر، لم تتعلم شيئا من الناحية النظرية الحسابية)

ومع بيرل التعابير المنطقية:

/^((.)(?1)\2|.?)$/

ورغم ذلك، فقد أشار الكثيرون، فإن هذا لا يمكن اعتبار التعبير العادي إذا كنت تريد أن تكون صارمة. التعابير العادية لا يدعم العودية.

هنا واحد للكشف عن قلب مستو 4-حرف (على سبيل المثال: الفعل)، أي نوع من الحرف:

\(.\)\(.\)\2\1

هنا واحد للكشف عن 5 حروف قلب مستو (على سبيل المثال: رادار )، والتحقق من حروف فقط:

\([a-z]\)\([a-z]\)[a-z]\2\1

وهكذا يبدو أننا بحاجة إلى التعابير المنطقية مختلف عن كل كلمة طول ممكن. تشمل هذه الوظيفة على قائمة بيثون البريدية بعض التفاصيل كما لماذا (محدود الدولة الآلات وضخ يما).

واعتمادا على مدى ثقة أنت، كنت أعطي هذه الإجابة:

<اقتباس فقرة>   

وأود أن لا تفعل ذلك مع العادية   التعبير. انها ليست المناسب   استخدام التعبيرات العادية.

على نعم ، أو يمكنك أن تفعل ذلك في صافي!

(?<N>.)+.?(?<-N>\k<N>)+(?(N)(?!))

ويمكنك التحقق من ذلك href="http://blog.stevenlevithan.com/archives/balancing-groups" هنا ! انها وظيفة رائعة!

وأما المثال لا الحصر سبق أن ذكرت، وليس هناك واحد التعبير العادي التي سوف الكشف عن سياق متناظر العام من خارج منطقة الجزاء، ولكن إذا كنت ترغب في الكشف قلب مستو تصل إلى مدة معينة، يمكنك استخدام شيء من هذا القبيل

(.?)(.?)(.?)(.?)(.?).?\5\4\3\2\1

StackOverflow مليء بإجابات مثل "التعبيرات العادية؟لا، لا يدعمون ذلك.هم لا أستطيع ادعمه.".

الحقيقة هي ذلك التعبيرات العادية لا علاقة لها القواعد النحوية العادية أي أكثر من ذلك. تتميز التعبيرات العادية الحديثة بوظائف مثل مجموعات التكرار والموازنة، كما أن توفر تطبيقاتها يتزايد باستمرار (انظر أمثلة روبي هنا، على سبيل المثال).في رأيي، فإن التمسك بالاعتقاد القديم بأن التعبيرات العادية في مجالنا ليست سوى مفهوم برمجي هو مجرد نتائج عكسية.وبدلاً من كرههم بسبب اختيار الكلمات الذي لم يعد الأنسب، فقد حان الوقت لنا لقبول الأمور والمضي قدمًا.

وهنا أ اقتباس من لاري وول, ، منشئ لغة Perl نفسها:

(...) يتعلق الأمر بشكل عام بما نسميه "التعبيرات العادية"، والتي لا ترتبط إلا بشكل هامشي بالتعبيرات العادية الحقيقية.ومع ذلك، فقد نما المصطلح مع قدرات محركات مطابقة الأنماط لدينا، لذلك لن أحاول محاربة الضرورة اللغوية هنا.ومع ذلك، سأطلق عليها بشكل عام اسم "regexes" (أو "regexen"، عندما أكون في مزاج أنجلوسكسوني).

وهنا أ مشاركة مدونة بواسطة أحد مطوري PHP الأساسيين:

وبما أن المقال طويل جدًا، فإليك ملخصًا للنقاط الرئيسية:

  • إن "التعبيرات العادية" التي يستخدمها المبرمجون ليس لديها الكثير من القواسم المشتركة مع المفهوم الأصلي للانتظام في سياق نظرية اللغة الرسمية.
  • يمكن أن تتوافق التعبيرات العادية (على الأقل PCRE) مع جميع اللغات الخالية من السياق.على هذا النحو، يمكنها أيضًا مطابقة HTML جيدة التصميم وجميع لغات البرمجة الأخرى تقريبًا.
  • يمكن أن تتطابق التعبيرات العادية مع بعض اللغات الحساسة للسياق على الأقل.
  • مطابقة التعبيرات العادية هي NP كاملة.على هذا النحو، يمكنك حل أي مشكلة NP أخرى باستخدام التعبيرات العادية.

ومع ذلك، يمكنك مطابقة المتناظرات مع التعابير المنطقية باستخدام هذا:

^(?'letter'[a-z])+[a-z]?(?:\k'letter'(?'-letter'))+(?(letter)(?!))$

... والذي من الواضح أنه لا علاقة له بالقواعد النحوية العادية.
مزيد من المعلومات هنا: http://www.regular-expressions.info/balancing.html

يمكن القيام بذلك في بيرل الآن.باستخدام مرجع العودي:

if($istr =~ /^((\w)(?1)\g{-1}|\w?)$/){
    print $istr," is palindrome\n";
}

تم تعديله بناءً على الجزء الأخير القريب http://perldoc.perl.org/perlretut.html

في روبي يمكنك استخدام مجموعات القبض على اسمه. ذلك شيء من هذا القبيل سوف تعمل -

def palindrome?(string)
  $1 if string =~ /\A(?<p>| \w | (?: (?<l>\w) \g<p> \k<l+0> ))\z/x
end

وتحاول ذلك، وأنها تعمل ...

1.9.2p290 :017 > palindrome?("racecar")
 => "racecar" 
1.9.2p290 :018 > palindrome?("kayak")
 => "kayak" 
1.9.2p290 :019 > palindrome?("woahitworks!")
 => nil 

وهنا جوابي ل Regex Golf المستوى الخامس (رجل، خطة).إنه يعمل لما يصل إلى 7 أحرف باستخدام Regexp للمتصفح (أنا أستخدم Chrome 36.0.1985.143).

^(.)(.)(?:(.).?\3?)?\2\1$

إليك واحدة تصل إلى 9 أحرف

^(.)(.)(?:(.)(?:(.).?\4?)?\3?)?\2\1$

لزيادة الحد الأقصى لعدد الأحرف التي ستعمل من أجلها، عليك استبدالها بشكل متكرر .? مع (؟:(.).؟\ن؟)؟.

/\A(?<a>|.|(?:(?<b>.)\g<a>\k<b+0>))\z/

ويصح لمحرك Oniguruma (الذي يستخدم في روبي)

عملي رف الكتب

وانها في الواقع أسهل أن تفعل ذلك مع سلسلة التلاعب بدلا من التعابير العادية:

bool isPalindrome(String s1)

{

    String s2 = s1.reverse;

    return s2 == s1;
}

وأنا أدرك هذا لا يجيب حقا سؤال في مقابلة، ولكن هل يمكن استخدامها لاظهار كيف كنت تعرف طريقة أفضل للقيام مهمة، وأنك لست "شخص نموذجي بمطرقة، الذي يرى كل مشكلة كما مسمار ".

في بيرل (انظر أيضا <لأ href = "https://stackoverflow.com/questions/233243/how-to-check-that-a-string-is-a-palindrome-using-regular-expressions#235199 "> زولت Botykai في الإجابة ):

$re = qr/
  .                 # single letter is a palindrome
  |
  (.)               # first letter
  (??{ $re })??     # apply recursivly (not interpolated yet)
  \1                # last letter
/x;

while(<>) {
    chomp;
    say if /^$re$/; # print palindromes
}

وفيما يتعلق التعبير PCRE (من MizardX):

و/ ^ (() (1) \ 2 |.؟.؟) $ /

هل اختبرت ذلك؟ على بلدي PHP 5.3 تحت وين إكس بي برو أنه فشل في: aaaba في الواقع، لقد عدلت التعبير التعبير قليلا، على النحو التالي:

و/ ^ (() (1) * \ 2 |.؟.؟) $ /

وأعتقد أن ما يحدث هو أنه في حين ترتكز الزوج الخارجي من الشخصيات، منها الداخلية المتبقية ليست كذلك. هذه ليست هي الحل الكامل لأنه في حين أنه يمر بشكل غير صحيح على "aaaba" و "aabaacaa"، لم تفشل بشكل صحيح على "aabaaca".

وأتساءل عما إذا كان هناك إصلاح لهذا، وأيضا، هل سبيل المثال بيرل (عن طريق سيباستيان JF / زولت) اجتياز اختبارات بلدي بشكل صحيح؟

وتشابا غابور من فيينا

يمكن للتعبيرات العادية العودية أن تفعل ذلك!

خوارزمية بسيطة جدًا وبديهية لاكتشاف سلسلة تحتوي على متناظر:

   (\w)(?:(?R)|\w?)\1

في rexegg.com/regex-recursion يشرح البرنامج التعليمي كيف يعمل.


إنه يعمل بشكل جيد مع أي لغة، هنا مثال مقتبس من نفس المصدر (الرابط) كإثبات للمفهوم، باستخدام PHP:

$subjects=['dont','o','oo','kook','book','paper','kayak','okonoko','aaaaa','bbbb'];
$pattern='/(\w)(?:(?R)|\w?)\1/';
foreach ($subjects as $sub) {
  echo $sub." ".str_repeat('-',15-strlen($sub))."-> ";
  if (preg_match($pattern,$sub,$m)) 
      echo $m[0].(($m[0]==$sub)? "! a palindrome!\n": "\n");
  else 
      echo "sorry, no match\n";
}

النواتج

dont ------------> sorry, no match
o ---------------> sorry, no match
oo --------------> oo! a palindrome!
kook ------------> kook! a palindrome!
book ------------> oo
paper -----------> pap
kayak -----------> kayak! a palindrome!
okonoko ---------> okonoko! a palindrome!
aaaaa -----------> aaaaa! a palindrome!
bbbb ------------> bbb

مقارنة

التعبير العادي ^((\w)(?:(?1)|\w?)\2)$ قم بنفس المهمة، ولكن كنعم/لا بدلاً من "يحتوي على".
ملاحظة:إنه يستخدم تعريفًا حيث "o" ليس ترسًا، والتنسيق الواصل "able-elba" ليس متناظرًا، ولكن "ableelba" كذلك.تسميتها تعريف1.
عندما يكون "o" و"able-elba" عبارة عن Palindrones، فإن التسمية تعريف2.

بالمقارنة مع "التعبيرات التقليدية المتناظرة" الأخرى،

  • ^((.)(?:(?1)|.?)\2)$ التعبير الأساسي أعلاه بدون \w التقييد، وقبول "قادرة إلبا".

  • ^((.)(?1)?\2|.)$ (@ليلديفيل) يستخدم تعريف2 (يقبل "o" و"able-elba" ويختلف أيضًا في التعرف على السلاسل "aaaaa" و"bbbb").

  • ^((.)(?1)\2|.?)$ (@ماركوس) لم يتم اكتشاف "kook" ولا "bbbb"

  • ^((.)(?1)*\2|.?)$ (@Csaba) يستخدم تعريف2.


ملحوظة:للمقارنة يمكنك إضافة المزيد من الكلمات في $subjects وخط لكل تعبير عادي مقارن،

  if (preg_match('/^((.)(?:(?1)|.?)\2)$/',$sub)) echo " ...reg_base($sub)!\n";
  if (preg_match('/^((.)(?1)?\2|.)$/',$sub)) echo " ...reg2($sub)!\n";
  if (preg_match('/^((.)(?1)\2|.?)$/',$sub)) echo " ...reg3($sub)!\n";
  if (preg_match('/^((.)(?1)*\2|.?)$/',$sub)) echo " ...reg4($sub)!\n";

وكما أشار <وأ href = "https://stackoverflow.com/questions/233243/how-to-check-that-a-string-is-a-palindrome-using-regular-expressions#233291" > ZCHudson أو تحديد ما إذا كان شيء هو سياق متناظر لا يمكن القيام به مع التعبير العادي المعتاد، حيث أن مجموعة من سياق متناظر ليست لغة العادية.

وأنا أختلف تماما مع <لأ href = "https://stackoverflow.com/questions/233243/how-to-check-that-a-string-is-a-palindrome-using-regular-expressions#233296" > Airsource المحدودة عندما يقول ان "انها ليست المتوقعون" ليس هذا النوع من الجواب هو يبحث عن المقابلة. وخلال المقابلة التي أجريتها، لقد جئت إلى هذا النوع من الأسئلة عندما يواجه مرشحا جيدا، للتحقق مما إذا كان يمكن العثور على حجة على حق عندما اقترحنا منه أن يفعل شيئا خاطئا. أنا لا ترغب في استئجار شخص سوف تحاول أن تفعل شيئا في الطريق الخطأ إذا كان يعرف أكثر واحد.

وشيء يمكنك القيام به مع بيرل: http://www.perlmonks.org/؟node_id= 577368

وأود أن أشرح المقابلة أن اللغة تتكون من قلب مستو ليست لغة عادية لكن بدلا خالية من السياق.

والتعبير العادي التي من شأنها أن تتطابق مع كل قلب مستو سيكون <م> انهائي . بدلا من ذلك أود أن أقترح أنه تقييد نفسه إما إلى الحد الأقصى لحجم قلب مستو لقبول. أو إذا كانت هناك حاجة كل قلب مستو استخدامها في الحد الأدنى نوع من NDPA، أو مجرد استخدام سلسلة انعكاس بسيط / يساوي التقنية.

وأفضل يمكنك القيام به مع regexes، قبل نفاد الجماعات التقاط:

/(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?).?\9\8\7\6\5\4\3\2\1/

وهذا سوف تطابق كل قلب مستو تصل إلى 19 حرفا.

وProgramatcally حل لجميع أطوال تافهة:

str == str.reverse ? true : false

وليس لدي مندوب التعليق مضمنة بعد، ولكن التعبير المعتاد تقدمها MizardX، وتعديلها من قبل تشابا، يمكن تعديلها مزيد من العمل على انجاحه في PCRE. فشل الوحيد الذي وجدت هو سلسلة شار واحد، ولكن يمكنني اختبار لذلك بشكل منفصل.

و/^((.)(?1)?\2|.)$/

إذا كنت يمكن أن تجعل من الفشل على أي خيوط أخرى، يرجى التعليق.

#!/usr/bin/perl

use strict;
use warnings;

print "Enter your string: ";
chop(my $a = scalar(<STDIN>));    
my $m = (length($a)+1)/2;
if( (length($a) % 2 != 0 ) or length($a) > 1 ) { 
  my $r; 
  foreach (0 ..($m - 2)){
    $r .= "(.)";
  }
  $r .= ".?";
  foreach ( my $i = ($m-1); $i > 0; $i-- ) { 
    $r .= "\\$i";
  } 
  if ( $a =~ /(.)(.).\2\1/ ){
    print "$a is a palindrome\n";
  }
  else {
    print "$a not a palindrome\n";
 }
exit(1);
}
print "$a not a palindrome\n";

ومن نظرية الآلي ومن المستحيل أن تتطابق مع paliandrome من أي طول (لأن ذلك يتطلب كمية لا حصر له من الذاكرة). ولكن من الممكن لمباراة Paliandromes من طول ثابت. أقول لها ممكن لكتابة التعابير المنطقية التي تتوافق مع جميع paliandromes من طول <= 5 أو <= 6 الخ، ولكن لا> = 5 الخ حيث الحد الأعلى من غير الواضح

في روبي يمكنك استخدام \b(?'word'(?'letter'[a-z])\g'word'\k'letter+0'|[a-z])\b لمطابقة الكلمات سياق متناظر مثل a, dad, radar, racecar, and redivider. ملاحظة: هذه التعابير المنطقية مباريات فقط الكلمات سياق متناظر أن هناك عدد فردي من رسائل طويلة

.

ودعونا نرى كيف أن هذا يتطابق التعبير العادي مع الرادار. مباريات كلمة حدود \ ب في بداية السلسلة. محرك رجإكس يدخل مجموعة اسر "كلمة". [أ-ي] مباريات ص التي يتم بعد ذلك تخزينها في كومة ل "الرسالة" مجموعة اسر في مستوى العودية الصفر. الآن محرك رجإكس يدخل الإعادة الأولى للفريق "كلمة". (؟ "الرسالة" [أ-ي]) مباريات ويلتقط على مستوى العودية واحد. التعبير المعتاد يدخل الإعادة الثانية للفريق "كلمة". (؟ "الرسالة" [أ-ي]) يلتقط د على مستوى العودية اثنين. خلال recursions المقبلين، مجموعة يلتقط و r في المستويات الثلاثة والأربعة. فشل العودية الخامسة بسبب عدم وجود شخصيات تركت في سلسلة ل[أ-ي] للمباراة. المحرك باستخدام التعابير المنطقية يجب التراجع.

ويجب على المحرك باستخدام التعابير المنطقية الآن محاولة البديل الثاني داخل الجماعة "كلمة". ثاني [أ-ي] في التعبير العادي يطابق ص النهائي في السلسلة. المحرك يخرج الآن من العودية ناجحة، سوف مستوى واحد ما يصل الى العودية الثالثة.

وبعد مطابقة (وكلمة) المحرك يصل \ k'letter + 0 '. فشل backreference لأن محرك رجإكس قد وصلت بالفعل نهاية السلسلة الموضوع. لذلك تتراجع مرة أخرى. البديل الثاني الآن مباريات أ. محرك رجإكس يخرج من العودية الثالثة.

لقد مطابقة المحرك باستخدام التعابير المنطقية مرة أخرى (وكلمة) ويحتاج لمحاولة backreference مرة أخرى. يحدد backreference +0 أو على المستوى الحالي من العودية، الذي هو 2. وعلى هذا المستوى، يقابل مجموعة اسر د. فشل backreference لأن الحرف التالي في السلسلة ص. التراجع مرة أخرى، والبديل الثاني مباريات د.

والآن، \ k'letter + 0 "يطابق ثانية في السلسلة. ذلك لأن المحرك باستخدام التعابير المنطقية وصلت إلى الوراء في العودية أول خلالها مطابقة مجموعة التقاط أول أ. المحرك باستخدام التعابير المنطقية مخارج العودية الأولى.

والمحرك باستخدام التعابير المنطقية هي الآن مرة أخرى خارج كل العودية. أن هذا المستوى، مجموعة اسر المخزنة ص. يمكن للbackreference الآن تطابق ص النهائي في السلسلة. منذ محرك ليس داخل أي العودية أي أكثر من ذلك، العائدات مع ما تبقى من التعبير المعتاد بعد المجموعة. \ ب مباريات في نهاية السلسلة. يتم الوصول إلى نهاية التعبير العادي ويتم إرجاع الرادار المباراة بشكل عام.

وهنا هو شفرة PL / SQL الذي يحكي سواء سلسلة معينة هو سياق متناظر أو لا تستخدم التعابير العادية:

create or replace procedure palin_test(palin in varchar2) is
 tmp varchar2(100);
 i number := 0;
 BEGIN
 tmp := palin;
 for i in 1 .. length(palin)/2 loop
  if length(tmp) > 1 then  
    if regexp_like(tmp,'^(^.).*(\1)$') = true then 
      tmp := substr(palin,i+1,length(tmp)-2);
    else 
      dbms_output.put_line('not a palindrome');
      exit;
    end if;
  end if;  
  if i >= length(palin)/2 then 
   dbms_output.put_line('Yes ! it is a palindrome');
  end if;
 end loop;  
end palin_test;

ويمكنك أيضا أن تفعل ذلك دون استخدام العودية:

\A(?:(.)(?=.*?(\1\2?)\z))*?.?\2\z

وأو استبعاد سلسلة فارغة:

\A(?=.)(?:(.)(?=.*?(\1\2?)\z))*?.?\2\z

ويعمل مع بيرل، PCRE، روبي، جافا

تجريبي

وصقل طفيف في طريقة Airsource المحدودة، وفي شبة الكود:

WHILE string.length > 1
    IF /(.)(.*)\1/ matches string
        string = \2
    ELSE
        REJECT
ACCEPT

وبلدي $ بال = 'المالايالامية'؛

while($pal=~/((.)(.*)\2)/){                                 #checking palindrome word
    $pal=$3;
}
if ($pal=~/^.?$/i){                                         #matches single letter or no letter
    print"palindrome\n";
}
else{
    print"not palindrome\n";
}

و\b([a-z])?([a-z])?([a-z])?\2\1\b/gi

والمباريات 5 إلكتروني قلب مستو مثل الرجوع وقوارب الكاياك. وهي تفعل ذلك باستخدام (غير الجشع) مطابقة أي الرسائل الثلاث، تليها الحروف 2 و 1 مطابقة.

لينك لregex101 الموقع باستخدام هذا

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top