อะไรคือความแตกต่างระหว่าง eq?, eqv ?, เท่ากับ? และ = ใน Scheme?


87

ฉันสงสัยว่าความแตกต่างระหว่างการดำเนินการเหล่านั้นใน Scheme คืออะไร ฉันเคยเห็นคำถามที่คล้ายกันใน Stack Overflow แต่เป็นคำถามเกี่ยวกับ Lisp และไม่มีการเปรียบเทียบระหว่างตัวดำเนินการสามตัว

ฉันกำลังเขียนคำสั่งประเภทต่างๆใน Scheme และฉันได้รับผลลัพธ์ต่อไปนี้:

(eq? 5 5) -->#t
(eq? 2.5 2.5) -->#f
(equal? 2.5 2.5) --> #t
(= 2.5 2.5) --> #t

เหตุใดจึงเป็นเช่นนี้


3
และยังมีeqv?ซึ่งหมายถึงสิ่งที่แตกต่างจากeq?หรือequal?
newacct

คำตอบ:


160

ฉันจะตอบคำถามนี้ทีละน้อย เริ่มต้นด้วยเพรดิเคต=สมมูล เพรดิเคต=ใช้เพื่อตรวจสอบว่าตัวเลขสองตัวเท่ากันหรือไม่ หากคุณจัดหาสิ่งอื่นใดนอกจากตัวเลขจะทำให้เกิดข้อผิดพลาด:

(= 2 3)     => #f
(= 2.5 2.5) => #t
(= '() '()) => error

เพรดิเคตeq?ใช้เพื่อตรวจสอบว่าพารามิเตอร์สองตัวแสดงถึงวัตถุเดียวกันในหน่วยความจำหรือไม่ ตัวอย่างเช่น:

