دي (لغة برمجة)

المميزات

لقد صممت لغة دي بدروس مستفادة من الاستخدام العملي للغة  C++ أكثر من الاعتماد في ذلك على التصور النظري. وبالرغم من واقع أنها تستخدم العديد من مفاهيم لغتي C / سي++، فإنها أيضا لا تستخدم البعض الآخر وكذلك فإنها غير متوافقة مع الشيفرة الأصلية لـ C/C++. كما أنها تضيف إلى وظيفية  C++ من خلال تطبيق التصميم التعاقدي، واختبار الوحدة، والوحدات الحقيقية، وجمع القمامة، والمصفوفات من الدرجة الأولى، والمصفوفات الترابطية، والمصفوفات الديناميكية، والمصفوفة الشرائحية، والدوال المتداخلة، والفئات الداخلية، والإغلاقات، والدوال المجهولة، وتنفيذ الوظيفة في وقت التصريف، والتقييم الكسول، كما أنها أعادت هندسة بنية القالب. وتحتفظ لغة دي بقدرة  C++ على القيام باتشفير متدني لامستوى وتضيف له بدعم من أجل inline assembler. ويحل محل الوراثة المتعددة لـ C++ أسلوب جافا ذو الوراثة الواحدة المزود بواجهات ومتخلطات. إن إعلان وبيان وتعبير دي عن البنية مرتبط بقوة بنظيرتها في  C++.
ويحدد inline assembler نوع الاختلافات بين دي ولغات التطبيق مثل جافا و سي شارب. ويمكن inline assembler المبرمجين من إدخال شفرة تجميع الآلة المحددة وفق معيار شفرة لغة دي- وهو التكنيك الذي يستخدم عادة من قبل مبرمجي النظام للوصول إلى الميزات منخفضة المستوى للمعالج والمطلوبة لتشغيل البرامج التي تتداخل مباشرة مع العتاد hardware المحدد مثل نظم التشغيل ومشغلات الجهاز.
يوجد في لغة دي دعم للتوثيق، بما يسمح بتوليد الآلي للتوثيق.

نماذج البرمجة

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

الأمرية

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

القائمة على الكائن

إن البرمجة القائمة على الكائن في دي تقوم على تراتبية موروثة واحدة، مع جميع الفئات المشتقة من فئة الكائن Object. ولا تدعم لغة دي الموروث المتعدد؛ وبدلا من ذلك فإنها تستخدم واجهات بأسلوب الجافا، والتي يمكن مقارنتها بالفئات المجردة الصرفة للغة C++، والفئات mixins، والتي تسمح بفصل الوظيفية المشتركة عن التراتبية الموروثة. إضافة إلى ذلك فإن دي 2.0 تسمح بإعلان الطرق الثابتة والنهائية (غير الظاهرية) في الواجهات.

البرمجة الأعلى

تدعم البرمجة الأعلى مجموعة من القوالب، وتنفيذ وظيفة تصريف الوقت، التتابعات tuples، وسلسلة الفئات. وتظهر النماذج التالية بعضا من مميزات تصريف الوقت في لغة دي.
ويمكن أن تكون القوالب في لغة دي مكتوبة في أسلوب أكثر شبها بالوظيفة مقارنة بما هو عليه الحال في سي++. وهذه وظيفة منتظمة تحسب مضروب رقم ما:
ulong factorial(ulong n){
    if(n <2)
        return 1;
    else
        return n * factorial(n - 1);}
وهنا فإن استخدام إذا الثابتة، وهي البنية الشرطية لتصريف الوقت في لغة دي، يظهر لبناء قالب يقوم بنفس الحساب باستخدام الشفرة التي تشبه الوظيفة المذكورة عاليه:
template Factorial(ulong n){
    static if(n <2)
        const Factorial = 1;
    else
        const Factorial = n * Factorial!(n - 1);}
