لماذا ترتبط الكتابة الديناميكية في كثير من الأحيان باللغات المفسرة؟

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

سؤال

أسئلة بسيطة من الناس: أقوم بالكثير من البرمجة (من الناحية المهنية والشخصية) بلغات مجمعة مثل C ++/java وفي لغات متوسعة مثل Python/JavaScript. أنا شخصياً أجد أن الكود الخاص بي دائمًا ما يكون أكثر قوة تقريبًا عندما أقوم برمجة باللغات المكتوبة بشكل ثابت. ومع ذلك ، فإن كل لغة تم تفسيرها تقريبًا تستخدم الكتابة الديناميكية (PHP ، Perl ، Python ، إلخ). أعرف لماذا تستخدم اللغات المترجمة كتابة ثابتة (معظم الوقت) ، لكن لا يمكنني معرفة النفور من الكتابة الثابتة في تصميم اللغة المفسر.

لماذا الانفصال الحاد؟ هل هو جزء من طبيعة اللغات المفسرة؟ OOP؟

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

المحلول

سؤال مهم. راجع للشغل ، أنا المؤلف/المشرف PHC (برنامج التحويل البرمجي لـ PHP) ، وأنا أقوم بعمل درجة الدكتوراه على المترجمين للغات الديناميكية ، لذلك آمل أن أتمكن من تقديم بعض الأفكار.

أعتقد أن هناك افتراضًا خاطئًا هنا. لم يصمم مؤلفو PHP و Perl و Python و Ruby و Lua وما إلى ذلك "اللغات المفسرة" ، وتصميمهم لغات ديناميكية ، وقاموا بتنفيذها باستخدام المترجمين الفوريين. لقد فعلوا ذلك لأن المترجمين الفوريين أسهل بكثير في الكتابة من المترجمين.

تم تفسير تطبيق Java الأول ، وهي لغة مطبوعة بشكل ثابت. يوجد المترجمون المترجمون المترجمون في اللغات الثابتة: Haskell و OCAML على حد سواء لديهم فترات فوريين ، وكان هناك مترجم مشهور لـ C ، لكن ذلك كان منذ وقت طويل. إنها شائعة لأنها تسمح استبدال, والتي يمكن أن تجعل التطوير أسهل.

ومع ذلك ، هناك نفور من الكتابة الثابتة في مجتمع اللغة الديناميكية ، كما تتوقع. وهم يعتقدون أن أنظمة النوع الثابت التي توفرها C و C ++ و Java هي مطوّلة ، ولا تستحق كل هذه الجهد. أعتقد أنني أتفق مع هذا إلى حد ما. البرمجة في Python أكثر متعة بكثير من C ++.

لمعالجة نقاط الآخرين:

  • يقول دلامبلين: "لم أشعر أبدًا بقوة أن هناك أي شيء مميز حول التجميع مقابل التفسير الذي اقترح ديناميكيًا على الكتابة الثابتة." حسنًا ، أنت مخطئ جدًا هناك. تجميع اللغات الديناميكية أمر صعب للغاية. يوجد في الغالب eval بيان يجب مراعاته ، والذي يستخدم على نطاق واسع في جافا سكريبت وروبي. يقوم PHC بتجميع PHP قبل الوقت ، لكننا ما زلنا بحاجة إلى مترجم في وقت التشغيل للتعامل معه evalس. eval لا يمكن تحليلها أيضًا بشكل ثابت في برنامج التحويل البرمجي المحسن ، على الرغم من وجود ملف تقنية رائعة إذا كنت لا تحتاج إلى سلامة.

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

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

  • الجواب الأكثر صحة حتى الآن هو الحصول على إيفو ويتزل. ومع ذلك ، يمكن التعامل مع النقاط التي يصفها في وقت التشغيل في برنامج التحويل البرمجي ، ويوجد العديد من المترجمين لـ LISP والمخطط الذي يحتوي على هذا النوع من الربط الديناميكي. ولكن ، نعم ، هذا صعب.

نصائح أخرى

تستخدم اللغات المفسرة الكتابة الديناميكية لأنه لا توجد خطوة تجميع لإجراء التحليل الثابت. اللغات المترجمة تقوم بتحليل ثابت في وقت التجميع مما يعني أنه يتم الإبلاغ عن أي أخطاء من النوع إلى المطور أثناء عملها.

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

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

تخيل السيناريو التالي (في بيثون):

import random
foo = 1

def doSomeStuffWithFoo():
    global foo
    foo = random.randint(0, 1)

def asign():
    global foo
    if foo == 1:
        return 20
    else:
        return "Test"


def toBeStaticallyAnalyzed():
    myValue = asign()

    # A "Compiler" may throw an error here because foo == 0, but at runtime foo maybe 1, so the compiler would be wrong with its assumption
    myValue += 20


doSomeStuffWithFoo() # Foo could be 1 or 0 now... or 4 ;)
toBeStaticallyAnalyzed()

