سؤال

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

(defmacro ++ (variable)
  (incf variable))

ولكن هذا يعطيني خطأً بسيطًا عند محاولة استخدامه.ما الذي سيجعلها تعمل؟

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

المحلول

تذكر أن الماكرو يقوم بإرجاع تعبير ليتم تقييمه.للقيام بذلك، عليك أن تقتبس:

(defmacro ++ (variable)
   `(incf ,variable))

نصائح أخرى

تعمل كلتا الإجابتين السابقتين، لكنهما توفران لك ماكرو تسميه باسم

(++ varname)

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

(defun plusplus-reader (stream subchar arg)
   (declare (ignore subchar arg))
   (list 'incf (read stream t nil t)))
(set-dispatch-macro-character #\+ #\+ #'plusplus-reader)

يجب أن يجعل ++var في الواقع يقرأ كـ (incf فار).

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

إذا كنت تريد زيادة لاحقة بسيطة، جرب هذا:

(defmacro post-inc (number &optional (delta 1))
  "Returns the current value of number, and afterwards increases it by delta (default 1)."
  (let ((value (gensym)))
    `(let ((,value ,number))
       (incf ,number ,delta)
       ,value)))

بناء الجملة (++ a) هو اسم مستعار عديمة الفائدة ل (incf a).لكن لنفترض أنك تريد دلالات ما بعد الزيادة:استرجاع القيمة القديمةفي Common Lisp، يتم ذلك باستخدام prog1, ، كما في: (prog1 i (incf i)).لا تعاني لغة Lisp الشائعة من أوامر تقييم غير موثوقة أو غامضة.التعبير السابق يعني ذلك i يتم تقييمها، ثم يتم تخزين القيمة في مكان ما (incf i) يتم تقييمها، ومن ثم يتم إرجاع القيمة المخزنة.

صنع مضاد للرصاص تماما pincf (بريد-incf) ليست تافهة تماما. (incf i) لديه خاصية لطيفة ذلك i يتم التقييم مرة واحدة فقط.نحن نريد أو نحب (pincf i) أن يكون لديك هذه الخاصية أيضًا.وبالتالي فإن الماكرو البسيط يقصر:

(defmacro pincf (place &optional (increment 1))
  `(prog1 ,place (incf ,place ,increment))

للقيام بذلك بشكل صحيح علينا أن نلجأ إلى "محلل مكان المهمة" الذي يطلق عليه Lisp get-setf-expansion للحصول على المواد التي تسمح للماكرو الخاص بنا بتجميع الوصول بشكل صحيح:

(defmacro pincf (place-expression &optional (increment 1) &environment env)
  (multiple-value-bind (temp-syms val-forms
                        store-vars store-form access-form)
                        (get-setf-expansion place-expression env)
    (when (cdr store-vars)
      (error "pincf: sorry, cannot increment multiple-value place. extend me!"))
    `(multiple-value-bind (,@temp-syms) (values ,@val-forms)
       (let ((,(car store-vars) ,access-form))
         (prog1 ,(car store-vars)
                (incf ,(car store-vars) ,increment)
                ,store-form)))))

بعض الاختبارات باستخدام CLISP.(ملحوظة:التوسعات التي تعتمد على مواد من get-setf-expansion قد تحتوي على تعليمات برمجية خاصة بالتنفيذ.هذا لا يعني أن الماكرو الخاص بنا ليس محمولاً!)