وفي المثالين التاليين، فإن كلا من القالب والوظيفة المحددان عاليه يستخدمان لحساب المضاعفات. إن أنواع الثوابت ليست بحاجة إلى وضوح محدد كما هو حال المصرف في استنباط أنواعها من الجوانب اليمنى للواجبات:
const fact_7 = Factorial!(7);
وفيما يلي مثال على تنفيذ وظيف تصريف الوقت. يمكن استخدام الوظائف المعتادة في التعبيرات الثابتة والمصرفة زمنيا بشرط أن تلبي معايير معينة:
const fact_9 = factorial(9);
ويؤدي قالب std.metastrings.Formatمهمة تنسيق البيانات الذي يشبه طباعة إف printf، ويستعرض "msg" pragma النتيجة عند وقت التصرف:
import std.metastrings;pragma(msg, Format!("7! = %s", fact_7));pragma(msg, Format!("9! = %s", fact_9));
إن سلسلة الفئات Mixins، بالترافق مع تنفيذ وظيفة تصريف الوقت، تسمح بتوليد شفرة دي باستخدام عمليات مسلسلة عند وقت التصرف. ويمكن أن يستخدم ذلك لتحليل اللغات محددة النطاق لشفرة دي، والتي سيتم التصرف فيها كجزء من البرنامج
import std.algorithm, std.range, std.stdio;int main(){
    int[] a1 = [0,1,2,3,4,5,6,7,8,9];
    int[] a2 = [6,7,8,9];
    immutable pivot = 5; // must be immutable to allow access from inside mysum

    int mysum(int a, int b) pure // pure function
    {
        if (b <= pivot) // ref to enclosing-scope
            return a + b;
        else
            return a;
    }

    auto result = reduce!(mysum)( chain(a1, a2) ); // passing a delegate (closure)
    writeln("Result: ", result); // output is "15"

    return 0;}

الوظيفية

D 2.0 only.
import std.algorithm, std.range, std.stdio;int main(){
    int[] a1 = [0,1,2,3,4,5,6,7,8,9];
    int[] a2 = [6,7,8,9];
    immutable pivot = 5; // must be immutable to allow access from inside mysum

    int mysum(int a, int b) pure // pure function
    {
        if (b <= pivot) // ref to enclosing-scope
            return a + b;
        else
            return a;
    }

    auto result = reduce!(mysum)( chain(a1, a2) ); // passing a delegate (closure)
    writeln("Result: ", result); // output is "15"

    return 0;}

التزامن

D 2.0 only.
import std.concurrency, std.stdio, std.typecons;int main(){
    auto tid = spawn(&foo); // create an actor object

    foreach(i; 0 .. 10)
        tid.send(i);    // send some integers
    tid.send(1.0f);     // send a float
    tid.send("hello");  // send a string
    tid.send(thisTid);  // send an object (Tid)

    receive( (int x) {  writeln("Main thread receives message: ", x);  });

    return 0;}void foo(){
    bool cont = true;

    while (cont)
    {
        receive(  // pattern matching
            (int msg) { writeln("int receive: ", msg); },  // int type
            (Tid sender){ cont = false; sender.send(-1); },  // object type
            (Variant v){ writeln("huh?"); }  // any type
        );
    }}

إدارة الذاكرة

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

التفاعل مع النظم الأخرى

إن واجهة التطبيق الثنائية لسي (ABI) مدعومة أيضا كجميع الأنواع الأساسية والمشتقة، وتمكن من الوصول المباشر إلى شيفرة ومكتبات سي الموجودة. إن مكتبة سي القياسية هي جزء من المعيار دي.
إن واجهة التطبيق الثنائية لـ C++ ليست مدعومة بالكامل، بالرغم من أنه يمكن لدي أن تستخدم شيفرة C++ المكتوبة لواجهة التطبيق الثنائية لسي. ويفهم محلل دي (C++) خارجي يستدعي اتفاقا من أجل صلة محدودة بكائنات C++، لكنه لا يطبق إلا في D 2.0.

الإصدارات

توجد لغة برمجة دي في إصدارين: 1.0 و 2.0. وقد أصبحت دي 1.0مستقرة بإصدار D2.0 في 17 يونيو 2007، ومنذ ذلك الوقت قدمت إضافات مزيدة للغة منذ تلك لاتي أضيفت للنسخة 2.0. إن إصدار كتاب أندريه ألكسندريسكو الذي يحمل عنوان لغة برمجة دي في 12 يونيو 2010 شكل بداية لاستقرار D2.0.

دي 1.0