(define x '(2 3))
(define y '(2 3))
(eq? x y)         => #f
(define y x)
(eq? x y)         => #t

อย่างไรก็ตามโปรดทราบว่ามีรายการว่างเพียงรายการเดียว'()ในหน่วยความจำ (จริงๆแล้วรายการว่างไม่มีอยู่ในหน่วยความจำ แต่ตัวชี้ไปยังตำแหน่งหน่วยความจำ0ถือเป็นรายการว่าง) ดังนั้นเมื่อเปรียบเทียบรายการว่างeq?จะกลับมาเสมอ#t(เนื่องจากเป็นตัวแทนของวัตถุเดียวกันในหน่วยความจำ):

(define x '())
(define y '())
(eq? x y)      => #t

ตอนนี้ขึ้นอยู่กับการนำไปใช้eq?หรืออาจไม่คืน#tค่าดั้งเดิมเช่นตัวเลขสตริง ฯลฯ ตัวอย่างเช่น:

(eq? 2 2)     => depends upon the implementation
(eq? "a" "a") => depends upon the implementation

นี่คือจุดที่เพรดิเคตเข้าeqv?มาในรูปภาพ ค่าeqv?นี้เหมือนกับเพรดิเคตทุกประการeq?ยกเว้นว่าจะกลับมา#tเป็นค่าดั้งเดิมที่เหมือนกันเสมอ ตัวอย่างเช่น:

(eqv? 2 2)     => #t
(eqv? "a" "a") => depends upon the implementation

ดังนั้นจึงeqv?เป็น superset eq?และสำหรับกรณีส่วนใหญ่คุณควรใช้eqv?แทนeq?แทน

ในที่สุดเราก็มาถึงเพequal?รดิเคต เพรดิเคตequal?จะเหมือนกับเพรดิเคตทุกประการeqv?ยกเว้นว่าสามารถใช้เพื่อทดสอบว่าทั้งสองรายการเวกเตอร์ ฯลฯ มีองค์ประกอบที่สอดคล้องกันซึ่งตรงตามเพรดิเคตeqv?หรือไม่ ตัวอย่างเช่น:

(define x '(2 3))
(define y '(2 3))
(equal? x y)      => #t
(eqv? x y)        => #f

โดยทั่วไป:

  1. ใช้เพรดิเคต=เมื่อคุณต้องการทดสอบว่าตัวเลขสองตัวเท่ากันหรือไม่
  2. ใช้เพรดิเคตeqv?เมื่อคุณต้องการทดสอบว่าค่าที่ไม่ใช่ตัวเลขสองค่าเท่ากันหรือไม่
  3. ใช้เพรดิเคตequal?เมื่อคุณต้องการทดสอบว่าสองรายการเวกเตอร์และอื่น ๆ เท่ากันหรือไม่
  4. อย่าใช้เพรดิเคตeq?เว้นแต่คุณจะรู้แน่ชัดว่ากำลังทำอะไรอยู่

7
AFAIK (eqv? "a" "a") ==> unspecified. คุณจะต้องใช้equal?หรือ (อาจมีการปรับให้เหมาะสมมากกว่านี้)string=?
Sylwester

3
ตามรายงาน , (eq? '(1) '(1))คือไม่ระบุรายละเอียดเพื่อให้คุณ(define x '(1 2))ภาพประกอบอาจจะไม่ทำงาน
Will Ness

4
ถูกต้องและให้ข้อมูลมาก โดยเฉพาะแนวทางในตอนท้าย.
Germán Diago

2
แต่ eq? ดูเหมือนจะถูกกำหนดสำหรับสัญลักษณ์และควรสังเกต! ถ้าสัญลักษณ์เหมือนกัน eq? ผลตอบแทน #t. ตัวอย่าง(eq? 'foo 'foo) -> #t, (eq? 'foo 'bar)-> เท็จ ฉันอ่านที่นี่และที่นี่
Nedko

13

มีสองหน้าเต็มในข้อกำหนด RnRS ที่เกี่ยวข้องกับeq?, eqv?, equal? and =. นี่คือร่าง R7RS ข้อมูลจำเพาะ ลองดูสิ!

คำอธิบาย:

  • = เปรียบเทียบตัวเลข 2.5 และ 2.5 มีค่าเท่ากัน
  • equal? สำหรับตัวเลขลดเป็น = 2.5 และ 2.5 จะมีค่าเท่ากัน
  • eq?เปรียบเทียบ 'ตัวชี้' หมายเลข 5 ในการดำเนินโครงการของคุณถูกนำไปใช้เป็น 'ทันที' (น่าจะเป็น) ดังนั้น 5 และ 5 จึงเหมือนกัน จำนวน 2.5 อาจต้องมีการจัดสรร 'บันทึกคะแนนลอยตัว' ในการใช้งาน Scheme ของคุณซึ่งตัวชี้ทั้งสองไม่เหมือนกัน

1
ลิงก์ไปยัง Draft R7RS Specification สิ้นสุดลงเมื่อวันที่ 2018-02-04
Jeremiah Peschka

2
อัปเดตเป็นลิงค์สด
GoZoner

10

eq?คือ#tเมื่อเป็นที่อยู่ / วัตถุเดียวกัน โดยปกติเราสามารถคาดหวัง #t สำหรับสัญลักษณ์บูลีนและอ็อบเจ็กต์เดียวกันและ #f สำหรับค่าที่อยู่คนละประเภทโดยมีค่าต่างกันหรือไม่ก็โครงสร้างเดียวกัน Scheme / Lisp-Implementations มีประเพณีในการฝังประเภทในตัวชี้และการฝัง ค่าในช่องว่างเดียวกันหากมีพื้นที่เพียงพอ ดังนั้นคำแนะนำบางส่วนจริงๆไม่ได้อยู่ แต่ค่าเช่นถ่านRหรือ 10Fixnum สิ่งเหล่านี้จะเกิดขึ้นeq?เนื่องจาก "ที่อยู่" เป็นประเภท + ค่าที่ฝังอยู่ การใช้งานบางอย่างยังนำค่าคงที่ไม่เปลี่ยนรูปมาใช้ซ้ำ (eq? '(1 2 3)' (1 2 3)) อาจเป็น #f เมื่อตีความ แต่ #t เมื่อคอมไพล์เนื่องจากอาจได้รับที่อยู่เดียวกัน (เช่นเดียวกับพูล String คงที่ใน Java) ด้วยเหตุนี้การเปิดเผยจำนวนมากที่เกี่ยวข้องeq? ไม่ได้ระบุไว้ดังนั้นการประเมินว่า #t หรือ #f ขึ้นอยู่กับการใช้งาน

eqv?เป็น # สำหรับสิ่งเดียวกับeq?. นอกจากนี้ยังเป็น # ถ้าเป็นตัวเลขหรืออักขระและค่าของมันจะเหมือนกันแม้ว่าข้อมูลจะใหญ่เกินกว่าที่จะใส่ลงในตัวชี้ได้ ดังนั้นสำหรับสิ่งเหล่าeqv?นี้การตรวจสอบประเภทดังกล่าวจึงเป็นหนึ่งในสิ่งที่ได้รับการสนับสนุนซึ่งทั้งสองเป็นประเภทเดียวกันและวัตถุเป้าหมายมีค่าข้อมูลเท่ากัน

equal?คือ #t สำหรับสิ่งเดียวกันeqv?และถ้าเป็นประเภทผสมเช่นคู่เวกเตอร์สตริงและไบต์เวกเตอร์ที่ทำซ้ำequal?กับส่วนต่างๆ ในทางปฏิบัติก็จะกลับ #t ถ้าวัตถุทั้งสองลักษณะเดียวกัน ก่อนหน้า R6RS ไม่ปลอดภัยที่จะใช้equal?กับโครงสร้างวงกลม

=เหมือนeqv?แต่ใช้ได้กับประเภทตัวเลขเท่านั้น มันอาจจะมีประสิทธิภาพมากขึ้น

string=?เหมือนequal?แต่ใช้ได้กับสตริงเท่านั้น มันอาจจะมีประสิทธิภาพมากขึ้น


6

equal? เปรียบเทียบสองวัตถุซ้ำ ๆ (ทุกประเภท) เพื่อความเท่าเทียมกัน

  • โปรดทราบว่าสิ่งนี้อาจมีราคาแพงสำหรับโครงสร้างข้อมูลขนาดใหญ่เนื่องจากอาจต้องข้ามรายการสตริงเวกเตอร์และอื่น ๆ ทั้งหมด

  • ถ้าวัตถุมีเพียงองค์ประกอบเดียว (EG: หมายเลขตัวละคร ฯลฯ ) eqv?นี้เป็นเช่นเดียวกับ


eqv? ทดสอบวัตถุสองชิ้นเพื่อตรวจสอบว่าทั้งสอง "ปกติถือว่าเป็นวัตถุเดียวกัน" หรือไม่

  • eqv?และeq?มีการดำเนินการที่คล้ายคลึงกันมากและความแตกต่างระหว่างการดำเนินการเหล่านี้จะมีความเฉพาะเจาะจงในการนำไปใช้งาน

eq?เหมือนกับeqv?แต่อาจสามารถแยกแยะความแตกต่างที่ละเอียดกว่าได้และอาจนำไปใช้งานได้อย่างมีประสิทธิภาพมากขึ้น

  • eqv?ตามสเปคนี้อาจจะมีการดำเนินการเป็นไปอย่างรวดเร็วและมีประสิทธิภาพการเปรียบเทียบตัวชี้เมื่อเทียบกับการดำเนินงานที่มีความซับซ้อนมากขึ้นสำหรับ


= เปรียบเทียบตัวเลขเพื่อความเท่าเทียมกันของตัวเลข

  • โปรดทราบว่าสามารถระบุตัวเลขได้มากกว่าสองหมายเลขเช่น: (= 1 1.0 1/1 2/2)

ฉันคิดว่าeq?เป็นความเท่าเทียมกันของตัวชี้ที่แท้จริง (ไม่ใช่eqv?) เป็น "สิ่งที่ดีที่สุดหรือเลือกปฏิบัติที่สุด" เช่น(eqv? 2 2)รับประกันว่าจะเป็น#tแต่(eq? 2 2)"ไม่ระบุ" กล่าวคือขึ้นอยู่กับว่าการนำไปใช้สร้างอ็อบเจ็กต์หน่วยความจำใหม่จริงสำหรับแต่ละหมายเลขที่อ่านใหม่หรือใช้ซ้ำที่สร้างไว้ก่อนหน้านี้หากทำได้
Will Ness

@ WillNess - จับได้ดีขอบคุณ ความแตกต่างระหว่างeq?และeqv?ละเอียดอ่อนมากกว่าการดำเนินการอื่น ๆ
Justin Ethier

5

คุณไม่ได้กล่าวถึงการใช้งานโครงการ แต่ใน Racket eq?จะส่งคืนค่าจริงหากอาร์กิวเมนต์อ้างถึงวัตถุเดียวกันเท่านั้น ตัวอย่างที่สองของคุณคือการให้ #f เนื่องจากระบบกำลังสร้างเลขทศนิยมใหม่สำหรับแต่ละอาร์กิวเมนต์ ไม่ใช่วัตถุเดียวกัน

equal?และ=กำลังตรวจสอบความเท่าเทียมกันของค่า แต่=ใช้ได้กับตัวเลขเท่านั้น

หากคุณกำลังใช้ไม้ตรวจสอบที่นี่สำหรับข้อมูลเพิ่มเติม มิฉะนั้นให้ตรวจสอบเอกสารการดำเนินโครงการของคุณ


3
ยังดีกว่า ... อ่านสเปค ... r6rs.org/final/html/r6rs/r6rs-ZH-14.html#node_sec_11.5
Dirk

3

คิดว่าeq?เป็นความเท่าเทียมกันของตัวชี้ ผู้เขียนรายงานต้องการให้มีความกว้างมากที่สุดเท่าที่จะเป็นไปได้ดังนั้นพวกเขาจึงไม่พูดเรื่องนี้โดยสิ้นเชิงเพราะขึ้นอยู่กับการนำไปใช้งานและหากจะกล่าวว่ามันจะให้ความสำคัญกับการใช้งานตามตัวชี้ แต่พวกเขาพูด

โดยปกติจะเป็นไปได้ที่จะใช้ eq? มีประสิทธิภาพมากกว่า eqv? ตัวอย่างเช่นการเปรียบเทียบตัวชี้อย่างง่าย

นี่คือสิ่งที่ฉันหมายถึง (eqv? 2 2)รับประกันคืนสินค้า#tแต่(eq? 2 2)ไม่ระบุ ลองนึกภาพการใช้งานตามตัวชี้ ในนั้นeq?เป็นเพียงการเปรียบเทียบตัวชี้ เนื่องจาก(eq? 2 2)ไม่ได้ระบุจึงหมายความว่าการใช้งานนี้มีอิสระที่จะสร้างการแสดงวัตถุหน่วยความจำใหม่ของแต่ละหมายเลขใหม่ที่อ่านจากซอร์สโค้ดeqv?ต้องตรวจสอบข้อโต้แย้งจริง

OTOH (eq 'a 'a)คือ#t. ซึ่งหมายความว่าการดำเนินการดังกล่าวจะต้องรู้จักสัญลักษณ์ที่มีชื่อซ้ำกันและใช้เหมือนกันหนึ่งวัตถุเป็นตัวแทนในหน่วยความจำสำหรับพวกเขาทั้งหมด

สมมติว่าการใช้งานไม่ได้ใช้ตัวชี้ ตราบใดที่เป็นไปตามรายงานก็ไม่สำคัญ ผู้เขียนไม่ต้องการถูกมองว่าเป็นการกำหนดลักษณะเฉพาะของการนำไปใช้กับผู้ปฏิบัติดังนั้นพวกเขาจึงเลือกใช้ถ้อยคำอย่างระมัดระวัง

นี่คือการคาดเดาของฉันอยู่แล้ว

อย่างหยาบมากeq?คือความเท่าเทียมกันของตัวชี้eqv?คือ (อะตอม -) การรับรู้ค่าequal?ยังตระหนักถึงโครงสร้าง (ตรวจสอบอาร์กิวเมนต์ซ้ำเพื่อให้ในที่สุดก็(equal? '(a) '(a))จำเป็นต้องเป็น#t) =เป็นตัวเลขstring=?สำหรับสตริงและรายละเอียดคือ ในรายงาน


0

นอกเหนือจากคำตอบก่อนหน้านี้ฉันจะเพิ่มความคิดเห็น

เพรดิเคตทั้งหมดเหล่านี้ต้องการกำหนดฟังก์ชันนามธรรมของ identityอ็อบเจ็กต์ แต่อยู่ในบริบทที่ต่างกัน

EQ?ขึ้นอยู่กับการนำไปใช้งานและไม่ได้ตอบคำถามare 2 objects the same?ในการใช้งานที่ จำกัด เท่านั้น จากมุมมองการนำไปใช้เพรดิเคตนี้เพียงแค่เปรียบเทียบตัวเลข 2 ตัว (ตัวชี้กับวัตถุ) มันไม่ได้ดูที่เนื้อหาของวัตถุ ตัวอย่างเช่นหากการใช้งานของคุณไม่ได้เก็บสตริงไว้ภายในโดยเฉพาะ แต่จัดสรรหน่วยความจำที่แตกต่างกันสำหรับแต่ละสตริงก็(eq? "a" "a")จะเป็นเท็จ

EQV?- สิ่งนี้ดูภายในวัตถุ แต่มีการใช้งานที่ จำกัด (eqv? (lambda(x) x) (lambda(x) x))มันคือการดำเนินการขึ้นอยู่กับผลตอบแทนที่ได้ถ้ามันเป็นจริงสำหรับ นี่คือปรัชญาเต็มรูปแบบในการกำหนดเพรดิเคตนี้อย่างที่เราทราบกันดีในปัจจุบันว่ามีวิธีการที่รวดเร็วในการเปรียบเทียบการทำงานของฟังก์ชันบางอย่างโดยมีการใช้งานที่ จำกัด แต่eqv?ให้คำตอบที่สอดคล้องกันสำหรับตัวเลขขนาดใหญ่สตริง ฯลฯ

ในทางปฏิบัติเพรดิเคตเหล่านี้บางส่วนพยายามใช้นิยามนามธรรมของวัตถุ (ทางคณิตศาสตร์) ในขณะที่คนอื่น ๆ ใช้การเป็นตัวแทนของวัตถุ (วิธีการนำไปใช้บนเครื่องจริง) คำจำกัดความทางคณิตศาสตร์ของอัตลักษณ์มาจาก Leibniz และกล่าวว่า:

X = Y  iff  for any P, P(X) = P(Y)
X, Y being objects and
P being any property associated with object X and Y.

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

ลองจินตนาการถึงนิยามเชิงนามธรรมของตัวตนเพื่อความต่อเนื่อง แม้ว่าคุณจะสามารถให้คำจำกัดความของชุดย่อยของฟังก์ชันได้ ( คลาสฟังก์ชันแบบเรียกซ้ำของซิกมา ) แต่ภาษาก็ไม่ได้กำหนดให้เพรดิเคตเป็นจริงหรือเท็จ มันจะซับซ้อนมากทั้งคำจำกัดความของภาษาและการใช้งานอื่น ๆ อีกมากมาย

บริบทสำหรับเพรดิเคตอื่น ๆ นั้นวิเคราะห์ได้ง่ายกว่า

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