2013-07-28 5 views
8
(push x list) 

लिस्प में विपक्ष के रूप में क्या जोड़ना है?

(setq list (cons x list)) 

के लिए विस्तारित क्या निम्नलिखित के लिए विस्तारित:

(setq list (append list2 list)) 

? क्या इसके लिए एक मानक मैक्रो है?

+3

मेरा मानना ​​है कि ऐसा कोई मैक्रो नहीं है, लेकिन आप इसे स्वयं लिख सकते हैं :) – monoid

+1

आप * nconc * पर एक नज़र डाल सकते हैं, जो कि आप बिल्कुल पूछना नहीं चाहते हैं, लेकिन थोड़ा समान है। –

+1

@arbautjc nconc भी setq के साथ इस्तेमाल नहीं किया जाना चाहिए? या तो (setq सूची (nconc सूची-से-प्रीपेन्ड सूची)) या (setq सूची (nconc सूची सूची-से-एपेंड-एंड-एंड))। दोनों मामलों में, setq आवश्यक है। –

उत्तर

15

जैसा कि अन्य उत्तरों और टिप्पणियों ने इंगित किया है, इसके लिए एक मानक मैक्रो नहीं है, और आप अपना खुद का लिख ​​सकते हैं। मेरी राय में, यह define-modify-macro के लिए एक अच्छा मामला है, और मैं पहले इसका वर्णन करूंगा। आप get-setf-expansion का उपयोग करके मैन्युअल रूप से ऐसा मैक्रो भी लिख सकते हैं, और मैं इसका भी एक उदाहरण दिखाऊंगा।

define-modify-macro

define-modify-macro के लिए HyperSpec पृष्ठ पर उदाहरणों में से एक का उपयोग करना appendf है:

विवरण:

परिभाषित-संशोधित-मैक्रो नामक मैक्रो नाम को परिभाषित करता है पढ़ने और लिखने के लिए एक जगह।

नए मैक्रो के लिए तर्क एक स्थान हैं, इसके बाद लैम्ब्डा-सूची में दिए गए तर्कों के बाद। परिभाषित-संशोधित-मैक्रो के साथ परिभाषित मैक्रोज़ गेट-सेट-विस्तार के लिए पर्यावरण पैरामीटर को सही ढंग से पास करते हैं।

जब मैक्रो का आह्वान किया जाता है, तो स्थान की पुरानी सामग्री और नए मूल्य प्राप्त करने के लिए लैम्ब्डा-सूची तर्कों पर फ़ंक्शन लागू होता है, और परिणाम को परिणाम रखने के लिए अपडेट किया जाता है।

उदाहरण

(define-modify-macro appendf (&rest args) 
    append "Append onto list") => APPENDF 
