ความแตกต่างระหว่าง "set", "setq" และ "setf" ใน Common Lisp?


166

อะไรคือความแตกต่างระหว่าง "set", "setq" และ "setf" ใน Common LISP?


9
พฤติกรรมของสิ่งเหล่านี้ได้รับการตอบรับค่อนข้างดีในคำตอบ แต่คำตอบที่ยอมรับนั้นมีคำศัพท์ที่เข้าใจผิดสำหรับ "f" ใน "setf" คำตอบของ f ใน setf คืออะไร บอกว่ามันมีไว้สำหรับ "ฟังก์ชั่น" และให้การอ้างอิงเพื่อสำรอง
Joshua Taylor

คำตอบ:


169

แต่เดิมใน Lisp ไม่มีตัวแปรคำศัพท์ - เฉพาะตัวแปรแบบไดนามิก และไม่มี SETQ หรือ SETF เพียงฟังก์ชั่น SET

สิ่งที่เขียนตอนนี้:

(setf (symbol-value '*foo*) 42)

ถูกเขียนเป็น:

(set (quote *foo*) 42)

ซึ่งท้ายที่สุดก็คือย่อมาจาก SETQ (SET Quote):

(setq *foo* 42)

จากนั้นตัวแปรคำศัพท์เกิดขึ้นและ SETQ ก็ถูกนำมาใช้สำหรับการมอบหมายให้พวกเขาด้วยเช่นกันดังนั้นจึงไม่ใช่เรื่องง่ายที่จะล้อมรอบตลาดหลักทรัพย์

ต่อมามีบางคนคิดค้น SETF (เขตข้อมูล SET) เป็นวิธีทั่วไปในการกำหนดค่าให้กับโครงสร้างข้อมูลเพื่อทำมิเรอร์ค่า l ของภาษาอื่น:

x.car := 42;

จะเขียนเป็น

(setf (car x) 42)

สำหรับความสมมาตรและความเป็นเหตุเป็นผล SETF ได้จัดเตรียมฟังก์ชันการทำงานของ SETQ ณ จุดนี้มันจะถูกต้องที่จะบอกว่า SETQ เป็นแบบดั้งเดิมระดับต่ำและ SETF เป็นการดำเนินงานระดับสูง

จากนั้นสัญลักษณ์มาโครจะเกิดขึ้น ดังนั้นมาโครสัญลักษณ์นั้นสามารถทำงานได้อย่างโปร่งใสมันก็ตระหนักว่า SETQ จะต้องทำหน้าที่เหมือน SETF ถ้า "ตัวแปร" ที่กำหนดให้เป็นแมโครสัญลักษณ์จริงๆ:

(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))

foo => 42

(setq foo 13)

foo => 13

*hidden* => (13 . 42)

ดังนั้นเรามาถึงในปัจจุบัน: SET และ SETQ ยังคงเป็นภาษาที่เสื่อมโทรมของภาษาถิ่นที่เก่ากว่าและอาจจะถูกบูตจากผู้สืบทอดในที่สุดของ Common Lisp


45
Lisp สามัญมักมีตัวแปรคำศัพท์ คุณต้องพูดถึง Lisp ก่อนที่ Common Lisp
Rainer Joswig

4
หาก SET และ SETQ ถูกบูตจากตัวต่อ Common LISP พวกเขาจะต้องได้รับการแทนที่ การใช้รหัสระดับสูงนั้นมี จำกัด แต่ต้องใช้รหัสระดับต่ำ (ตัวอย่างเช่นการนำรหัส SETF ไปใช้) จำเป็นต้องใช้
Svante

13
มีเหตุผลที่คุณเลือก 'รถยนต์' เป็นฟิลด์แทนที่จะเป็นบางสิ่งที่อาจสับสนในขณะที่ฟังก์ชั่นของรถหรือไม่
drudru

9
คำตอบนี้เพื่ออะไรฉยืน setf สำหรับ? อ้างว่าfจริง ๆ แล้วหมายถึงฟังก์ชั่นไม่ใช่เขตข้อมูล (หรือแบบฟอร์มสำหรับเรื่องนั้น) และให้การอ้างอิงดังนั้นในขณะที่ setf สำหรับเขตข้อมูลทำให้รู้สึกบางอย่างดูเหมือนว่ามันอาจไม่ถูกต้อง
Joshua Taylor

1
สรุป: setเป็นฟังก์ชั่น ดังนั้นจึงไม่รู้จักสภาพแวดล้อม setไม่สามารถดูตัวแปรศัพท์ มันสามารถตั้งค่าสัญลักษณ์ - ค่าของการโต้แย้งเท่านั้น setqไม่ใช่ "set quote" อีกต่อไป ความจริงที่ว่าsetqเป็นรูปแบบพิเศษไม่ใช่มาโครแสดงให้เห็นว่า
KIM Taegyoon

142
(set ls '(1 2 3 4)) => Error - ls has no value

(set 'ls '(1 2 3 4)) => OK

(setq ls '(1 2 3 4)) => OK - make ls to (quote ls) and then have the usual set

(setf ls '(1 2 3 4)) => OK - same as setq so far BUT

(setf (car ls) 10) => Makes ls '(10 2 3 4) - not duplicated by setq/set

12
ฉันพบว่าคำตอบของคุณชัดเจนกว่าคำตอบที่ได้รับการโหวตอันดับต้น ๆ ขอบคุณมาก.
CDR

2
@ อย่างน้อยโปรดอย่าใช้ตัวอักษร "l" (ell) เป็นตัวแปรหรือสัญลักษณ์ในโค้ดตัวอย่าง มันยากเกินไปที่จะแยกความแตกต่างทางสายตาจากตัวเลข 1
DavidBooth

ไม่ฉันยังไม่เข้าใจว่า (car ls) สามารถเป็น l-value ได้หรือไม่ คุณเข้าใจวิธีการแปล CLisp เป็น C หรือไม่? และวิธีการเขียนล่าม CLisp?
ครั้ง

@ user1952009 clisp เป็นการใช้งาน Common LISP หนึ่งรายการ หากคุณต้องการอ้างถึงภาษานั้นเอง CL คือคำย่อที่ใช้มากที่สุด
ssice

2
@ user1952009 หลังจาก(setq ls '(((1)))), (setf (car (car (car ls))) 5)เป็นพฤติกรรมที่ไม่ได้กำหนดเพราะค่าของlsเป็นค่าคงที่ (เช่นการปรับเปลี่ยนตัวอักษรสตริงใน C) หลังจาก(setq ls (list (list (list 1)))), (setf (car (car (car ls))) 5)ทำงานเช่นเดียวกับls->val->val->val = 5ใน C.
ไคล์

21

setqเป็นเช่นเดียวsetกับหาเรื่องแรกที่ยกมา - เป็นเหมือน(set 'foo '(bar baz)) ในทางกลับกันมันบอบบางจริงๆ - มันเหมือนกับ "อ้อม" ฉันแนะนำhttp://www.nano.com/lisp/cmucl-tutorials/LISP-tutorial-16.htmlเป็นวิธีที่ดีกว่าในการเริ่มต้นทำความเข้าใจมากกว่าคำตอบใด ๆ ที่นี่สามารถให้ ... ในระยะสั้นแม้ว่าจะใช้เวลา อาร์กิวเมนต์แรกเป็น "อ้างอิง" เพื่อที่จะทำงาน (เป็น ARG แรก) เพื่อตั้งค่ารายการภายในอาร์เรย์(setq foo '(bar baz))setfsetf(aref myarray 3)setf


1
เหมาะสมที่สุดสำหรับชื่อ setq จำง่าย ขอบคุณ
CDR

17

คุณสามารถใช้setfแทนsetหรือsetqไม่ใช้ในทางกลับกันได้เนื่องจากsetfยังสามารถกำหนดค่าขององค์ประกอบแต่ละส่วนของตัวแปรได้หากตัวแปรนั้นมีองค์ประกอบของแต่ละบุคคล ดู exaples ด้านล่าง:

ตัวอย่างทั้งสี่จะกำหนดรายการ (1, 2, 3) ให้กับตัวแปรชื่อ foo

(set (quote foo) (list 1 2 3))    ;foo => (1 2 3)
(1 2 3)

(set 'foo '(1 2 3))   ;foo => (1 2 3) same function, simpler expression
(1 2 3)

(setq foo '(1 2 3))   ;foo => (1 2 3) similar function, different syntax
(1 2 3)

(setf foo '(1 2 3))   ;foo => (1 2 3) more capable function
(1 2 3)

setfมีความสามารถเพิ่มของการตั้งค่าสมาชิกของรายการในfooค่าใหม่

foo                   ;foo => (1 2 3) as defined above
(1 2 3)

(car foo)             ;the first item in foo is 1
1

(setf (car foo) 4)    ;set or setq will fail since (car foo) is not a symbol
4

foo                   ;the fist item in foo was set to 4 by setf
(4 2 3)

อย่างไรก็ตามคุณสามารถกำหนดมาโครสัญลักษณ์ที่แสดงรายการเดียวภายใน foo

(define-symbol-macro foo-car (car foo))    ; assumes FOO => (1 2 3)
FOO-CAR

foo-car               ;foo-car is now a symbol for the 1st item in foo
1

(setq foo-car 4)      ;set or setq can set the symbol foo-car 
4

foo                   ;Lisp macros are so cool
(4 2 3)

คุณสามารถใช้defvarถ้าคุณยังไม่ได้กำหนดตัวแปรและไม่ต้องการให้มันมีค่าจนกระทั่งในภายหลังในรหัสของคุณ

(defvar foo2)
(define-symbol-macro foo-car (car foo2))

13

หนึ่งสามารถคิดSETและSETQการสร้างระดับต่ำ

  • SET สามารถกำหนดค่าของสัญลักษณ์

  • SETQ สามารถกำหนดค่าของตัวแปร

แล้วก็ SETFเป็นแมโครซึ่งมีการตั้งค่าหลายอย่าง: สัญลักษณ์ตัวแปรองค์ประกอบอาเรย์สล็อตอินสแตนซ์ ...

สำหรับสัญลักษณ์และตัวแปรหนึ่งสามารถคิดเป็นถ้าSETFขยายเข้าไปในและSETSETQ

* (macroexpand '(setf (symbol-value 'a) 10))

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

ดังนั้นSETและSETQจะใช้ในการใช้งานฟังก์ชั่นบางอย่างของSETFซึ่งเป็นโครงสร้างทั่วไปมากขึ้น คำตอบอื่น ๆ บางคำบอกเล่าเรื่องราวที่ซับซ้อนมากขึ้นเล็กน้อยเมื่อเราคำนึงถึงมาโครสัญลักษณ์


4

ฉันต้องการเพิ่มคำตอบก่อนหน้านี้ว่า setf เป็นมาโครที่เรียกใช้ฟังก์ชันเฉพาะขึ้นอยู่กับสิ่งที่ถูกส่งผ่านเป็นอาร์กิวเมนต์แรก เปรียบเทียบผลลัพธ์ของการขยายแมโครของ setf กับอาร์กิวเมนต์ชนิดต่าง ๆ :

(macroexpand '(setf a 1))

(macroexpand '(setf (car (list 3 2 1)) 1))

(macroexpand '(setf (aref #(3 2 1) 0) 1))

สำหรับอาร์กิวเมนต์บางประเภท "setf function" จะถูกเรียก:

(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.