الاتحاد المصنف

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

وصف

تُعدّ الاتحادات المُوسومة ذات أهمية بالغة في لغات البرمجة الوظيفية مثل ML و Haskell ، حيث تُسمى أنواع البيانات (انظر نوع البيانات الجبرية )، ويستطيع المُصرّف التحقق من معالجة جميع حالات الاتحاد المُوسوم، مما يُجنّب أنواعًا عديدة من الأخطاء. كما تُستخدم أنواع المجموع المُدقّق في وقت الترجمة على نطاق واسع في Rust ، حيث تُسمى enum . مع ذلك، يُمكن إنشاؤها في أي لغة برمجة تقريبًا ، وهي أكثر أمانًا من الاتحادات غير المُوسومة، والتي تُسمى غالبًا ببساطة الاتحادات، وهي مُشابهة لها ولكنها لا تُحدّد صراحةً أي عضو من الاتحاد قيد الاستخدام حاليًا.

غالباً ما تُصاحب الاتحادات المُوسومة مفهوم المُنشئ ، وهو مُشابه للمُنشئ الخاص بالفئة ولكنه ليس نفسه . المُنشئ هو دالة أو تعبير يُنتج قيمة من نوع الاتحاد المُوسوم، مُعطىً وسماً وقيمة من النوع المُقابل.

رياضيًا، تتوافق الاتحادات الموسومة مع الاتحادات المنفصلة أو المميزة ، والتي تُكتب عادةً باستخدام +. بالنظر إلى عنصر من اتحاد منفصلأ+ب{\displaystyle A+B}، ومن الممكن تحديد ما إذا كان قد جاء منأ{\displaystyle A}أوب{\displaystyle B}إذا كان العنصر موجودًا في كليهما، فسيكون هناك نسختان مختلفتان فعليًا من القيمة فيأ+ب{\displaystyle A+B}واحد منأ{\displaystyle A}وواحد منب{\displaystyle B}.

في نظرية الأنواع ، يُطلق على الاتحاد المُوسوم اسم نوع المجموع . أنواع المجموع هي ثنائية أنواع الضرب . تختلف الرموز، ولكن عادةً ما يكون نوع المجموعأ+ب{\displaystyle A+B}يأتي مع شكلين تمهيديين ( حقن )حقن1:أأ+ب{\displaystyle {\texttt {inj}}_{1}:A\mapsto A+B}وحقن2:بأ+ب{\displaystyle {\texttt {inj}}_{2}:B\mapsto A+B}يُعدّ تحليل الحالة شكلاً من أشكال الاستبعاد، ويُعرف باسم مطابقة الأنماط في لغات البرمجة المشابهة لـ ML : إذاهـ{\displaystyle e}له نوعأ+ب{\displaystyle A+B}وهـ1{\displaystyle e_{1}}وهـ2{\displaystyle e_{2}}نوعτ{\displaystyle \tau }بناءً على الافتراضاتx:أ{\displaystyle x:A}وy:ب{\displaystyle y:B}على التوالي، ثم المصطلحجأsهـ هـ oو xهـ1|yهـ2{\displaystyle {\mathsf {case}}\ e\ {\mathsf {of}}\ x\Rightarrow e_{1}\mid y\Rightarrow e_{2}}له نوعτ{\displaystyle \tau }يتوافق نوع المجموع مع الفصل المنطقي الحدسي في إطار تطابق كاري-هوارد .

يمكن اعتبار النوع المُعدّد حالةً مُنحلة: اتحاد مُوسوم لأنواع الوحدة . وهو يُقابل مجموعة من المُنشئات الفارغة، ويمكن تنفيذه كمتغير وسم بسيط، لأنه لا يحتوي على أي بيانات إضافية بخلاف قيمة الوسم.

يتم عادةً تنفيذ العديد من تقنيات البرمجة وهياكل البيانات، بما في ذلك rope و lazy evaluation و class hierarchy (انظر أدناه) و arbitrary-precision arithmatic و CDR coding و indirection bit وأنواع أخرى من المؤشرات الموسومة ، باستخدام نوع من الاتحاد الموسوم.