صدر الإصدار 1.00من دي ديجيتال مارس في 2 يناير 2007. ومنذ صدور دي 2.0 لم يحظ دي 1.0 وتطبيقه الخاص بديجيتال مارس إلا بإضافات هامشية ومميزات غير كبيرة لم تؤثر على التوافقية تأثيرا رجعيا.

تقسيم المكتبة القياسية

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

الدعم غير المحدود للمكتبات المشاركة/ الحركية

إن مكتبات Unix’s ELF المتشاركة مدعوة لاستخدام مصرف جي دي سي.
وبالنسبة لنظم ويندوز فإن مكتبات الوصل الديناميكية Dynamic Link Libraries تكون مدعومة وتسمح للكائنات المخصصة من قبل جامعة المهملات في لغة دي بأن يمر بأمان إلى وظائف سي، لأن جامع المهملات يمسح الركام للمؤشرات. بأي حال فإنه لا تزال هناك قيود متعلقة بمكتبات الوصل الميكانيكية في لغة دي بما فيها حقيقة أن معلومات نوع وقت التشغيل للفئات المعرفة في مكتبات الوصل الديناميكية غير متوافقة مع تلك المحددة في القابلة للتنفيذ، وهكذا فإن أي كائن يكون من داخل مكتبات الوصل الديناميكية يجب أن يكتمل قبل تفريغ مكتبات الوصل الديناميكية.

تناول السلسلة

تمتعت اللغة ثلاثة أنواع حروف مميزة وهي ( حرف char، حرف و wchar، وحرف د dchar) وثلاثة كنيات مسلسلة (وهي السلسلة، سلسلة و، وسلسلة د، التي هي ببساطة مجموعات ديناميكية من السابقة) والتي تقدم وحدات شفرات وسلاسل UTF-8, UTF-16 ,UTF-32 على التوالي. ولأسباب متعلقة بالأداء، فإن تشريح السلسلة والخاصية الطويلة يعملان على وحدات التشفير أكثر من نقاط التشفير (الأحرف)، والتي تجعل المطورين مضطربين باستمرار. ولأن كل من UTF-8 و UTF-16 عبارة عن فك شفرات حرفية متباينة الطول، فإن الوصول باستخدام مؤشر نقطة التشفير في وقت ثابت ليس ممكنا بدون الحفاظ على جداول بحث إضافية. إن الشفرة التي تحتاج إلى وصول عشوائي سريع لنقاط التشفير سوف تحول السلاسل إلى UTF-32 أولا، أو استخدام جداول البحث. بأي حال فإن ذلك أيضا أمرا صحيحا بالنسبة للغات البرمجة الأخرى الداعمة لفك الشفرات Unicode مثل جافا و سي شارب اللتان تستخدمان UTF-16، وهكذا فغنهما قد تحتاجا إلى أزواج بديلة لتمثيل بعض نقاط التشفير.

المثال 1

إن هذا البرنامج المثالي يطبع سطر أمر معطياته. وتكون وظيفته الأساسية هي نقطة إدخال برنامج دي، وأرجات args هي مجموعة من السلاسل التي تمثل معطيات سطر الأمر. والسلسلة في دي هي مجموعة من الأحرف، والممثلة من خلال الحرف [] في دي 1.0 أو ( حرف) [] غير قابل للتغيير في أبجدية دي 2.0. بأي حال فإن الإصدارات الأحدث للغة تعرف السلسلة باعتبارها كنية عن الحرف[] أو ( حرف) [] غير قابل للتغيير ، ومن الضروري هنا وجود تعريف كنية واضحة للتوافق مع الإصدارات الأقدم
import std.stdio: writefln;void main(string[] args){
    foreach (i, arg; args)
        writefln("args[%d] = '%s'", i, arg);}
يمكن لبيان الفوريتش أن يحدد أي مجموعة، وفي هذه الحالة فإنه ينتج مؤشرات مرتبة (i) وقيم (arg) من مجموعة أرجات args. إن المؤشر i والقيمة arg لهما أنواعهما المستنبطة من نوع المجموعة أرجات.
وباستخدام مكتبة تانجو فإن الشفرة السابقة ستكون كما يلي:
import tango.io.Stdout;void main(char[][] args){
    foreach (i, arg; args)
        Stdout("args[")(i)("] = '")(arg)("'").newline();}