سؤال

بعد اجتياز الأجزاء الرئيسية من كتاب Lisp التمهيدي، ما زلت لا أستطيع فهم ما هو العامل الخاص (quote) (أو ما يعادلها ') تعمل الوظيفة، ومع ذلك فقد كان هذا موجودًا في جميع أنحاء كود Lisp الذي رأيته.

ماذا تعمل، أو ماذا تفعل؟

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

المحلول

اجابة قصيرةتجاوز قواعد التقييم الافتراضية وقم بذلك لا تقييم التعبير (الرمز أو s-exp)، وتمريره إلى الوظيفة تمامًا كما كتبته.

اجابة طويلة:قاعدة التقييم الافتراضية

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

(* (+ a 2)
   3)

والذي بدوره يقيم (+ a 2), ، بالتقييم a و 2.قيمة الرمز a يتم البحث عنه في مجموعة الربط المتغيرة الحالية، ثم يتم استبداله.يقول a مرتبط حاليًا بالقيمة 3:

(let ((a 3))
  (* (+ a 2)
     3))

سوف نحصل على (+ 3 2), ، + يتم استدعاؤه بعد ذلك في 3 و 2 لينتج 5.شكلنا الأصلي هو الآن (* 5 3) العائد 15.

يشرح quote بالفعل!

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

هذا هو المكان quote ادخل.لنفترض أنك تريد رسم عمليات تخصيص الموارد من تطبيق Python، ولكنك تريد رسم عمليات تخصيص الموارد في Lisp.اجعل تطبيق Python الخاص بك يفعل شيئًا مثل هذا:

print("'(")
while allocating:
    if random.random() > 0.5:
        print(f"(allocate {random.randint(0, 20)})")
    else:
        print(f"(free {random.randint(0, 20)})")
    ...
print(")")

مما يتيح لك الحصول على مخرجات تبدو مثل هذا (جميلة بعض الشيء):

'((allocate 3)
  (allocate 7)
  (free 14)
  (allocate 19)
  ...)

تذكر ما قلته عنه quote ("علامة") هل تتسبب في عدم تطبيق القاعدة الافتراضية؟جيد.ما يمكن أن يحدث خلاف ذلك هو أن قيم allocate و free يتم البحث عنها، ونحن لا نريد ذلك.في Lisp لدينا، نرغب في القيام بما يلي:

(dolist (entry allocation-log)
  (case (first entry)
    (allocate (plot-allocation (second entry)))
    (free (plot-free (second entry)))))

بالنسبة للبيانات الواردة أعلاه، سيتم إجراء التسلسل التالي لاستدعاءات الوظائف:

(plot-allocation 3)
(plot-allocation 7)
(plot-free 14)
(plot-allocation 19)

لكن ماذا عن list?

حسنا، في بعض الأحيان لك يفعل تريد تقييم الحجج.لنفترض أن لديك وظيفة أنيقة تتعامل مع رقم وسلسلة وتعيد قائمة بالنتائج الناتجة ...أشياء.لنبدأ بداية خاطئة:

(defun mess-with (number string)
  '(value-of-number (1+ number) something-with-string (length string)))

Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER (1+ NUMBER) SOMETHING-WITH-STRING (LENGTH STRING))

يا!هذا ليس ما أردناه.نريد ان بشكل انتقائي تقييم بعض الحجج، وترك البعض الآخر كرموز.جرب رقم 2!

(defun mess-with (number string)
  (list 'value-of-number (1+ number) 'something-with-string (length string)))

Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER 21 SOMETHING-WITH-STRING 3)

ليس فقط quote, ، لكن backquote

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