يمكن اعتبار الاتحاد الموسوم أبسط أنواع تنسيقات البيانات ذاتية الوصف . ويمكن اعتبار وسم الاتحاد الموسوم أبسط أنواع البيانات الوصفية .

في اللغات التي تعتمد على الكتابة الحساسة للتدفق ، يمكن تنفيذ الاتحادات الموسومة من خلال مزيج من أنواع الاتحاد وأنواع السجلات . [ 1 ]

المزايا والعيوب

تتمثل الميزة الأساسية للاتحاد المُوسَم على الاتحاد غير المُوسَم في أن جميع عمليات الوصول آمنة، بل ويمكن للمُصرِّف التحقق من معالجة جميع الحالات. أما الاتحادات غير المُوسَمة فتعتمد على منطق البرنامج لتحديد الحقل النشط حاليًا بشكل صحيح، مما قد يؤدي إلى سلوك غير متوقع وأخطاء يصعب اكتشافها في حال فشل هذا المنطق.

تتمثل الميزة الأساسية للاتحاد المُوسوم مقارنةً بالسجل البسيط الذي يحتوي على حقل لكل نوع في توفير مساحة التخزين من خلال تداخل مساحة التخزين لجميع الأنواع. بعض التطبيقات تُخصص مساحة تخزين كافية لأكبر نوع، بينما تُعدّل تطبيقات أخرى حجم قيمة الاتحاد المُوسوم ديناميكيًا حسب الحاجة. عندما تكون القيمة غير قابلة للتغيير ، يصبح من السهل تخصيص مساحة التخزين المطلوبة فقط.

تتمثل العيوب الرئيسية للاتحادات الموسومة في أن الوسم يشغل مساحة. ونظرًا لوجود عدد قليل من البدائل عادةً، يمكن ضغط الوسم في 2 أو 3 بتات كلما توفرت مساحة، ولكن في بعض الأحيان لا تتوفر حتى هذه البتات. في هذه الحالة، قد يكون من المفيد استخدام الوسوم المطوية أو المحسوبة أو المشفرة ، حيث تُحسب قيمة الوسم ديناميكيًا من محتويات حقل الاتحاد. ومن الأمثلة الشائعة استخدام القيم المحجوزة ، حيث قد تُرجع دالة تُعيد عددًا موجبًا القيمة -1 للإشارة إلى الفشل، وقيم المراقبة ، التي تُستخدم غالبًا في المؤشرات الموسومة .

أحيانًا، تُستخدم الاتحادات غير الموسومة لإجراء تحويلات على مستوى البت بين الأنواع، أو ما يُعرف بـ"تورية الأنواع" . أما الاتحادات الموسومة فليست مُخصصة لهذا الغرض؛ إذ عادةً ما تُعيّن قيمة جديدة كلما تغير الوسم.

تدعم العديد من لغات البرمجة، إلى حد ما، نوع البيانات الشامل ، وهو نوع يشمل جميع قيم جميع الأنواع الأخرى، وغالبًا ما تُوفّر طريقة لاختبار النوع الفعلي لقيمة من النوع الشامل. يُشار إلى هذه الأنواع أحيانًا باسم المتغيرات . في حين أن أنواع البيانات الشاملة تُشابه الاتحادات المُوسومة في تعريفها الرسمي، فإن الاتحادات المُوسومة النموذجية تتضمن عددًا قليلًا نسبيًا من الحالات، وتشكل هذه الحالات طرقًا مختلفة للتعبير عن مفهوم واحد متماسك، مثل عقدة بنية بيانات أو تعليمة. كما يُتوقع التعامل مع كل حالة ممكنة للاتحاد المُوسوم عند استخدامه. أما قيم نوع البيانات الشامل فهي غير مترابطة، ولا توجد طريقة عملية للتعامل معها جميعًا.

كما هو الحال مع أنواع الخيارات ومعالجة الاستثناءات ، تُستخدم الاتحادات الموسومة أحيانًا للتعامل مع حدوث نتائج استثنائية. غالبًا ما تُدمج هذه الوسوم في النوع كقيم محجوزة ، ولا يتم التحقق من حدوثها باستمرار: وهذا مصدر شائع لأخطاء البرمجة. يمكن صياغة هذا الاستخدام للاتحادات الموسومة كوحدة أحادية (monad) بالوظائف التالية:

