Sự khác biệt giữa eq ?, eqv ?, bằng ?, và = trong Scheme?


85

Tôi tự hỏi sự khác biệt giữa các hoạt động đó trong Scheme là gì. Tôi đã thấy các câu hỏi tương tự trong Stack Overflow nhưng chúng là về Lisp và không có sự so sánh giữa ba trong số các toán tử đó.

Tôi đang viết các loại lệnh khác nhau trong Scheme và tôi nhận được kết quả đầu ra sau:

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

Tại sao điều này là trường hợp?


3
và cũng có eqv?, có nghĩa là một cái gì đó khác với eq?hoặcequal?
newacct

Câu trả lời:


155

Tôi sẽ trả lời câu hỏi này dần dần. Hãy bắt đầu với =vị từ tương đương. Vị =từ được sử dụng để kiểm tra xem hai số có bằng nhau hay không. Nếu bạn cung cấp cho nó bất kỳ thứ gì khác ngoài một số thì nó sẽ phát sinh lỗi:

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

Vị eq?từ được sử dụng để kiểm tra xem hai tham số của nó có đại diện cho cùng một đối tượng trong bộ nhớ hay không. Ví dụ:

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

Tuy nhiên, lưu ý rằng chỉ có một danh sách trống '()trong bộ nhớ (thực tế danh sách trống không tồn tại trong bộ nhớ, nhưng một con trỏ đến vị trí bộ nhớ 0được coi là danh sách trống). Do đó, khi so sánh các danh sách trống eq?sẽ luôn trả về #t(vì chúng đại diện cho cùng một đối tượng trong bộ nhớ):

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

Bây giờ tùy thuộc vào việc triển khai eq?có thể trả về hoặc không trả về #tcác giá trị nguyên thủy như số, chuỗi, v.v. Ví dụ:

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

Đây là nơi eqv?vị từ đi vào hình ảnh. Giống eqv?hệt như eq?vị từ, ngoại trừ việc nó sẽ luôn trả về #tcác giá trị nguyên thủy giống nhau. Ví dụ:

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

Do đó, eqv?là một tập hợp siêu eq?và đối với hầu hết các trường hợp, bạn nên sử dụng eqv?thay thế eq?.

Cuối cùng chúng ta đến equal?vị ngữ. Vị equal?từ hoàn toàn giống với eqv?vị từ, ngoại trừ việc nó cũng có thể được sử dụng để kiểm tra xem hai danh sách, vectơ, v.v. có các phần tử tương ứng thỏa mãn eqv?vị từ hay không. Ví dụ:

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

Nói chung:

  1. Sử dụng =vị từ khi bạn muốn kiểm tra xem hai số có tương đương nhau không.
  2. Sử dụng eqv?vị từ khi bạn muốn kiểm tra xem hai giá trị không phải số có tương đương hay không.
  3. Sử dụng equal?vị từ khi bạn muốn kiểm tra xem hai danh sách, vectơ, v.v. có tương đương nhau hay không.
  4. Không sử dụng eq?vị ngữ trừ khi bạn biết chính xác mình đang làm gì.

7
THÔI (eqv? "a" "a") ==> unspecified. Bạn sẽ phải sử dụng equal?hoặc (càng có thể được tối ưu hóa)string=?
Sylwester