8]> (macroexpand `(pincf simple))
(LET* ((#:VALUES-12672 (MULTIPLE-VALUE-LIST (VALUES))))
 (LET ((#:NEW-12671 SIMPLE))
  (PROG1 #:NEW-12671 (INCF #:NEW-12671 1) (SETQ SIMPLE #:NEW-12671)))) ;
T
[9]> (macroexpand `(pincf (fifth list)))
(LET*
 ((#:VALUES-12675 (MULTIPLE-VALUE-LIST (VALUES LIST)))
  (#:G12673 (POP #:VALUES-12675)))
 (LET ((#:G12674 (FIFTH #:G12673)))
  (PROG1 #:G12674 (INCF #:G12674 1)
   (SYSTEM::%RPLACA (CDDDDR #:G12673) #:G12674)))) ;
T
[10]> (macroexpand `(pincf (aref a 42)))
(LET*
 ((#:VALUES-12679 (MULTIPLE-VALUE-LIST (VALUES A 42)))
  (#:G12676 (POP #:VALUES-12679)) (#:G12677 (POP #:VALUES-12679)))
 (LET ((#:G12678 (AREF #:G12676 #:G12677)))
  (PROG1 #:G12678 (INCF #:G12678 1)
   (SYSTEM::STORE #:G12676 #:G12677 #:G12678)))) ;
T

الآن هنا حالة اختبار رئيسية.وهنا المكان يحتوي على آثار جانبية: (aref a (incf i)).يجب تقييم هذا مرة واحدة بالضبط!

[11]> (macroexpand `(pincf (aref a (incf i))))
(LET*
 ((#:VALUES-12683 (MULTIPLE-VALUE-LIST (VALUES A (INCF I))))
  (#:G12680 (POP #:VALUES-12683)) (#:G12681 (POP #:VALUES-12683)))
 (LET ((#:G12682 (AREF #:G12680 #:G12681)))
  (PROG1 #:G12682 (INCF #:G12682 1)
   (SYSTEM::STORE #:G12680 #:G12681 #:G12682)))) ;
T

إذن ما يحدث أولاً هو ذلك A و (INCF I) يتم تقييمها، وتصبح المتغيرات المؤقتة #:G12680 و #:G12681.يتم الوصول إلى المصفوفة ويتم التقاط القيمة فيها #:G12682.ثم لدينا لدينا PROG1 الذي يحتفظ بتلك القيمة مقابل العودة.تتم زيادة القيمة وتخزينها مرة أخرى في موقع المصفوفة عبر CLISP system::store وظيفة.لاحظ أن استدعاء المتجر هذا يستخدم المتغيرات المؤقتة، وليس التعبيرات الأصلية A و I. (INCF I) يظهر مرة واحدة فقط.

من الناحية الدلالية، فإن عوامل تشغيل البادئة ++ و- في لغة مثل c++ أو أي لغة أخرى تعادل incf/decf في اللثغة الشائعة.إذا أدركت ذلك، ومثل الماكرو (غير الصحيح)، كنت تبحث بالفعل عن تغيير نحوي، فقد أوضحت لك بالفعل كيفية القيام بذلك باستخدام علامات التحديد الخلفية مثل `(incf ,x).لقد تم أيضًا توضيح كيفية جعل القارئ يخترق هذا الأمر ليقترب من بناء الجملة غير اللثغة.هذه هي المشكلة، حيث أن أيًا من هذه الأشياء لا يعد فكرة جيدة.بشكل عام، لا يبدو أن البرمجة غير الاصطلاحية لجعل اللغة تشبه لغة أخرى بشكل أوثق فكرة جيدة.

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

إذا كان هذا هو ما تبحث عنه، فأنا أقترح أ) الالتزام بأسماء incf/decf نظرًا لأنها اصطلاحية وتعمل بشكل جيد و ب) كتابة إصدارات post-incf وpost-decf، على سبيل المثال (defmacro post-incf (x) `(prog1 ,x (incf ,x)) أنواع الأشياء.

أنا شخصياً لا أرى كيف سيكون هذا مفيدًا بشكل خاص ولكن ymmv.

بالنسبة للزيادة المسبقة، يوجد بالفعل incf، ولكن يمكنك تحديد الخاص بك باستخدام

(define-modify-macro my-incf () 1+)

بالنسبة للزيادة اللاحقة، يمكنك استخدام هذا (من أدوات الأجرة):

(defmacro define-values-post-modify-macro (name val-vars lambda-list function)
 "Multiple-values variant on define-modify macro, to yield pre-modification values"
 (let ((env (gensym "ENV")))
   `(defmacro ,name (,@val-vars ,@lambda-list &environment ,env)
      (multiple-value-bind (vars vals store-vars writer-form reader-form)
          (get-setf-expansion `(values ,,@val-vars) ,env)
       (let ((val-temps (mapcar #'(lambda (temp) (gensym (symbol-name temp)))
                                 ',val-vars)))
          `(let* (,@(mapcar #'list vars vals)
                  ,@store-vars)
             (multiple-value-bind ,val-temps ,reader-form
               (multiple-value-setq ,store-vars
                 (,',function ,@val-temps ,,@lambda-list))
               ,writer-form
               (values ,@val-temps))))))))

(defmacro define-post-modify-macro (name lambda-list function)
 "Variant on define-modify-macro, to yield pre-modification values"
 `(define-values-post-modify-macro ,name (,(gensym)) ,lambda-list ,function))

(define-post-modify-macro post-incf () 1+)

على الرغم من أنني بالتأكيد سأضع في اعتباري الملاحظات والتنبيه لذلك سيمون التعليقات في منصبه، وأعتقد حقا أن user10029لا يزال نهج s يستحق المحاولة، لذا، من أجل المتعة فقط، حاولت دمجه مع الإجابة المقبولة لإنشاء ++x عمل المشغل (أي زيادة قيمة x في 1).جربها!

توضيح:لن يقوم SBCL القديم بتجميع نسخته لأنه يجب تعيين الرمز "+" بشكل صريح في جدول البحث عن الإرسال باستخدام make-dispatch-macro-character, ، ولا تزال هناك حاجة إلى الماكرو لتمرير اسم المتغير قبل تقييمه.لذلك يجب أن يقوم هذا بالمهمة:

(defmacro increment (variable)
  "The accepted answer"
  `(incf ,variable))

(make-dispatch-macro-character #\+) ; make the dispatcher grab '+'

(defun |inc-reader| (stream subchar arg)
  "sets ++<NUM> as an alias for (incf <NUM>).
   Example: (setf x 1233.56) =>1233.56
            ++x => 1234.56
            x => 1234.56"
   (declare (ignore subchar arg))
   (list 'increment (read stream t nil t)))

(set-dispatch-macro-character #\+ #\+ #'|inc-reader|)

يرى |inc-reader|docstring للحصول على مثال الاستخدام.يمكن العثور على الوثائق ذات الصلة (عن كثب) هنا:

أدى هذا التنفيذ إلى عدم فهم إدخالات الأرقام مثل +123 (ينتقل مصحح الأخطاء باستخدام no dispatch function defined for #\Newline) ولكن يبدو أن الحل البديل (أو حتى تجنبه) معقول:إذا كنت لا تزال ترغب في الالتزام بهذا، فربما يكون الخيار الأفضل هو عدم استخدام ++ كبادئة، ولكن ## أو أي حل آخر يشبه DSL

هتافات!

أندريس

هذا يجب أن يفي بالغرض، لكنني لست خبيرًا في اللثغة.

(defmacro ++ (variable)
  `(setq ,variable (+ ,variable 1)))
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top