كما ترون ، لن ترى المترجم أي معنى في هذا الموقف. من الممكن أن يحذرك من احتمال أن "myValue" ربما شيء آخر غير الرقم. ولكن بعد ذلك في JavaScript قد يفشل لأنه إذا كانت "MyValue" عبارة عن سلسلة ، فسيتم تحويل 20 ضمنيًا إلى سلسلة أيضًا ، وبالتالي لن يحدث أي خطأ. لذلك قد تحصل على الآلاف من التحذيرات عديمة الفائدة في كل مكان ، ولا أعتقد أن هذا هو نية المترجم.

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

إذن الحل الخاص بك كمترجم؟ - قم بإصلاحه بـ "جرب: باستثناء" :)

المجمعين + أنواع ثابتة = رمز الجهاز الفعال
المجمعين + الأنواع الديناميكية = رمز الجهاز غير الفعال

النظر في الرمز الكاذب التالي:

function foo(a, b) {
    return a+b
}

ستتمكن لغة ثابتة من معرفة (عن طريق الإعلان أو الاستدلال) بأن A و B هي أعداد صحيحة ، وسوف يتم تجميعها إلى

%reg = addi a,b

أو شيء مشابه ، على أي حال.

يجب على المترجم للغة الديناميكية أن ينبعث منه الرمز إليه
1. تحقق من أنها أنواع A و B
2. التعامل مع كل حالة أو مجموعة من الحالات

%reg1 = typeof a
beq %reg1, int, a_int_case
beq %reg1, float, a_float_case
beq %reg1, string, a_string_case

label a_int_case
%reg1 = typeof b
beq %reg1, int, a_int_b_int_case
beq %reg1, float, a_int_b_float_case
beq %reg1, string, a_int_b_string_case

label a_int_b_int_case
%out = addi a,b
goto done

label a_int_b_float_case
%tmp = mkfloat a
%out = addf %tmp,b
goto done

... Etc. I can't finish

على الرغم من أنه يمكنك إنشاء رمز آلي أكثر ذكاءً من ذلك ، إلا أنك لن تتمكن من المساعدة في توليد الكثير من التعليمات البرمجية - مما يجعل التجميع ليس فوزًا كبيرًا بلغة ديناميكية.

نظرًا لأن المترجمين الفوريين أسهل بكثير في الكتابة ، والتجميع لا يفيدك كثيرًا ، فلماذا لا تكتب مترجمًا؟

(لدى المجمعين في الوقت المناسب بالفعل معلومات من النوع ، ويمكنهم تجميعها وصولاً إلى البيان الفردي. لديهم بالفعل معلومات أكثر من أنظمة النوع الثابت يمكن أن يعمل على آلة حقيقية هو من قبيل الصدفة.)

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

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

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

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

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

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

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

تتفوق اللغات المفسرة في بيئة ديناميكية. إذا كنت تستطيع تفسير التعليمات البرمجية/المعلومات الجديدة في وقت التشغيل ، فلماذا لا. إذا كنت جيدًا حقًا في البرمجة الديناميكية ، فيمكنك إنشاء رمز يمكنه إنشاء متغيرات وتجزئة دون كتابة كل شيء على الإطلاق. يمكنك تقليل كمية الخطوط بشكل كبير إذا كنت تعمل بكميات هائلة من البيانات. يمكنك استخدام dumper البيانات لطباعة جميع المعلومات الخاصة بك لأن اللغات المفسرة عادة ما تتبع نوع المتغيرات في وقت التشغيل مما يتيح أن يكون ذلك ممكنًا. لا يمكنك القيام بذلك في Barebones C ++. المرة الوحيدة التي تعرف فيها C ++ و C ما يحدث في وقت الترجمة. بعد ذلك بنفسك إلا إذا قمت بتنفيذ شيء بنفسك.

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

في طريق أنماط البرمجة: ينتج عن الكود المكتوب بشكل ثابت نتائج ثابتة. ينتج التعليمات البرمجية المكتوبة ديناميكيًا نتائج ديناميكية أو ثابتة.

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

كل لغة لها صعودا وهبوطا. فقط يجب أن تختار واختيار بحكمة.

أعتقد أن الكتابة الثابتة تجعل الأمر أسهل على المجمعين وهذا هو السبب الرئيسي (إن لم يكن فقط) في وجوده في اللغات المترجمة.

بالنسبة لللغات المفسرة ، من الأسهل افتراض أن المتغيرات ليس لها نوع (فقط للقيم) لأنها لا يُعتقد أنها ليست بمثابة فرضية للبيانات التي يجب أن تتناسب مع الداخل بل تسمية للبيانات التي تطفو في مكان ما على الكومة.

إذا كان المبرمج يريد أن يؤكد دائمًا أن المتغير يحمل قيمة نوع معين (على سبيل المثال في المهمة). لا يوجد سبب لبناءها في اللغة. بالطبع هذا ليس نفس النوع من التحكم الذي لديك للغات المترجمة.

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

على الجانب الآخر. هل تعرف أي لغة تم تكتبها ديناميكيًا (بشكل ثابت ، وليس JIT)؟

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