3
theo Báo cáo , (eq? '(1) '(1))không xác định , vì vậy (define x '(1 2))hình minh họa của bạn có thể không hoạt động.
Will Ness,

4
Rất chính xác và nhiều thông tin. Đặc biệt là các hướng dẫn ở cuối.
Germán Diago,

2
Nhưng eq? dường như được xác định cho các ký hiệu và điều này cần được lưu ý! Nếu các ký hiệu trông giống nhau, eq? trả về #t. Ví dụ (eq? 'foo 'foo) -> #t, (eq? 'foo 'bar)-> false`. Tôi đọc cái này ở đâyở đây
Nedko

13

Có đầy đủ hai trang trong đặc tả RnRS liên quan đến eq?, eqv?, equal? and =. Đây là Thông số R7RS Dự thảo . Kiểm tra nó ra!

Giải trình:

  • = so sánh các số, 2,5 và 2,5 là số bằng nhau.
  • equal?đối với các số giảm xuống =, 2,5 và 2,5 là số bằng nhau.
  • eq?so sánh 'con trỏ'. Số 5, trong việc thực hiện Đề án của bạn, được thực hiện như là một 'ngay lập tức' (có thể), do đó 5 và 5 giống hệt nhau. Số 2,5 có thể yêu cầu phân bổ 'bản ghi dấu phẩy động' trong việc triển khai Đề án của bạn, hai con trỏ không giống nhau.

1
Liên kết đến Đặc điểm kỹ thuật R7RS Dự thảo đã chết kể từ
Jeremiah Peschka

2
Đã cập nhật thành liên kết trực tiếp.
GoZoner

10

eq?#tkhi nó là cùng một địa chỉ / đối tượng. Thông thường, người ta có thể mong đợi #t cho cùng một biểu tượng, boolean và đối tượng và #f cho các giá trị có kiểu khác nhau, với các giá trị khác nhau hoặc không cùng cấu trúc Các triển khai Scheme / Lisp có truyền thống nhúng kiểu vào con trỏ của chúng và nhúng các giá trị trong cùng một không gian nếu đủ dung lượng. Vì vậy, một số con trỏ thực sự không phải là địa chỉ mà là giá trị, như char Rhoặc Fixnum 10. Đây sẽ là eq?vì "địa chỉ" là một loại + giá trị được nhúng. Một số triển khai cũng sử dụng lại các hằng số bất biến. (eq? '(1 2 3)' (1 2 3)) có thể là #f khi được diễn giải nhưng #t khi được biên dịch vì nó có thể có cùng một địa chỉ. (Giống như nhóm chuỗi không đổi trong Java). Do đó, nhiều cuộc thám hiểm liên quan đếneq? là không xác định, do đó, nó đánh giá thành #t hoặc #f là tùy thuộc vào việc triển khai.

eqv?là #t cho những thứ tương tự như eq?. Nó cũng là #t nếu đó là một số hoặc ký tự và giá trị của nó giống nhau , ngay cả khi dữ liệu quá lớn để vừa với một con trỏ. Vì vậy, đối với những thứ eqv?đó, công việc bổ sung là kiểm tra xem kiểu đó có phải là một trong những kiểu được hỗ trợ hay không, rằng cả hai đều là cùng một kiểu và các đối tượng đích của nó có cùng giá trị dữ liệu.

equal?là #t cho những thứ tương tự eqv?và nếu đó là một kiểu phức hợp như cặp, vectơ, chuỗi và bytevector thì nó thực hiện một cách đệ quy equal?với các phần. Trong thực tế, nó sẽ trả về #t nếu hai đối tượng trông giống nhau . Trước R6RS, nó không an toàn khi sử dụng equal?trên các cấu trúc hình tròn.

=giống như eqv?nhưng nó chỉ hoạt động đối với các kiểu số . Nó có thể hiệu quả hơn.

string=?giống như equal?, nhưng nó chỉ hoạt động đối với chuỗi. Nó có thể hiệu quả hơn.


6

equal? so sánh đệ quy hai đối tượng (thuộc bất kỳ kiểu nào) để bằng nhau.

  • Lưu ý rằng điều này có thể tốn kém đối với cấu trúc dữ liệu lớn vì có thể toàn bộ danh sách, chuỗi, vectơ, v.v. phải được duyệt qua.

  • Nếu đối tượng chỉ chứa một phần tử duy nhất (EG: số, ký tự, v.v.), thì điều này giống như eqv?.


eqv? kiểm tra hai đối tượng để xác định xem cả hai đều "thường được coi là cùng một đối tượng".

  • eqv?eq?là các hoạt động rất giống nhau, và sự khác biệt giữa chúng sẽ là phần thực hiện cụ thể.

eq?giống như eqv?nhưng có thể phân biệt rõ hơn và có thể được thực hiện hiệu quả hơn.

  • Theo thông số kỹ thuật, điều này có thể được thực hiện như một phép so sánh con trỏ nhanh chóng và hiệu quả, trái ngược với một hoạt động phức tạp hơn cho eqv?.


= so sánh các số để bình đẳng số.

  • Lưu ý rằng có thể cung cấp nhiều hơn hai số, ví dụ: (= 1 1.0 1/1 2/2)

Tôi nghĩ eq?là bình đẳng con trỏ thực tế (không phải eqv?). Đó là "tốt nhất hoặc phân biệt đối xử nhất". Ví dụ: (eqv? 2 2)được bảo đảm là được #t, nhưng (eq? 2 2)là "không xác định". Tức là nó phụ thuộc vào việc một triển khai tạo đối tượng bộ nhớ mới thực sự cho mỗi số mới được đọc hay sử dụng lại một đối tượng đã tạo trước đó nếu có thể.
Will Ness

@WillNess - Bắt tốt, cảm ơn. Sự khác biệt giữa eq?eqv?là tinh tế hơn các hoạt động khác.
Justin Ethier

5

Bạn không đề cập đến việc triển khai lược đồ, nhưng trong Racket, eq?chỉ trả về true nếu các đối số tham chiếu đến cùng một đối tượng. Ví dụ thứ hai của bạn là kết quả #f vì hệ thống đang tạo một số dấu phẩy động mới cho mỗi đối số; chúng không cùng một đối tượng.

equal?=đang kiểm tra sự tương đương về giá trị, nhưng =chỉ áp dụng cho các số.

Nếu bạn đang sử dụng Vợt, hãy kiểm tra tại đây để biết thêm thông tin. Nếu không, hãy kiểm tra tài liệu về việc triển khai chương trình của bạn.


3
Tốt hơn nữa ... Đọc thông số kỹ thuật ... r6rs.org/final/html/r6rs/r6rs-ZH-14.html#node_sec_11.5
Dirk

3

Coi eq?như bình đẳng con trỏ. Các tác giả của Báo cáo muốn nó càng chung chung càng tốt để họ không nói thẳng điều này vì nó phụ thuộc vào việc triển khai và nói như vậy, sẽ ủng hộ việc triển khai dựa trên con trỏ. Nhưng họ nói

Nó thường sẽ có thể thực hiện eq? hiệu quả hơn nhiều so với eqv ?, chẳng hạn như một phép so sánh con trỏ đơn giản

Đây là những gì tôi muốn nói. (eqv? 2 2)được đảm bảo trả lại #tnhưng (eq? 2 2)không xác định. Bây giờ hãy tưởng tượng một triển khai dựa trên con trỏ. Trong đó eq?chỉ là so sánh con trỏ. Vì (eq? 2 2)là không xác định, có nghĩa là việc triển khai này là miễn phí chỉ cần tạo biểu diễn đối tượng bộ nhớ mới của mỗi số mới mà nó đọc từ mã nguồn. eqv?phải thực sự kiểm tra các lập luận của nó.

OTOH (eq 'a 'a)#t. Điều này có nghĩa là việc triển khai như vậy phải nhận ra các ký hiệu có tên trùng lặp và sử dụng cùng một đối tượng biểu diễn trong bộ nhớ cho tất cả chúng.

Giả sử một triển khai không dựa trên con trỏ. Miễn là nó tuân thủ Báo cáo, điều đó không quan trọng. Các tác giả chỉ không muốn bị coi là người ra lệnh chi tiết cụ thể của việc triển khai cho người triển khai, vì vậy họ lựa chọn từ ngữ của mình một cách cẩn thận.

Đây là suy đoán của tôi dù sao.

Vì vậy, rất thô, eq?là đẳng thức con trỏ, eqv?là (nguyên tử-) nhận biết giá trị, equal?cũng là nhận biết cấu trúc (kiểm tra các đối số của nó một cách đệ quy, vì vậy cuối cùng (equal? '(a) '(a))là bắt buộc phải có #t), =là đối với số, string=?đối với chuỗi và chi tiết là trong Báo cáo.


0

Ngoài các câu trả lời trước, tôi sẽ thêm một số nhận xét.

Tất cả các vị từ này muốn xác định chức năng trừu tượng của identitymột đối tượng nhưng trong các ngữ cảnh khác nhau.

EQ?phụ thuộc vào việc triển khai và nó không trả lời câu hỏi are 2 objects the same?chỉ trong trường hợp sử dụng hạn chế. Từ quan điểm thực hiện, vị từ này chỉ so sánh 2 số (con trỏ với các đối tượng), nó không xem xét nội dung của các đối tượng. Vì vậy, ví dụ: nếu việc triển khai của bạn không duy nhất giữ các chuỗi bên trong mà phân bổ bộ nhớ khác nhau cho mỗi chuỗi, thì (eq? "a" "a")sẽ là false.

EQV?- cái này nhìn bên trong các đối tượng, nhưng với việc sử dụng hạn chế. Nó phụ thuộc vào việc triển khai nếu nó trả về true for (eqv? (lambda(x) x) (lambda(x) x)). Đây là một triết lý đầy đủ về cách xác định vị từ này, như chúng ta biết ngày nay có một số phương pháp nhanh để so sánh chức năng của một số hàm, với việc sử dụng hạn chế. Nhưng eqv?cung cấp câu trả lời mạch lạc cho các số lớn, chuỗi, v.v.

Thực tế, một số vị từ này cố gắng sử dụng định nghĩa trừu tượng của một đối tượng (về mặt toán học), trong khi những vị từ khác sử dụng biểu diễn của một đối tượng (cách nó được triển khai trên một máy thực). Định nghĩa toán học về danh tính đến từ Leibniz và nó nói:

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.

Lý tưởng nhất là có thể triển khai định nghĩa này trên máy tính nhưng vì lý do khó xác định và / hoặc tốc độ, nó không được triển khai theo nghĩa đen. Đây là lý do tại sao có rất nhiều toán tử cố gắng tập trung vào các quan điểm khác nhau xung quanh định nghĩa này.

Hãy thử tưởng tượng định nghĩa trừu tượng của một danh tính để tiếp tục. Ngay cả khi bạn có thể cung cấp định nghĩa về một tập con các hàm ( lớp hàm đệ quy sigma ), ngôn ngữ này không áp đặt bất kỳ vị từ nào là đúng hay sai. Nó sẽ phức tạp hơn rất nhiều cả định nghĩa ngôn ngữ và nhiều hơn nữa trong việc triển khai.

Bối cảnh cho các vị từ khác dễ phân tích hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.