أحدث إصدار: 1.0.0 بيتا
مكتبة PHP 5.4+ لتمثيل علاقات التكافؤ واستراتيجيات تجزئة القيم وفرزها.
تعتبر علاقات التكافؤ مفيدة في إنشاء طريقة عامة لمقارنة القيم فيما يتعلق بالمتطلبات الخاصة بالمجال، بالإضافة إلى تمثيل معايير مخصصة لمقارنة القيم في سياقات محدودة، خاصة للاستخدام في المجموعات.
تغطي هذه المكتبة أيضًا الإمكانات التكميلية، مثل التجزئة والفرز، مما يجعلها إضافة قيمة إلى حزام أدوات التطوير لديك.
تم توثيق واجهة برمجة التطبيقات (API) على نطاق واسع في الكود المصدري. بالإضافة إلى ذلك، يتوفر أيضًا إصدار HTML لعرض أكثر ملاءمة في المتصفح.
استخدم Composer لتثبيت الحزمة:
$ composer require phpcommon/comparison
العلاقة هي أداة رياضية لوصف الارتباطات بين عناصر المجموعات. تستخدم العلاقات على نطاق واسع في علوم الكمبيوتر، وخاصة في قواعد البيانات وتطبيقات الجدولة.
على عكس معظم اللغات الحديثة، لا تدعم PHP التحميل الزائد للمشغل، والذي تم تجنبه تاريخيًا كخيار تصميم. بمعنى آخر، ليس من الممكن تجاوز السلوك الافتراضي للمشغلين الأصليين، مثل يساوي، متطابق، أكبر من، أقل من، وما إلى ذلك. على سبيل المثال، توفر Java الواجهة القابلة للمقارنة، بينما توفر Python بعض الطرق السحرية.
وتتجلى أهمية هذا المفهوم أكثر في المواقف التي يختلف فيها مفهوم التكافؤ أو الترتيب باختلاف موضوع المقارنة أو حسب السياق، كما سنناقش في الأقسام التالية.
في الرياضيات، علاقة التكافؤ هي علاقة ثنائية انعكاسية ومتماثلة ومتعدية . ولكن في مجال الحوسبة هناك خاصية أخرى يجب أن تؤخذ بعين الاعتبار: الاتساق . الاتساق يعني أن العلاقة لا ينبغي أن تنتج نتائج مختلفة لنفس المدخلات.
علاقة التكافؤ في كل مكان هي علاقة المساواة بين عناصر أي مجموعة. تشمل الأمثلة الأخرى ما يلي:
ولأغراض هذه المكتبة، يمكن أن تكون علاقة التكافؤ عامة أو خاصة بالنوع. يتم تعريف العلاقات الخاصة بالنوع من خلال تنفيذ واجهات Equatable
أو Equivalence
، في حين يجب أن تقوم المعادلات العامة بتنفيذ الواجهة الأخيرة.
تحدد الواجهة Equatable
طريقة معممة يطبقها الفصل لإنشاء طريقة خاصة بالنوع لتحديد المساواة في الحالات.
للتوضيح، يعتبر فئة Money
، والتي تهدف إلى تمثيل القيم النقدية. تعتبر هذه الفئة مرشحة جيدة لتنفيذ الواجهة Equatable
، لأن Money
هو كائن ذو قيمة، أي أن فكرة المساواة بين تلك الكائنات لا تعتمد على الهوية. بدلاً من ذلك، يكون مثيلان من Money
متساويين إذا كان لهما نفس القيم. وبالتالي، في حين أن Money::USD(5) === Money::USD(5)
تُرجع false
، Money::USD(5)->equals(Money::USD(5))
تُرجع true
.
وهنا الطبقة المذكورة سابقا:
final class Money implements Equatable
{
private $ amount ;
private $ currency ;
public function __construct ( $ amount , $ currency )
{
$ this -> amount = ( int ) $ amount ;
$ this -> currency = ( string ) $ currency ;
}
public function equals ( Equatable $ other )
{
if (! $ other instanceof self) {
return false ;
}
return $ this -> amount === $ other -> amount && $ this -> currency === $ other -> currency ;
}
}
ومع ذلك، هناك العديد من الحالات التي يصبح فيها وجود طريقة غير قياسية أو خارجية لمقارنة قيمتين ضروريًا. ربما تكون حالة الاستخدام الأكثر وضوحًا لتلك العلاقات المخصصة هي الاستخدام مع المجموعات، ولكنها مفيدة أيضًا في توفير تلك الإمكانات للقيم العددية أو فئة موجودة لا يمكنها توفيرها بنفسها، لأنها تنتمي إلى حزمة جهة خارجية أو تم إنشاؤها إلى PHP.
لنفترض أنك تقوم بتطوير برنامج لمساعدة المستشفيات على إدارة عمليات التبرع بالدم. ينص أحد المتطلبات على أن الممرضة لا يمكنها جمع الدم من المتبرعين الذين لديهم نفس فصيلة الدم. ستبدو العلاقة لهذا السيناريو كما يلي:
use PhpCommon Comparison Equivalence ;
class BloodGroupEquivalence implements Equivalence
{
public function equals ( Equatable $ other )
{
return get_class ( $ other ) === static ::class;
}
public function equivalent ( $ left , $ right )
{
if (! $ left instanceof Person) {
UnexpectedTypeException:: forType (Person::class, $ left );
}
if (! $ right instanceof Person) {
return false ;
}
return $ left -> getBloodType () === $ right -> getBloodType ();
}
}
تحدد هذه العلاقة ما إذا كان شخصان من نفس فصيلة الدم:
$ equivalence = new BloodGroupEquivalence ();
$ donors = new BloodDonors ( $ equivalence );
$ james = new Person ( ' James ' , ' A ' );
$ john = new Person ( ' John ' , ' A ' );
// James and John are considered equivalent once they are of the same blood group
var_dump ( $ equivalence -> equivalent ( $ james , $ john )); // Outputs bool(true)
// Initially, none of them are present in the collection
var_dump ( $ volunteers -> contains ( $ james )); // Outputs bool(false)
var_dump ( $ volunteers -> contains ( $ john )); // Outputs bool(false)
// Add James to the set of volunteers
$ donors -> add ( $ james );
// Now, considering only the blood group of each donor for equality, both of
// them are considered present in the collection
$ donors -> contains ( $ james ); // Outputs bool(true)
$ donors -> contains ( $ john ); // Outputs bool(true)
نظرًا لأن BloodGroupEquivalence
ينشئ علاقة تكافؤ بين الأشخاص بناءً على فصيلة دمهم، فسيتم تجاهل أي محاولة لإضافة جون إلى المجموعة، لأن جيمس موجود بالفعل وهم من نفس فصيلة الدم.
قد يبدو الأمر معقدًا بعض الشيء بالنسبة لمطلب بسيط في البداية، ولكن في الحالات الحقيقية يمكن استخدامه لمقارنة التكافؤ بين فصائل الدم المتوافقة، من أجل تقسيم المتبرعين إلى مجموعات.
توفر هذه المكتبة بعض علاقات التكافؤ العامة كجزء من المكتبة القياسية، كما هو موضح أدناه.
يقارن قيمتين للهوية.
تعتمد هذه العلاقة على نفس العامل. في معظم الحالات، تعتبر القيمتان متكافئتين إذا كان لهما نفس النوع والقيمة، ولكن هناك بعض الاستثناءات:
NAN
لا تساوي أي قيمة أخرى، بما في ذلك نفسها.يلخص الجدول التالي كيفية مقارنة المعاملات من الأنواع المختلفة:
$A $B | باطل | منطقية | عدد صحيح | يطفو | خيط | الموارد | صفيف | هدف |
---|---|---|---|---|---|---|---|---|
باطل | true | false | false | false | false | false | false | false |
منطقية | false | $A === $B | false | false | false | false | false | false |
عدد صحيح | false | false | $A === $B | false | false | false | false | false |
يطفو | false | false | false | $A === $B | false | false | false | false |
خيط | false | false | false | false | $A === $B | false | false | false |
الموارد | false | false | false | false | false | $A === $B | false | false |
صفيف | false | false | false | false | false | false | $A === $B | false |
هدف | false | false | false | false | false | false | false | $A === $B |
يقارن بين قيمتين للمساواة.
يتصرف تكافؤ القيمة تمامًا مثل تكافؤ الهوية، باستثناء أنه يفوض المقارنة بين الكائنات Equatable
إلى الكائنات التي تتم مقارنتها. بالإضافة إلى ذلك، يمكن تحديد العلاقات الخارجية لمقارنة قيم من نوع معين. يكون ذلك مفيدًا في الحالات التي يكون فيها من المرغوب فيه تجاوز السلوك الافتراضي لنوع معين، مع الاحتفاظ بجميع السلوكيات الأخرى. وهو مفيد أيضًا في تحديد العلاقة بين كائنات الفئات التي تنتمي إلى حزم الجهات الخارجية أو المضمنة في PHP.
يتم استخدام القواعد التالية لتحديد ما إذا كانت القيمتان تعتبران متكافئتين:
NAN
لا تساوي أي قيمة أخرى، بما في ذلك نفسها.Equatable
ويتم تقييم التعبير $left->equals($right)
إلى true
.$relation->equivalent($left, $right)
إلى true
.يلخص الجدول التالي كيفية مقارنة المعاملات من الأنواع المختلفة:
$A $B | باطل | منطقية | عدد صحيح | يطفو | خيط | الموارد | صفيف | هدف | متساوي |
---|---|---|---|---|---|---|---|---|---|
باطل | true | false | false | false | false | false | false | false | false |
منطقية | false | $A === $B | false | false | false | false | false | false | false |
عدد صحيح | false | false | $A === $B | false | false | false | false | false | false |
يطفو | false | false | false | $A === $B | false | false | false | false | false |
خيط | false | false | false | false | $A === $B | false | false | false | false |
الموارد | false | false | false | false | false | $A === $B | false | false | false |
صفيف | false | false | false | false | false | false | eq($A, $B) | false | false |
هدف | false | false | false | false | false | false | false | $A === $B | false |
متساوي | false | false | false | false | false | false | false | false | $A‑>equals($B) |
حيث تشير eq()
إلى دالة تقارن كل زوج من الإدخالات المقابلة بشكل متكرر، وفقًا للقواعد الموضحة أعلاه.
توفر هذه العلاقة أيضًا طريقة لتجاوز منطق التكافؤ لفئة معينة دون الحاجة إلى إنشاء علاقة جديدة. على سبيل المثال، لنفترض أنك تريد مقارنة مثيلات DateTime
استنادًا إلى قيمها، مع الاحتفاظ بالسلوك الافتراضي للأنواع الأخرى. يمكن تحقيق ذلك عن طريق تحديد علاقة مخصصة لاستخدامها عند مقارنة مثيل DateTime
بقيمة أخرى:
use PhpCommon Comparison Hasher ValueHasher as ValueEquivalence ;
use PhpCommon Comparison Hasher DateTimeHasher as DateTimeEquivalence ;
use DateTime ;
$ relation = new ValueEquivalence ([
DateTime::class => new DateTimeEquivalence ()
]);
$ date = ' 2017-01-01 ' ;
$ timezone = new DateTimeZone ( ' Pacific/Nauru ' );
$ left = new DateTime ( $ date , $ timezone );
$ right = new DateTime ( $ date , $ timezone );
// Outputs bool(true)
var_dump ( $ relation -> equivalent ( $ left , $ right ));
يقارن بين قيمتين للمساواة الدلالية.
ومن المقرر التكافؤ الدلالي للإصدارات المستقبلية. سيسمح بمقارنة القيم التي تبدو متشابهة لغويًا - حتى لو كانت من أنواع مختلفة. إنه مشابه لكيفية عمل المقارنة الفضفاضة في PHP، ولكن في ظل ظروف أكثر تقييدًا، بطريقة تحافظ على خصائص الانعكاسية والتماثل والعبور.
يقارن مثيلين DateTime
بناءً على التاريخ والوقت والمنطقة الزمنية الخاصة بهما.
تعتبر هذه العلاقة أن مثيلين DateTime
متكافئان إذا كان لهما نفس التاريخ والوقت والمنطقة الزمنية:
use PhpCommon Comparison Hasher IdentityHasher as IdentityEquivalence ;
use PhpCommon Comparison Hasher DateTimeHasher as DateTimeEquivalence ;
use DateTime ;
$ identity = new IdentityEquivalence ();
$ value = new DateTimeEquivalence ();
$ date = ' 2017-01-01 ' ;
$ timezone = new DateTimeZone ( ' Pacific/Nauru ' );
$ left = new DateTime ( $ date , $ timezone );
$ right = new DateTime ( $ date , $ timezone );
// Outputs bool(false)
var_dump ( $ identity -> equivalent ( $ left , $ right ));
// Outputs bool(true)
var_dump ( $ value -> equivalent ( $ left , $ right ));
في PHP، يمكن تمثيل مفاتيح المصفوفة فقط كأرقام وسلاسل. ومع ذلك، هناك العديد من الحالات التي يكون فيها تخزين الأنواع المعقدة كمفاتيح مفيدًا. خذ على سبيل المثال الفئات التي تمثل أنواعًا مختلفة من الأرقام أو السلاسل، مثل كائنات GMP، وسلاسل Unicode، وما إلى ذلك. سيكون من الملائم أن تكون قادرًا على استخدام مثل هذه الكائنات كمفاتيح المصفوفة أيضًا.
لسد هذه الفجوة، تقدم هذه المكتبة الواجهات Hashable
و Hasher
، التي تحدد بروتوكولًا لتوفير رموز التجزئة للقيم. لا تتطلب هذه الواجهات من المنفذين توفير وظائف تجزئة مثالية. أي أن القيمتين غير المتكافئتين قد يكون لهما نفس رمز التجزئة. ومع ذلك، لتحديد ما إذا كانت القيمتان اللتان لهما نفس رمز التجزئة متساويتين في الواقع، يجب دمج مفهومي التجزئة والتكافؤ بطريقة تكاملية. وهذا ما يفسر سبب قيام Hasher
و Hashable
بتوسيع Equivalence
Equatable
التوالي.
كلمة تحذير
تم تصميم رمز التجزئة للإدراج والبحث بشكل فعال في المجموعات التي تعتمد على جدول التجزئة وللتحقق السريع من عدم المساواة. رمز التجزئة ليس قيمة دائمة. لهذا السبب:
- لا تقم بإجراء تسلسل لقيم رمز التجزئة أو تخزينها في قواعد البيانات.
- لا تستخدم رمز التجزئة كمفتاح لاسترداد كائن من مجموعة ذات مفاتيح.
- لا ترسل رموز التجزئة عبر مجالات التطبيق أو العمليات. في بعض الحالات، قد يتم حساب رموز التجزئة على أساس كل عملية أو لكل مجال تطبيق.
- لا تستخدم رمز التجزئة بدلاً من القيمة التي يتم إرجاعها بواسطة وظيفة تجزئة التشفير إذا كنت بحاجة إلى تجزئة قوية من الناحية التشفيرية.
- لا تختبر مساواة رموز التجزئة لتحديد ما إذا كان هناك كائنان متساويان، بمجرد أن تحتوي القيم غير المتساوية على رموز تجزئة متطابقة.
هناك حالات قد يكون من المرغوب فيها تحديد منطق تجزئة مخصص للفئة التي تناسب متطلباتك بشكل أفضل. على سبيل المثال، لنفترض أن لديك فئة Point لتمثيل نقطة ثنائية الأبعاد:
namespace PhpCommon Comparison Equatable ;
final class Point implements Equatable
{
private $ x ;
private $ y ;
public function __construct ( $ x , $ y )
{
$ this -> x = ( int ) $ x ;
$ this -> y = ( int ) $ y ;
}
public function equals ( Equatable $ point )
{
if (! $ point instanceof Point) {
return false ;
}
return $ this -> x === $ point -> x && $ this -> y === $ point -> y ;
}
}
Point
تحمل إحداثيات x وy للنقطة. وفقا لتعريف الفئة، تعتبر نقطتان متساويتين إذا كان لهما نفس الإحداثيات. ومع ذلك، إذا كنت تنوي تخزين مثيلات Point
في خريطة قائمة على التجزئة، على سبيل المثال، لأنك تريد ربط الإحداثيات بالتسميات، فيجب عليك التأكد من أن الفصل الدراسي الخاص بك ينتج رموز تجزئة متماسكة مع المنطق المستخدم لتحديد متى يتم تعتبر النقاط متساوية:
namespace PhpCommon Comparison Equatable ;
final class Point implements Hashable
{
private $ x ;
private $ y ;
public function __construct ( $ x , $ y )
{
$ this -> x = ( int ) $ x ;
$ this -> y = ( int ) $ y ;
}
public function equals ( Equatable $ point )
{
if (! $ point instanceof Point) {
return false ;
}
return $ this -> x === $ point -> x && $ this -> y === $ point -> y ;
}
public function getHash ()
{
return 37 * ( 31 + $ this -> $ x ) + $ this -> $ x ;
}
}
بهذه الطريقة، تعمل طريقة getHash()
وفقًا لطريقة equals()
، على الرغم من أن خوارزمية التجزئة قد لا تكون مثالية. إن تنفيذ الخوارزميات الفعالة لإنشاء أكواد التجزئة يقع خارج نطاق هذا الدليل. ومع ذلك، يوصى باستخدام خوارزمية سريعة تنتج نتائج مختلفة بشكل معقول للقيم غير المتساوية، وتحويل منطق المقارنة الثقيل إلى Equatable::equals()
.
لاحظ أن الكائنات القابلة للتجزئة يجب أن تكون غير قابلة للتغيير، أو تحتاج إلى ممارسة الانضباط في عدم تغييرها بعد استخدامها في الهياكل القائمة على التجزئة.
يوفر Hasher وظيفة التجزئة للأنواع البدائية وكائنات الفئات التي لا تنفذ Hashable
.
تهدف طريقة hash()
التي تقدمها هذه الواجهة إلى توفير وسيلة لإجراء عمليات فحص عدم التكافؤ السريعة والإدراج والبحث الفعال في هياكل البيانات القائمة على التجزئة. هذه الطريقة دائمًا متماسكة مع equivalent()
، مما يعني أنه بالنسبة لأي مراجع $x
و $y
، إذا كانت equivalent($x, $y)
، ثم hash($x) === hash($y)
. ومع ذلك، إذا تم تقييم equivalence($x, $y)
إلى false
، فقد يظل hash($x) === hash($y)
صحيحًا. ولهذا السبب فإن طريقة hash()
مناسبة للتحقق من عدم التكافؤ ، ولكن ليس للتحقق من التكافؤ .
توفر جميع تطبيقات Equivalence
في هذه المكتبة أيضًا وظيفة التجزئة. يمكن العثور على مزيد من المعلومات حول كيفية تجزئة القيم في وثائق التنفيذ المعني.
باتباع نفس منطق المفاهيم التي تمت مناقشتها سابقًا، تعد Comparable
Comparator
واجهات لتوفير استراتيجيات الفرز الطبيعية والمخصصة، على التوالي. تحدد كلا الواجهتين علاقة ترتيب إجمالية، وهي علاقة انعكاسية وغير متماثلة ومتعدية .
تفرض هذه الواجهة ترتيبًا إجماليًا على كائنات كل فئة تقوم بتنفيذها. يُشار إلى هذا الترتيب بالترتيب الطبيعي للفئة، ويُشار إلى الطريقة Comparable::compareTo()
باسم طريقة المقارنة الطبيعية الخاصة بها.
يوضح المثال التالي كيف يمكن للفئة تحديد الترتيب الطبيعي لمثيلاتها:
use PhpCommon Comparison UnexpectedTypeException ;
final class BigInteger implements Comparable
{
private $ value ;
public function __construct ( $ value )
{
$ this -> value = ( string ) $ value ;
}
public function compareTo ( Comparable $ other )
{
if (! $ other instanceof self) {
throw UnexpectedTypeException:: forType (BigInteger::class, $ other );
}
return bccomp ( $ this -> value , $ other -> value );
}
}
الغرض من Comparator
هو السماح لك بتحديد واحدة أو أكثر من استراتيجيات المقارنة التي ليست استراتيجية المقارنة الطبيعية للفئة. من الناحية المثالية، يجب تنفيذ Comparator
بواسطة فئة مختلفة عن تلك التي تحدد استراتيجية المقارنة لها. إذا كنت تريد تحديد استراتيجية مقارنة طبيعية لفئة ما، فيمكنك تنفيذ Comparable
بدلاً من ذلك.
يمكن تمرير المقارنات إلى طريقة فرز المجموعة للسماح بالتحكم الدقيق في ترتيب الفرز الخاص بها. ويمكن استخدامه أيضًا للتحكم في ترتيب هياكل بيانات معينة، مثل المجموعات المصنفة أو الخرائط المصنفة. على سبيل المثال، ضع في اعتبارك المقارنة التالية التي تقوم بترتيب السلاسل وفقًا لطولها:
use PhpCommon Comparison Comparator ;
class StringLengthComparator implements Comparator
{
public function compare ( $ left , $ right )
{
return strlen ( $ left ) <=> strlen ( $ right );
}
}
$ comparator = new StringLengthComparator ();
// Outputs int(-1)
var_dump ( $ comparator -> compare ( ' ab ' , ' a ' ));
يمثل هذا التنفيذ إحدى الطرق العديدة الممكنة لفرز السلاسل. تتضمن الاستراتيجيات الأخرى الفرز أبجديًا ومعجميًا وما إلى ذلك.
الرجاء مراجعة سجل التغيير لمزيد من المعلومات عما تغير مؤخرًا.
$ composer test
تحقق من وثائق الاختبار لمزيد من التفاصيل.
المساهمات في الحزمة هي موضع ترحيب دائما!
يرجى الاطلاع على المساهمة والسلوك للحصول على التفاصيل.
إذا اكتشفت أي مشكلات متعلقة بالأمان، فيرجى إرسال بريد إلكتروني إلى [email protected] بدلاً من استخدام أداة تعقب المشكلات.
جميع محتويات هذه الحزمة مرخصة بموجب ترخيص MIT.