يعود:أ(أ+هـ)=أقيمةأ{\displaystyle {\texttt {return}}\colon A\to \left(A+E\right)=a\mapsto {\text{value}}\,a}
ربط:(أ+هـ)(أ(ب+هـ))(ب+هـ)=أو{خطأهـلو أ=خطأهـوألو أ=قيمةأ{\displaystyle {\texttt {bind}}\colon \left(A+E\right)\to \left(A\to \left(B+E\right)\right)\to \left(B+E\right)=a\mapsto f\mapsto {\begin{cases}{\text{err}}\,e&{\text{if}}\ a={\text{err}}\,e\\f\,a'&{\text{if}}\ a={\text{value}}\,a'\end{cases}}}

أينقيمة{\displaystyle {\text{value}}}وخطأ{\displaystyle {\text{خطأ}}}هي مُنشئات نوع الاتحاد،أ{\displaystyle A}وب{\displaystyle B}أنواع النتائج الصالحة وهـ{\displaystyle E}هذا هو نوع من حالات الخطأ. أو بدلاً من ذلك، يمكن وصف نفس الموناد بواسطةيعود{\displaystyle {\texttt {return}}}ووظيفتان إضافيتان،fmap{\displaystyle {\texttt {fmap}}}وينضم{\displaystyle {\texttt {join}}}:

fmap:(أب)((أ+هـ)(ب+هـ))=وأ{خطأهـلو أ=خطأهـقيمة(وأ)لو أ=قيمةأ{\displaystyle {\texttt {fmap}}\colon (A\to B)\to \left(\left(A+E\right)\to \left(B+E\right)\right)=f\mapsto a\mapsto {\begin{cases}{\text{err}}\,e&{\text{if}}\ a={\text{err}}\,e\\{\text{value}}\,{\text{(}}\,f\,a'\,{\text{)}}&{\text{if}}\ a={\text{value}}\,a'\end{cases}}}
ينضم:((أ+هـ)+هـ)(أ+هـ)=أ{خطأهـلو أ=خطأهـخطأهـلو أ=قيمة(خطأ)هـ)قيمةألو أ=قيمة(قيمةأ){\displaystyle {\texttt {join}}\colon ((A+E)+E)\to (A+E)=a\mapsto {\begin{cases}{\text{err}}\,e&{\mbox{if}}\ a={\text{err}}\,e\\{\text{err}}\,e&{\text{if}}\ a={\text{value}}\,{\text{(err}}\,e\,{\text{)}}\\{\text{value}}\,a'&{\text{if}}\ a={\text{value}}\,{\text{(value}}\,a'\,{\text{)}}\end{cases}}}

أمثلة

لنفترض أننا نريد بناء شجرة ثنائية من الأعداد الصحيحة. في لغة التعلم الآلي، سنفعل ذلك عن طريق إنشاء نوع بيانات على النحو التالي:

نوع البيانات شجرة = ورقة | عقدة من ( عدد صحيح * شجرة * شجرة )

هذا اتحاد مُوسَم ذو حالتين: الأولى، وهي الورقة، تُستخدم لإنهاء مسار في الشجرة، وتعمل بشكل مشابه للقيمة الفارغة في لغات البرمجة الإجرائية. أما الفرع الآخر فيحتوي على عقدة، تتضمن عددًا صحيحًا وشجرة فرعية يسرى وأخرى يمنى. تُعدّ Leaf وNode دالتين بانيتين، تُمكّناننا من إنشاء شجرة مُحددة، مثل:

Node ( 5 , Node ( 1 , Leaf , Leaf ), Node ( 3 , Leaf , Node ( 4 , Leaf , Leaf )))

والذي يتوافق مع هذه الشجرة:

الشجرة التي تنتجها الدوال البانية المذكورة أعلاه

الآن يمكننا بسهولة كتابة دالة آمنة من حيث النوع، والتي على سبيل المثال، تحسب عدد العقد في الشجرة:

دالة countNodes ( Leaf ) = 0 | countNodes ( Node ( int , left , right )) = 1 + countNodes ( left ) + countNodes ( right )

الدعم اللغوي

ALGOL 68

في لغة ALGOL 68 ، تُسمى الاتحادات الموسومة بالأوضاع الموحدة ، والوسم ضمني، caseويتم استخدام البنية لتحديد الحقل الموسوم:

modenode = union (real, int, compl, string);

مثال على استخدام كلمة unioncase"of" node:

node n := "1234"; case n in ( real r): print(("real:", r)),  ( int i): print(("int:", i)), ( compl c): print(("compl:", c)), ( سلسلة نصية s): اطبع(("سلسلة نصية:", s)) اطبع (("?:", n)) esac

في ALGOL 68، يمكن تحويل الاتحاد تلقائيًا إلى اتحاد أوسع، على سبيل المثال إذا كان من الممكن التعامل مع جميع مكوناته بواسطة معلمة الاتحاد الخاصة بالطباعة ، فيمكن ببساطة تمرير الاتحاد إلى الطباعة كما في حالة الإخراج أعلاه.

آدا

في لغة آدا ، تسمى هذه "الأنواع المميزة".

نوع Shape_Kind هو ( مربع ، مستطيل ، دائرة نوع Shape ( النوع : نوع_الشكل ) هو سجل Center_X : عدد صحيح ؛ Center_Y : عدد صحيح ؛ حالة النوع هي عندما يكون المربع => الضلع : عدد صحيح ؛ عندما يكون المستطيل => العرض ، الارتفاع : عدد صحيح ؛ عندما يكون الدائرة => نصف القطر : عدد صحيح ؛ نهاية الحالة ؛ نهاية السجل ؛-- أي محاولة للوصول إلى عضو يعتمد وجوده على قيمة معينة للمميز، بينما -- المميز ليس هو المتوقع، تؤدي إلى حدوث خطأ.

لغة سي++

بينما لا توفر لغة C سوى unionنوع الاتحاد غير الموسوم، فإن لغة C++ توفر فئة (منذ C++17 )، ولكن بدلاً من أن تكون ميزة أساسية في اللغة مثل ، فهي فئة مكتبة قياسية .std::variant<Ts...>union

استيراد std ؛باستخدام std :: string ؛ باستخدام std :: variant ؛struct Cat { string name ; };struct Dog { string name ; };struct Bird { string name ; };باستخدام حيوان أليف = متغير < قطة ، كلب ، طائر > ؛الحيوان الأليف p1 = قطة ( "شوارب" ); الحيوان الأليف p2 = كلب ( "ريكس" );

يُعد "نمط التحميل الزائد" نمط تصميم شائعًا يُنفذ مطابقة الأنماط الجبرية باستخدام وراثة القوالب المتغيرة . [ 2 ]

// نوع مساعد لقالب الزائر < typename ... Ts > struct Overload : public Ts ... { using Ts :: operator ()...; }// قالب دليل الاستنتاج < typename ... Ts > Overload ( Ts ...) -> Overload < Ts ... > ;

بعد ذلك، std::visit()يمكن استخدام الدالة لاستدعاء كل حالة على حدة، حيث يتم التعامل مع كل حالة بواسطة دالة لامدا : [ 3 ]

باستخدام القيمة = متغير < عدد صحيح ، عدد عشري ، سلسلة نصية > ؛القيمة v = 3.14 ؛// يطبع "double: 3.14" std :: visit ( Overload { []( int i ) -> void { std :: println ( "int: {}" , i ); }, []( double d ) -> void { std :: println ( "double: {}" , d ); }, []( const string & s ) -> void { std :: println ( "string: {}" , s ); } }, v );

بالإضافة إلى ذلك، std::visit()يمكن جعلها تُرجع نوعًا ما:

حيوان أليف p = طائر ( "بولي" );string name = std :: visit ( Overload { [ ] ( const Cat & c ) -> string { return c.name ; }, [ ] ( const Dog & d ) -> string { return d.name ; }, []( const Bird & b ) - > string { return b.name ; } } , p ) ;// يطبع "اسم الحيوان الأليف: بولي" std :: println ( "اسم الحيوان الأليف: {}" , name );

تم تقديم نوع النتيجة لمعالجة الأخطاء (كما هو الحال في لغة Rust) إلى C++23 على النحو التالي :std::result::Result<T,E>std::expected<T,E>

سي شارب

لم تكن لغة C# تدعم الاتحادات الموسومة تقليديًا. وكانت أقرب طريقة لتقريبها هي من خلال مطابقة الأنماط على recordالأنواع. مع C# 15، تم تقديم الاتحادات الموسومة باستخدام الكلمة المفتاحية union. [ 4 ]

سجل فئة السيارة ( سلسلة موديل سجل فئة الدراجة ( سلسلة موديل سجل فئة الحافلة ( سلسلة موديل مركبة الاتحاد ( سيارة ، دراجة ، حافلة Vehicle car = new Car ( "Tesla Model 3" ) ; Console.WriteLine(car.Value ) ; // Car { Model = Tesla Model 3 }Vehicle bike = new Bicycle ( "Giant Escape 3" ); Console.WriteLine ( bike.Value ) ; // Bicycle { Model = Giant Escape 3 }Vehicle bus = new Bus ( " Volvo 9700" ) ; Console.WriteLine ( bus.Value ) ; // Bus { Model = Volvo 9700 }المركبة v = /* مركبة ما هنا */ ; سلسلة النموذج = v switch { سيارة c => c . النموذج , دراجة bk => b . النموذج , حافلة bs => bs . النموذج , };

قيمة defaultالاتحاد هي null، ولكن إذا كانت جميع الأنواع في الاتحاد غير قابلة للتصفير ، switchفلا داعي للتحقق من التعبير null.

إعصار

توفر لغة Cyclone ، وهي لهجة من لغة C مع تحسينات في الأمان، اتحادات موسومة. [ 5 ] وقد تم الإعلان عنها باستخدام @taggedمُؤهِّل.

@tagged union Foo { int i ; double d ; char * @fat s ; };void printFoo ( union Foo x ) { // سيتم تنبيه المُصرّف في حالة عدم وجود حالة مُحددة switch ( x ) { case { . i = i }: printf ( "%d" , i ); break ; case { . d = d }: printf ( "%g" , d ); break ; case { . s = s }: printf ( "%s" , s ); break ; } }

د

توفر Dstd.variant الوحدة النمطية، مع أنواع مثل Variant(تمثل أي نوع )، و (تمثل نوع بيانات جبري). [ 6 ]std.variant.Algebraic!(T...)

استيراد std.variant ؛Algebraic !( int , string ) v = 10 ;int result = v.visit !( ( string s ) => cast ( int ) s.length , ( int i ) => i , () = > -1 ) ( ) ; writeln ( result ) ; // 10

فا#

لقد مارست لغة F# التمييز ضد النقابات:

نوع الشجرة = | ورقة | عقدة القيمة : عدد صحيح * يسار : شجرة * يمين : شجرةlet tree = Node ( 5 , Node ( 1 , Leaf , Leaf ), Node ( 3 , Leaf , Node ( 4 , Leaf , Leaf )))

ولأن الحالات المحددة شاملة، يمكن للمترجم التحقق من معالجة جميع الحالات في مطابقة النمط:

مطابقة الشجرة مع | Node ( x , _, _) -> printfn "قيمة العقدة في المستوى الأعلى: %i" x | Leaf -> printfn "العقدة في المستوى الأعلى هي ورقة"

هاكس

تعمل تعدادات Haxe أيضًا كاتحادات موسومة: [ 7 ]

enum Color { Red ; Green ; Blue ; Rgb ( r : Int , g : Int , b : Int ); }

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

switch ( color ) { case Red : trace ( "كان اللون أحمر" ); case Green : trace ( "كان اللون أخضر" ); case Blue : trace ( "كان اللون أزرق" ); case Rgb ( r , g , b ): trace ( "كانت قيمة اللون الأحمر " + r ); }

جافا

في لغة جافا ، أقرب طريقة لتنفيذ اتحاد مُوسَم هي من خلال فئة مُغلقة ، والتي تُقيّد بشكل مباشر الأنواع التي يُمكنها وراثة الفئة. يُمكن إجراء مطابقة الأنماط نفسها على switchالتعبيرات. [ 8 ]

واجهة مغلقة باسم Shape تسمح بـ Circle و Rectangle و Triangle {} ، سجل Circle ( نصف قطر مزدوج ) ينفذ Shape {} ، سجل Rectangle ( عرض مزدوج ، ارتفاع مزدوج ) ينفذ Shape {} ، سجل Triangle ( قاعدة مزدوجة ، ارتفاع مزدوج ) ينفذ Shape {}دالة المساحة المزدوجة ( الشكل s ) { return switch ( الشكل ) { case دائرة ( double r ) -> Math.PI * r * r ; case مستطيل ( double w , double h ) -> w * h ; case مثلث ( double b , double h ) -> 0.5 * b * h ; } ; }

لغة الآلة القياسية

في لغة ML القياسية ، يكون الاتحاد الموسوم "نوع بيانات جبري" أو "نوع مجموع".

شكل نوع البيانات = دائرة حقيقية | مستطيل حقيقي * حقيقي | نقطةمنطقة ممتعة s = حالة s من دائرة r => 3.1415926535 * r * r | مستطيل ( w , h ) => w * h | نقطة => 0.0

نيم

تحتوي لغة Nim على متغيرات للكائنات [ 9 ] مشابهة في تعريفها لتلك الموجودة في لغتي Pascal و Ada:

نوع ShapeKind = تعداد skSquare ، skRectangle ، skCircle، الشكل = كائن ، مركز س ، مركز ص : عدد صحيح، نوع الحالة : ShapeKind، من skSquare : ضلع : عدد صحيح ، من skRectangle : طول ، ارتفاع : عدد صحيح، من skCircle : نصف القطر : عدد صحيح

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

استيراد باتيproc `~` [ A ] ( a : A ): ref A = new ( result ) result [] = aقائمة المتغيرات [ أ ] : لا شيء Cons ( x : أ ، xs : مرجع قائمة [ أ ] )proc listHelper [ A ] ( xs : seq [ A ] ) : ​​List [ A ] = if xs.len == 0 : Nil [ A ] ( ) else : Cons ( xs [ 0 ] , ~ listHelper ( xs [ 1..xs.high ] ) )proc list [ A ] ( xs : varargs [ A ] ) : ​​List [ A ] = listHelper ( @xs )proc sum ( xs : List [ int ] ): int = ( block : match xs : Nil : 0 Cons ( y , ys ): y + sum ( ys [] ) )echo sum ( list ( 1 , 2 , 3 , 4 , 5 ))

أوكاميل

في لغة OCaml ، يختلف بناء جملة الاتحاد الموسوم قليلاً عن لغة Standard ML، ولكنه لا يزال متشابهًا تقريبًا.

نوع الشكل = | دائرة من نوع عدد عشري | مستطيل من نوع عدد عشري * عدد عشري | نقطةlet area = function | Circle r -> Float . pi *. r *. r | Rectangle ( w , h ) -> w *. h | Point -> 0 . 0

باسكال

في لغة باسكال ، تسمى هذه "السجلات المتغيرة".

نوع الشكل = ( مربع ، مستطيل ، دائرة ) ؛ الشكل = سجل مركز س : عدد صحيح ؛ مركز ص : عدد صحيح ؛ حالة النوع : نوع الشكل من مربع : ( الضلع : عدد صحيح ) ؛ مستطيل : ( العرض ، الارتفاع : عدد صحيح ) ؛ دائرة : ( نصف القطر : عدد صحيح ) ؛ نهاية ؛

بايثون

يُقدّم الإصدار 3.9 من بايثون دعمًا لتعليقات الكتابة التي يمكن استخدامها لتحديد نوع الاتحاد المُوسوم (PEP-593 [ 10 ] ):

من typing استورد Annotated و TypedDictالعملة = Annotated [ TypedDict ( 'Currency' , { 'dollars' : float , 'pounds' : float }, total = False ), TaggedUnion , ]

الصدأ

تتمتع لغة Rust بدعم واسع النطاق للاتحادات الموسومة، والتي تسمى enums. [ 11 ] على سبيل المثال:

enum Tree { Leaf , Node ( i64 , Box < Tree > , Box < Tree > ) }

كما يسمح بالمطابقة على الاتحادات:

let tree : Tree = Tree :: Node ( 2 , Box :: new ( Tree :: Node ( 0 , Box :: new ( Tree :: Leaf ), Box :: new ( Tree :: Leaf ))), Box :: new ( Tree :: Node ( 3 , Box :: new ( Tree :: Leaf ), Box :: new ( Tree :: Node ( 4 , Box :: new ( Tree :: Leaf ), Box :: new ( Tree :: Leaf ))))) );fn add_values ​​( tree : Tree ) -> i64 { match tree { Tree :: Node ( v , a , b ) => v + add_values ​​( * a ) + add_values ​​( * b ), Tree :: Leaf => 0 } }fn main () { assert_eq! ( add_values ​​( tree ), 9 ); }

يعتمد نموذج معالجة الأخطاء في لغة Rust بشكل كبير على هذه الاتحادات الموسومة، وخاصة النوع الذي يكون إما أو ، والنوع الذي يكون إما أو . [ 12 ]std::option::Option<T>NoneSome(T)std::result::Result<T,E>Ok(T)Err(E)

سكالا

تحتوي لغة سكالا على فئات الحالة:

فئة مجردة مغلقة Tree، كائن حالة Leaf يمتد من Tree، فئة حالة Node ( القيمة : عدد صحيح ، اليسار : Tree ، اليمين : Tree ) تمتد من Treeval tree = Node ( 5 , Node ( 1 , Leaf , Leaf ), Node ( 3 , Leaf , Node ( 4 , Leaf , Leaf )))

لأن التسلسل الهرمي للفئات مغلق، يمكن للمترجم التحقق من معالجة جميع الحالات في مطابقة النمط:

مطابقة الشجرة { حالة العقدة ( س ، _ ، _ ) => println ( "قيمة العقدة في المستوى الأعلى: " + س ) حالة الورقة => println ( "العقدة في المستوى الأعلى هي ورقة" ) }

تسمح فئات الحالة في لغة سكالا أيضًا بإعادة الاستخدام من خلال التنميط الفرعي:

فئة مجردة مغلقة Shape ( centerX : Int , centerY : Int ) فئة حالة Square ( side : Int , centerX : Int , centerY : Int ) تمتد من Shape ( centerX , centerY ) فئة حالة Rectangle ( length : Int , height : Int , centerX : Int , centerY : Int ) تمتد من Shape ( centerX , centerY ) فئة حالة Circle ( radius : Int , centerX : Int , centerY : Int ) تمتد من Shape ( centerX , centerY )

تمت إضافة التعدادات في Scala 3، [ 13 ] مما يسمح لنا بإعادة كتابة أمثلة Scala السابقة بشكل أكثر إيجازًا:

enum Tree [ + T ]: case Leaf case Node ( x : Int , left : Tree [ T ], right : Tree [ T ])enum Shape ( centerX : Int , centerY : Int ): case Square ( side : Int , centerX : Int , centerY : Int ) extends Shape ( centerY , centerX ) case Rectangle ( length : Int , height : Int , centerX : Int , centerY : Int ) extends Shape ( centerX , centerY ) case Circle ( radius : Int , centerX : Int , centerY : Int ) extends Shape ( centerX , centerY )

سويفت

تدعم لغة Swift أيضًا بشكل كبير الاتحادات الموسومة عبر التعدادات. [ 14 ] على سبيل المثال:

enum Tree { case leaf indirect case node ( Int , Tree , Tree ) }let tree = Tree.node ( 2 , .node ( 0 , .leaf , .leaf ) , .node ( 3 , .leaf , .node ( 4 , .leaf , .leaf ) ) )دالة add_values ​​( _ tree : Tree ) -> Int { switch tree { case let . node ( v , a , b ): return v + add_values ​​( a ) + add_values ​​( b )case . leaf : return 0 } }assert ( add_values ​​( tree ) == 9 )

تايب سكريبت

باستخدام TypeScript، من الممكن أيضًا إنشاء اتحادات مُوسومة. على سبيل المثال:

واجهة Leaf { نوع : "ورقة" ; }واجهة Node { نوع : "node" ؛ قيمة : رقم ؛ يسار : شجرة ؛ يمين : شجرة ؛ }نوع الشجرة = ورقة | عقدة ؛const root : Tree = { kind : "node" , value : 5 , left : { kind : "node" , value : 1 , left : { kind : "leaf" }, right : { kind : "leaf" } }, right : { kind : "node" , value : 3 , left : { kind : "leaf" }, right : { kind : "node" , value : 4 , left : { kind : "leaf" }, right : { kind : "leaf" } } } };دالة زيارة ( شجرة : شجرة ) { تبديل ( نوع الشجرة ) { حالة "ورقة " : توقف ؛ حالة " عقدة " : سجل ( قيمة الشجرة ) ؛ زيارة ( يسار الشجرة ) ؛ زيارة ( يمين الشجرة ) ؛ توقف ؛ } }

التسلسلات الهرمية للفئات كاتحادات مصنفة

في التسلسل الهرمي النموذجي للفئات في البرمجة كائنية التوجه ، يمكن لكل فئة فرعية تغليف بيانات خاصة بها. تُحدد البيانات الوصفية المستخدمة للبحث عن الدوال الافتراضية (على سبيل المثال، مؤشر جدول الدوال الافتراضية للكائن في معظم تطبيقات لغة C++) الفئة الفرعية، وبالتالي تعمل فعليًا كعلامة تُحدد البيانات المخزنة بواسطة الكائن (انظر RTTI ). يقوم مُنشئ الكائن بتعيين هذه العلامة، وتبقى ثابتة طوال دورة حياة الكائن.

مع ذلك، يتضمن التسلسل الهرمي للفئات تعدد أشكال الأنواع الفرعية الحقيقي . ويمكن توسيعه بإنشاء فئات فرعية إضافية من نفس النوع الأساسي، وهو ما لا يمكن التعامل معه بشكل صحيح في نموذج الوسم/الإرسال. لذا، لا يمكن عادةً إجراء تحليل الحالة أو الإرسال على "وسم" الكائن الفرعي كما هو الحال مع الاتحادات الموسومة. تسمح بعض اللغات، مثل سكالا، بـ"إغلاق" الفئات الأساسية، وتوحيد الاتحادات الموسومة مع الفئات الأساسية المغلقة.

انظر أيضاً

مراجع

  1. https://arxiv.org/pdf/2111.03354 ص 8
  2. بارتلومي فيليبك (10 نوفمبر 2023). "سطران من التعليمات البرمجية و3 ميزات في C++17 - نمط التحميل الزائد" . isocpp.org . ISO C++.
  3. cppreference.com (29 يونيو 2026). "std::visit" . cppreference.com . cppreference.com.
  4. بيل واغنر (2 أبريل 2026). "استكشف أنواع الاتحاد في لغة C# 15" . devblogs.microsoft.com . مايكروسوفت.
  5. "الإعصار: الاتحادات المصنفة" .
  6. ^ أندريه ألكسندريسكو (29 يونيو 2026). "std.variant" . dlang.org . لغة د.
  7. "استخدام التعدادات - هاكس - مجموعة أدوات متعددة المنصات" . مؤسسة هاكس.
  8. شركة أوراكل (29 يونيو 2026). "تعبيرات التبديل" . docs.oracle.com . شركة أوراكل.
  9. "دليل لغة نيم" . nim-lang.org . تم الاطلاع عليه بتاريخ 23 يناير 2020 .
  10. "PEP 593 -- التعليقات التوضيحية المرنة للدوال والمتغيرات" . Python.org . تم الاطلاع عليه بتاريخ 2021-06-20 .
  11. "لغة البرمجة Rust" . موزيلا.
  12. "Rust By Example" . Mozilla.
  13. "مرجع لغة سكالا 3: التعدادات" . فريق سكالا.
  14. "التعدادات - لغة برمجة سويفت (سويفت 5.4)" . docs.swift.org . تم الاطلاع عليه بتاريخ 28-04-2021 .