(defun mess-with (number string)
  `(value-of-number ,(1+ number) something-with-string ,(length string)))

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

مهلا، لقد نسيت quote!

إذا أين يتركنا هذا؟اه صحيح ماذا يفعل quote في الواقع تفعل؟إنها ببساطة تُرجع حجتها (حججها) بدون تقييم!هل تتذكر ما قلته في البداية عن الوظائف العادية؟تبين أن بعض المشغلين/الوظائف بحاجة إلى ذلك لا تقييم حججهم.مثل IF - لن ترغب في تقييم الفرع الآخر إذا لم يتم أخذه، أليس كذلك؟ما يسمى مشغلين خاصين, ، جنبا إلى جنب مع وحدات الماكرو، تعمل من هذا القبيل.العوامل الخاصة هي أيضًا "بديهية" اللغة - الحد الأدنى من مجموعة القواعد - والتي يمكنك من خلالها تنفيذ بقية Lisp من خلال دمجها معًا بطرق مختلفة.

ارجع الى quote, ، رغم ذلك:

Lisp> (quote spiffy-symbol)
SPIFFY-SYMBOL

Lisp> 'spiffy-symbol ; ' is just a shorthand ("reader macro"), as shown above
SPIFFY-SYMBOL

قارن بـ (على Steel-Bank Common Lisp):

Lisp> spiffy-symbol
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "initial thread" RUNNING   {A69F6A9}>:
  The variable SPIFFY-SYMBOL is unbound.

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

(SB-INT:SIMPLE-EVAL-IN-LEXENV SPIFFY-SYMBOL #<NULL-LEXENV>)
0] 

لأنه لا يوجد spiffy-symbol في النطاق الحالي!

تلخيص لما سبق

quote, backquote (مع فاصلة)، و list هي بعض الأدوات التي تستخدمها لإنشاء القوائم، وهي ليست قوائم قيم فحسب، ولكن كما ترى يمكن استخدامها كقوائم خفيفة الوزن (لا حاجة إلى تحديد struct) هياكل البيانات!

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

القرصنة سعيدة!

نصائح أخرى

تقول "لا تقيمني".على سبيل المثال، إذا أردت استخدام قائمة كبيانات، وليس كرمز، فستضع عرض أسعار أمامها.على سبيل المثال،

(print '(+ 3 4)) يطبع "(+ 3 4)"، بينما(print (+ 3 4)) المطبوعات "7"

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

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

هذا يمكن أن يسبب مشاكل واضحة.إذا قمت بتعديل ثابت، فقد يؤدي ذلك إلى تعديل الاستخدامات الأخرى لنفس الثابت في تعليمات برمجية غير مرتبطة تمامًا.على سبيل المثال، يمكنك مقارنة بعض المتغيرات بـ '(1 1) في بعض الوظائف، وفي دالة مختلفة تمامًا، ابدأ قائمة بـ '(1 1) ثم أضف المزيد من العناصر إليها.عند تشغيل هذه الوظائف، قد تجد أن الوظيفة الأولى لم تعد تطابق الأشياء بشكل صحيح، لأنها تحاول الآن مقارنة المتغير بـ '(1 1 2 3 5 8 13)، وهو ما أعادته الوظيفة الثانية.هاتان الوظيفتان غير مرتبطتين تمامًا، لكن لهما تأثير على بعضهما البعض بسبب استخدام الثوابت.حتى التأثيرات السيئة الأكثر جنونًا يمكن أن تحدث، مثل تكرار قائمة عادية تمامًا وفجأة تكرارًا لا نهائيًا.

استخدم الاقتباس عندما تحتاج إلى قائمة ثابتة، مثلاً للمقارنة.استخدم القائمة عندما ستقوم بتعديل النتيجة.

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

ما يقوله (اقتباس X) هو في الأساس "لا تفعل أي شيء ، فقط أعطني X." لا تحتاج إلى أن تكون قائمة كما في (QUOTE (ABC)) أو رمز كما في (QUOTE FOO).يمكن أن يكون أي كائن مهما كان.في الواقع، فإن نتيجة تقييم القائمة التي يتم إنتاجها بواسطة (LIST 'QUOTE SOME-OBJECT) ستُرجع دائمًا فقط SOME-OBJECT، مهما كان.

الآن، السبب الذي يجعل (QUOTE (ABC)) يبدو كما لو أنه أنشأ قائمة عناصرها هي A وB وC هو أن هذه القائمة هي ما تُرجعه بالفعل؛ولكن في الوقت الذي يتم فيه تقييم نموذج عرض الأسعار، تكون القائمة بشكل عام موجودة بالفعل لفترة من الوقت (كأحد مكونات نموذج عرض الأسعار!)، والتي تم إنشاؤها إما بواسطة المُحمل أو القارئ قبل تنفيذ التعليمات البرمجية.

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

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

يقوم الاقتباس بإنشاء هياكل بيانات القائمة، على سبيل المثال، ما يلي متكافئ:

(quote a)
'a

يمكن استخدامه أيضًا لإنشاء قوائم (أو أشجار):

(quote (1 2 3))
'(1 2 3)

من الأفضل أن تحصل على كتاب تمهيدي عن اللثغة، مثل اللثغة المشتركة العملية (وهو متاح للقراءة على الانترنت).

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

(define atom?              ; defining a procedure atom?
  (lambda (x)              ; which as one argument x
(and (not (null? x)) (not(pair? x) )))) ; checks if the argument is atom or not
(atom? '(a b c)) ; since it is a list it is false #f

السطر الأخير (الذرة؟'abc) يقوم بتمرير abc كما هو الحال في الإجراء للتحقق مما إذا كان abc ذرة أم لا، ولكن عندما تقوم بتمرير (atom؟abc) ثم يتحقق من قيمة abc ويمرر القيمة إليها.ومنذ ذلك الحين، لم نقدم أي قيمة لها

في ايماكس ليسب:

ما الذي يمكن اقتباسه؟

القوائم والرموز.

نقلا عن رقم يتم تقييمه للرقم نفسه:'5 بالضبط مثل 5.

ماذا يحدث عند اقتباس القوائم؟

على سبيل المثال:

'(one two) يقيم ل

(list 'one 'two) الذي يقيم ل

(list (intern "one") (intern ("two"))).

(intern "one") ينشئ رمزًا باسم "واحد" ويخزنه في خريطة تجزئة "مركزية"، لذلك في أي وقت تقوله 'one ثم الرمز المسمى "one" سيتم البحث عنه في خريطة التجزئة المركزية تلك.

ولكن ما هو الرمز؟

على سبيل المثال، في لغات OO (Java/Javascript/Python) يمكن تمثيل الرمز ككائن له name الحقل، وهو اسم الرمز مثل "one" أعلاه، ويمكن ربط البيانات و/أو التعليمات البرمجية بهذا الكائن.

لذلك يمكن تنفيذ رمز في بايثون على النحو التالي:

class Symbol:
   def __init__(self,name,code,value):
       self.name=name
       self.code=code
       self.value=value

في Emacs Lisp على سبيل المثال، يمكن أن يحتوي الرمز على 1) بيانات مرتبطة به و(في نفس الوقت - لنفس الرمز) 2) رمز مرتبط به - اعتمادًا على السياق، يتم استدعاء البيانات أو الكود.

على سبيل المثال، في إليسب:

(progn
  (fset 'add '+ )
  (set 'add 2)
  (add add add)
)

يقيم ل 4.

لأن (add add add) يقيم على النحو التالي:

(add add add)
(+ add add)
(+ 2 add)
(+ 2 2)
4

لذلك، على سبيل المثال، باستخدام Symbol فئة حددناها في بايثون أعلاه، وهذا add يمكن كتابة ELisp-Symbol بلغة بايثون كـ Symbol("add",(lambda x,y: x+y),2).

شكرًا جزيلاً للأشخاص في IRC #emacs لشرح الرموز والاقتباسات لي.

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

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

إجابة مختصرة أخرى:

quote يعني دون تقييم ذلك، و اقتباس خلفي هو الاقتباس ولكن ترك أبواب خلفية.

مرجع جيد:

يوضح الدليل المرجعي لـ Emacs Lisp الأمر بوضوح شديد

9.3 الاقتباس

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

نموذج خاص:كائن الاقتباس

This special form returns object, without evaluating it. 

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

فيما يلي بعض الأمثلة على التعبيرات التي تستخدم الاقتباس:

(quote (+ 1 2))
     ⇒ (+ 1 2)

(quote foo)
     ⇒ foo

'foo
     ⇒ foo

''foo
     ⇒ (quote foo)

'(quote foo)
     ⇒ (quote foo)

9.4 الاقتباس الخلفي

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

`(a list of (+ 2 3) elements)
     ⇒ (a list of (+ 2 3) elements)

'(a list of (+ 2 3) elements)
     ⇒ (a list of (+ 2 3) elements)

تشير العلامة الخاصة "،" الموجودة داخل وسيطة الاقتباس الخلفي إلى قيمة غير ثابتة.يقوم مقيم Emacs Lisp بتقييم الوسيطة "،" ويضع القيمة في بنية القائمة:

`(a list of ,(+ 2 3) elements)
     ⇒ (a list of 5 elements)

يُسمح بالاستبدال بـ "،" في المستويات الأعمق من بنية القائمة أيضًا.على سبيل المثال:

`(1 2 (3 ,(+ 4 5)))
     ⇒ (1 2 (3 9))

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

(setq some-list '(2 3))
     ⇒ (2 3)

(cons 1 (append some-list '(4) some-list))
     ⇒ (1 2 3 4 2 3)

`(1 ,@some-list 4 ,@some-list)
     ⇒ (1 2 3 4 2 3)
Code is data and data is code.  There is no clear distinction between them.

هذه عبارة كلاسيكية يعرفها أي مبرمج LISP.

عندما تقتبس رمزًا، سيكون هذا الرمز عبارة عن بيانات.

1 ]=> '(+ 2 3 4)
;Value: (+ 2 3 4)

1 ]=> (+ 2 3 4)
;Value: 9

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

1 ]=> 'code
;Value: code

1 ]=> '10
;Value: 10

1 ]=> '"ok"
;Value: "ok"

1 ]=> code
;Unbound variable: code

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

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