(setq x '(a b c) y x) => (A B C) 
(appendf x '(d e f) '(1 2 3)) => (A B C D E F 1 2 3) 
x => (A B C D E F 1 2 3) 
y => (A B C) 

appendf उदाहरण के लिए, आप जो खोज रहे हैं से उलट के बाद से अतिरिक्त तर्क place तर्क की पूंछ के रूप में जोड़ दिए जाते हैं है। हालांकि, हम वांछित व्यवहार के कार्यात्मक संस्करण लिख सकें (बस append तर्क आदेश के साथ बदली यह है), और फिर define-modify-macro का उपयोग करें:

(defun swapped-append (tail head) 
    (append head tail)) 

(define-modify-macro swapped-appendf (&rest args) 
    swapped-append) 

(let ((x '(1 2 3)) 
     (y '(4 5 6))) 
    (swapped-appendf x y) 
    x) 
; => (4 5 6 1 2 3) 

आप एक समारोह के रूप swapped-append परिभाषित करने के लिए नहीं करना चाहते हैं, तो आप दे सकते हैं एक lambda -expression define-modify-macro: जैसे, धारणात्मक, (swapped-appendf list list2)(setq list (append list2 list)) के लिए विस्तारित

(define-modify-macro swapped-appendf (&rest args) 
    (lambda (tail head) 
    (append head tail))) 

(let ((x '(1 2 3)) 
      (y '(4 5 6))) 
  (swapped-appendf x y) 
  x) 
; => (4 5 6 1 2 3) 

तो, इस सवाल का जवाब है। यह अभी भी मामला है कि swapped-appendf पर तर्क गलत क्रम में प्रतीत हो सकते हैं।सब के बाद, अगर हम define-modify-macro और cons का उपयोग कर push परिभाषित, तर्क मानक push से एक अलग क्रम में होगा:

(define-modify-macro new-push (&rest args) 
    (lambda (list item) 
    (cons item list))) 

(let ((x '(1 2 3))) 
    (new-push x 4) 
    x) 
; => (4 1 2 3) 

define-modify-macro के बारे में पता करने के लिए एक आसान उपकरण है, और मैं इसे उपयोगी जब कार्यात्मक मिल गया है (यानी, गैर-दुष्प्रभाव) कार्यों के संस्करण लिखना आसान है और एक एपीआई के लिए एक संशोधित संस्करण भी वांछित है। रों तर्क item और list हैं

get-setf-expansion

new-push का उपयोग करते हुए 'जबकि push रों तर्क, list और item हैं'। मुझे नहीं लगता कि swapped-appendf में तर्क आदेश काफी महत्वपूर्ण है, क्योंकि यह मानक मुहावरे नहीं है। हालांकि, prependf मैक्रो लिखकर अन्य आदेश प्राप्त करना संभव है जिसका कार्यान्वयन get-setf-expansion को स्थान के लिए Setf Expansion सुरक्षित रूप से प्राप्त करने के लिए और एकाधिक मूल्यांकन से बचने के लिए उपयोग करता है।

(defmacro prependf (list place &environment environment) 
    "Store the value of (append list place) into place." 
    (let ((list-var (gensym (string '#:list-)))) 
    (multiple-value-bind (vars vals store-vars writer-form reader-form) 
     (get-setf-expansion place environment) 
     ;; prependf works only on a single place, so there 
     ;; should be a single store-var. This means we don't 
     ;; handle, e.g., (prependf '(1 2 3) (values list1 list2)) 
     (destructuring-bind (store-var) store-vars 
     ;; Evaluate the list form (since its the first argument) and 
     ;; then bind all the temporary variables to the corresponding 
     ;; value forms, and get the initial value of the place. 
     `(let* ((,list-var ,list) 
       ,@(mapcar #'list vars vals) 
       (,store-var ,reader-form)) 
      (prog1 (setq ,store-var (append ,list-var ,store-var)) 
      ,writer-form)))))) 

(let ((x '(1 2 3)) 
     (y '(4 5 6))) 
    (prependf y x) 
    x) 
; => (4 5 6 1 2 3) 

get-setf-expansion के उपयोग का मतलब है कि और अधिक जटिल स्थानों पर इस मैक्रो काम करता है, भी:

(let ((x (list 1 2 3)) 
     (y (list 4 5 6))) 
    (prependf y (cddr x)) 
    x) 
; => (1 2 4 5 6 3) 

शैक्षिक उद्देश्यों के लिए, यह प्रासंगिक macroexpansions देखने के लिए दिलचस्प है, और कैसे वे एक से अधिक मूल्यांकन से बचने रूप, और writer-form एस क्या वास्तव में मूल्य निर्धारित करने के लिए उपयोग किया जाता है। वहाँ get-setf-expansion में बंडल कार्यशीलता का एक बहुत कुछ है, और इसके बारे में कुछ कार्यान्वयन विशिष्ट है:

;; lexical variables just use SETQ 
CL-USER> (pprint (macroexpand-1 '(prependf y x))) 
(LET* ((#:LIST-885 Y) 
     (#:NEW886 X)) 
    (PROG1 (SETQ #:NEW886 (APPEND #:LIST-885 #:NEW886)) 
    (SETQ X #:NEW886))) 

;; (CDDR X) gets an SBCL internal RPLACD 
CL-USER> (pprint (macroexpand-1 '(prependf y (cddr x)))) 
(LET* ((#:LIST-882 Y) 
     (#:G883 X) 
     (#:G884 (CDDR #:G883))) 
    (PROG1 (SETQ #:G884 (APPEND #:LIST-882 #:G884)) 
    (SB-KERNEL:%RPLACD (CDR #:G883) #:G884))) 

;; Setting in an array gets another SBCL internal ASET function 
CL-USER> (pprint (macroexpand-1 '(prependf y (aref some-array i j)))) 
(LET* ((#:LIST-887 Y) 
     (#:TMP891 SOME-ARRAY) 
     (#:TMP890 I) 
     (#:TMP889 J) 
     (#:NEW888 (AREF #:TMP891 #:TMP890 #:TMP889))) 
    (PROG1 (SETQ #:NEW888 (APPEND #:LIST-887 #:NEW888)) 
    (SB-KERNEL:%ASET #:TMP891 #:TMP890 #:TMP889 #:NEW888))) 
+0

अच्छा समाधान। शायद हम इस मैक्रो * प्रीपेन्डफ * का नाम बदल सकते हैं?:-) –

+1

* कॉल * '(.... पूंछ सिर) में तर्क आदेश 'अप्राकृतिक IMHO लगता है। आप इसे 'परिभाषित-संशोधित-मैक्रो' का उपयोग करने में सक्षम होने के लिए करते हैं, क्योंकि यह पहले तर्क को सेट करने के स्थान के रूप में मानता है, लेकिन यहां दूसरे तर्क का इलाज करना स्वाभाविक है। –

+0

@WillNess यह मेरे लिए अप्राकृतिक नहीं लगता है क्योंकि यह एक असामान्य ऑपरेटर है, लेकिन मैंने 'get-setf-expansion' आधारित मैक्रो के साथ उत्तर अपडेट किया है जो दूसरे क्रम में तर्क प्राप्त करता है। –

1

जहां तक ​​मुझे पता है, वहां कुछ भी तैयार नहीं है, लेकिन इसे बनाने के लिए अपेक्षाकृत आसान होना चाहिए।

(defmacro tail-push (place val) 
    (let ((tmp (gensym "TAIL"))) 
    `(let ((,tmp ,place)) 
     (setf (cdr (last ,tmp)) ,val) 
     ,tmp))) 
+1

यह काम नहीं करता है: आप expr को संशोधित करते हैं, वैल नहीं । –

+0

@arbautjc एर, यह इस मैक्रो का * बिंदु * है। '(एक बी दबाएं)' बी के सिर पर रखता है, '(पूंछ-पुश ए बी)' की पूंछ पर बी डालता है। अच्छी तरह से सममित। नाम को 'प्लेस' में और भी वर्णनात्मक बनाने के लिए बदल दिया। – Vatine

+1

निश्चित रूप से सममित, लेकिन ओपी द्वारा नहीं पूछा गया था, जो कि बराबर नहीं है (सेटक सूची (सूची 2 सूची संलग्न करें)। आप देखते हैं, यह * पूंछ * है (सूची, सूची नहीं 2) जिसे बदला जाना चाहिए। आपका मैक्रो क्या करता है बिल्कुल (nconc जगह वैल) है। –

3

चीज़ें थोड़ी, Vatine के जवाब के बारे में स्पष्ट करने के लिए:

प्रारंभिक सवाल के साथ

, हम

(defparameter list '(1 2 3)) 
(defparameter list2 '(4 5 6)) 
(setq list (append list2 list)) 

list 
(4 5 6 1 2 3) 

list2 
(4 5 6) 
है

यही है, list2 सूची में अनुशंसित है, लेकिन list2 स्वयं संशोधित नहीं है। कारण यह है कि जोड़ना सीधे इसके तर्कों को नहीं बदलता है।

अब,

साथ
(defmacro tail-push (place val) 
    (let ((tmp (gensym "TAIL"))) 
    `(let ((,tmp ,place)) 
     (setf (cdr (last ,tmp)) ,val) 
     ,tmp))) 

पहले तर्क

स्विच करके देखें

(defparameter list '(1 2 3)) 
(defparameter list2 '(4 5 6)) 
(tail-push list2 list) 

list 
(1 2 3) 

list2 
(4 5 6 1 2 3) 

दूसरा कोशिश,

(defparameter list '(1 2 3)) 
(defparameter list2 '(4 5 6)) 
(tail-push list list2) 

list 
(1 2 3 4 5 6) 

list2 
(4 5 6) 

बहरहाल, सूची में से एक दूसरे से जोड़ दिया जाता है बस, क्योंकि nconc, या (rplacd (अंतिम ...) ...) या यहाँ, direc टली (setf (सीडीआर (अंतिम ...)) ...), केवल जोड़ सकते हैं, पूर्ववत नहीं। और हम केवल दावा नहीं कर सकते कि पहली कोशिश सही जवाब देती है '(4 5 6 1 2 3), क्योंकि सूची संशोधित नहीं की गई थी, जबकि सूची 2 था, जो बिल्कुल आवश्यक नहीं था।

हालांकि

, यहोशू समाधान के साथ,

(defun swapped-append (tail head) 
    (append head tail)) 

(define-modify-macro swapped-appendf (&rest args) 
    swapped-append) 

(defparameter list '(1 2 3)) 
(defparameter list2 '(4 5 6)) 
(swapped-appendf list list2) 

list 
(4 5 6 1 2 3) 

list2 
(4 5 6) 

और यह अपेक्षा के अनुरूप काम कर रहा है।

1

अगर (push x lst)(setf lst (cons x lst)) के रूप में फैलता है तो बस बनाने के लिए मैक्रो prepend जैसे कॉल (prepend xs lst) का विस्तार होता है कि (setf lst (append xs lst)) के रूप में:

(defmacro prepend (a b) 
    `(setf ,b (append ,a ,b))) 

2 तर्क एक जगह निरूपित चाहिए, लेकिन तो यह push के रूप में के लिए होना चाहिए कुंआ।

आप सावधान रहने की तर्क वहाँ जगह अंदर लंबा भारी गणना नहीं करने के लिए किसी और होगा, या:

[14]> (setq x (list (list 1 2) (list 3 4))) 
((1 2) (3 4)) 
[15]> (prepend '(a b c) (nth (print (- 1 1)) x)) 

0    ;; calculated and 
0    ;; printed twice! 
(A B C 1 2) 
[16]> x 
((A B C 1 2) (3 4)) 
2

यहोशू टेलर कैसे कॉमन लिस्प में यह करने के लिए उल्लेख किया है। मैं Emacs लिस्प में कैसे जवाब देंगे:

(require 'cl-lib) 
(defmacro appendf (place &rest lists) 
    `(cl-callf append ,place ,@lists)) 
(defmacro prependf (list place) 
    `(cl-callf2 append ,list ,place)) 

और कुछ परीक्षण:

(let ((to-prepend '(the good)) 
     (acc '(the bad)) 
     (to-append-1 '(the weird)) 
     (to-append-2 '(pew pew))) 
    (prependf to-prepend acc) 
    (appendf acc to-append-1 to-append-2) 
    (list :acc acc 
     :to-prepend to-prepend 
     :to-append-1 to-append-1 
     :to-append-2 to-append-2)) 
; ⇒ (:acc (the good the bad the weird pew pew) :to-prepend (the good) :to-append-1 (the weird) :to-append-2 (pew pew)) 

मैक्रो विस्तार परीक्षण:

(let ((print-gensym t)) 
    (print 
    (macroexpand '(prependf y (cddr x))))) 
; prints (let* ((#:a1 y) (#:v x)) (setcdr (cdr #:v) (append #:a1 (cddr #:v)))) 

macroexpand -1 और सुंदर मुद्रण के लिए, macrostep पैकेज का उपयोग करें।

संबंधित मुद्दे