आप वास्तव में हैश तालिका को तीन बार एक्सेस कर सकते हैं। क्यूं कर? चूंकि push
मैक्रो कोड में विस्तार कर सकता है जो सूची प्राप्त करने के लिए gethash
करता है, और फिर मान को संग्रहीत करने के लिए कुछ system::sethash
ऑपरेशन करता है।
इस समस्या में, आप किसी स्थान की मान का निरीक्षण कर रहे हैं, जो एक सूची है। यदि वह सूची कुछ अनुमानित परीक्षण को पूरा करती है, तो आप उस स्थान पर कुछ धक्का देते हैं।
(push-if <new-value> <predicate> <place>)
उदाहरण के लिए::
यह समस्या जो इस अर्थ विज्ञान कब्जा विशेष प्रयोजन ऑपरेटर बनाने के द्वारा हमला किया जा सकता है
(push-if i #'may-add (gethash complex-list table))
यह push-if
एक मैक्रो पर get-setf-expansion
फ़ंक्शन का उपयोग करता है जो के रूप में परिभाषित किया गया है <place>
उस स्थान तक पहुंचने के लिए कोड उत्पन्न करने के लिए आवश्यक टुकड़े प्राप्त करने के लिए फॉर्म तर्क।
जेनरेट कोड स्थान से पुराना मान प्राप्त करने के लिए लोड फॉर्म का मूल्यांकन करता है, फिर पुराने मान पर स्थिति लागू करता है, और यदि यह सफल होता है, तो यह get-setf-expansion
से प्राप्त उचित अस्थायी स्टोर चर में नया मान तैयार करता है और स्टोर फॉर्म का मूल्यांकन करता है।
यह पोर्टेबल लिस्प में आप सबसे अच्छा कर सकते हैं, और आप पाते हैं कि यह अभी भी ऊपर बताए गए दो हैश ऑपरेशंस करता है। (जो मामले में आप आशा है कि हैश तालिका अपने आप में एक सभ्य कैशिंग अनुकूलन है लेकिन कम से कम यह दो ऑप्स के लिए नीचे है।।)
दृष्टिकोण के रूप में जगह परिवर्तनशील रूपों में बनाया के रूप में अनुकूलित किया जाएगा:, push
incf
, rotatef
, आदि। हमारे push-if
बिल्ट-इन्स के बराबर होंगे।
यदि यह अभी भी बेकार है (कोई हैश स्थान अपडेट करने के लिए दो हैश करता है, कैशिंग ऑप्टिमाइज़ेशन के साथ), तो इसे ठीक करने का एकमात्र तरीका कार्यान्वयन स्तर पर है।
push-if
कोड इस प्रकार है:
(defmacro push-if (new-value predicate-fun list-place &environment env)
(multiple-value-bind (temp-syms val-forms
store-vars store-form access-form)
(get-setf-expansion list-place env)
(let ((old-val (gensym)))
(when (rest store-vars)
(error "PUSH-IF: cannot take ref of multiple-value place"))
`(multiple-value-bind (,@temp-syms) (values ,@val-forms)
(let ((,old-val ,access-form))
(when (funcall ,predicate-fun ,old-val)
(setf ,(first store-vars) (cons ,new-value ,old-val))
,store-form))))))
नमूना विस्तार:
> (macroexpand '(push-if new test place))
(LET* ((#:VALUES-12731 (MULTIPLE-VALUE-LIST (VALUES))))
(LET ((#:G12730 PLACE))
(WHEN (FUNCALL TEST #:G12730) (SETF #:NEW-12729 (CONS NEW #:G12730))
(SETQ PLACE #:NEW-12729)))) ;
सरल मामले के लिए समझदार लगता है जब जगह एक चर है। केवल थोड़ी सी समस्या है जिसे मैं ठीक नहीं कर रहा हूं: फॉर्म new
, test
और place
का मूल्यांकन केवल एक बार किया जाता है, लेकिन बाएं से दाएं क्रम में नहीं! एक हैश तालिका जगह के साथ
टेस्ट (CLISP):
> (macroexpand '(push-if new test (gethash a b)))
(LET*
((#:VALUES-12736 (MULTIPLE-VALUE-LIST (VALUES A B)))
(#:G12732 (POP #:VALUES-12736)) (#:G12733 (POP #:VALUES-12736)))
(LET ((#:G12735 (GETHASH #:G12732 #:G12733)))
(WHEN (FUNCALL TEST #:G12735) (SETF #:G12734 (CONS NEW #:G12735))
(SYSTEM::PUTHASH #:G12732 #:G12733 #:G12734)))) ;
अहा; a
और b
का मूल्यांकन करने से बचने के लिए अब कुछ और दिलचस्प कोड उत्पन्न किया जा रहा है। gethash
फ़ंक्शन एक बार लागू किया जाता है, लेकिन इसके तर्क gensym चर हैं। पुराना मान #:G12735
के रूप में कब्जा कर लिया गया है। परीक्षण इस पर लागू होता है, और यदि यह गुजरता है, तो स्टोर variabel #:G12734
को पुराने सूची मूल्य के साथ अपडेट किया गया है जिसमें new
इसके सामने लगाया गया है। फिर, वह मान हैश तालिका में system::puthash
के साथ रखा गया है।
तो इस लिस्प कार्यान्वयन में, अद्यतन करने के लिए दो हैश तालिका संचालन से बचने का कोई तरीका नहीं है: gethash
और system::puthash
। यह सबसे अच्छा है जो हम कर सकते हैं और उम्मीद करते हैं कि दो अनुकूलित अनुकूलित जोड़ी के रूप में काम करते हैं।
कूल - धन्यवाद :) –
ऐसा लगता है कि हम पॉल एफ। डायटज़ का धन्यवाद कर सकते हैं: http://git.boinkor.net/gitweb/sbcl.git/commitdiff/bc1783335d78be988465e4fc7cf9c5fdb88a3fa4 –