Scheme'de eq ?, eqv ?, equ ?, ve = arasındaki fark nedir?


87

Scheme'deki bu işlemler arasındaki farkın ne olduğunu merak ediyorum. Stack Overflow'da benzer sorular gördüm ama bunlar Lisp ile ilgili ve bu operatörlerin üçü arasında bir karşılaştırma yok.

Scheme'de farklı türde komutlar yazıyorum ve aşağıdaki çıktıları alıyorum:

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

Bu neden böyle?


3
ve orada da eqv?hangi farklı bir şey anlamına gelir eq?veyaequal?
newacct

Yanıtlar:


160

Bu soruyu adım adım cevaplayacağım. =Eşdeğerlik yüklemiyle başlayalım . =Yüklem iki sayı eşit olup olmadığını kontrol etmek için kullanılır. Bir sayı dışında bir şey verirseniz, o zaman bir hataya neden olur:

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

eq?Yüklem iki parametre hafızasında aynı nesne respresent olmadığını kontrol etmek için kullanılır. Örneğin:

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

Bununla birlikte, '()bellekte yalnızca bir boş liste olduğunu unutmayın (aslında boş liste bellekte yoktur, ancak bellek konumuna bir işaretçi 0boş liste olarak kabul edilir). Dolayısıyla, boş listeleri karşılaştırırken eq?her zaman geri dönecektir #t(çünkü bellekte aynı nesneyi temsil ederler):

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

Şimdi uygulamaya bağlı olarak sayılar, dizeler vb. Gibi ilkel değerler için eq?dönebilir veya dönmeyebilir #t. Örneğin:

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

Bu, eqv?yüklemin ortaya çıktığı yerdir . Her zaman aynı ilkel değerler için dönmesi dışında eqv?, eq?yüklem ile tamamen aynıdır #t. Örneğin:

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

Bu nedenle eqv?, bir üst kümesidir eq?ve çoğu durumda eqv?bunun yerine kullanmanız gerekir eq?.

Sonunda equal?yüklemeye geldik . equal?Öncül tam olarak aynı eqv?zamanda, iki listeleri, vektörler, vb tatmin elemanları ilgili olup olmadığını test etmek için kullanılabilir dışında yüklemi eqv?yüklemi. Örneğin:

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

Genel olarak:

  1. =İki sayının eşdeğer olup olmadığını test etmek istediğinizde yüklemi kullanın .
  2. eqv?İki sayısal olmayan değerin eşdeğer olup olmadığını test etmek istediğinizde yüklemi kullanın .
  3. equal?İki listenin, vektörün vb. Eşdeğer olup olmadığını test etmek istediğinizde yüklemi kullanın .
  4. eq?Tam olarak ne yaptığınızı bilmediğiniz sürece yüklemeyi kullanmayın .

7
AFAIK (eqv? "a" "a") ==> unspecified. equal?Veya (muhtemelen daha optimize edilmiş string=?
olanı

3
göre Raporu , (eq? '(1) '(1))olduğu belirtilmemiş senin böylece, (define x '(1 2))illüstrasyon çalışmayabilir.
Will Ness

4
Çok doğru ve bilgilendirici. Özellikle sondaki yönergeler.
Germán Diago

2
Ama eq? semboller için tanımlanmış gibi görünüyor ve bu not edilmelidir! Semboller aynı görünüyorsa, eq? #t döndürür. Örnek (eq? 'foo 'foo) -> #t, (eq? 'foo 'bar)-> false`. Bunu burada ve burada
Nedko

13

İle ilgili RnRS belirtiminde tam iki sayfa vardır eq?, eqv?, equal? and =. İşte Taslak R7RS Spesifikasyonu . Bunu kontrol et!

Açıklama:

  • = sayıları karşılaştırır, 2.5 ve 2.5 sayısal olarak eşittir.
  • equal?sayılar için =2,5 ve 2,5 sayısal olarak eşittir.
  • eq?'işaretçileri' karşılaştırır. Şema uygulamanızda 5 rakamı 'acil' (muhtemelen) olarak uygulanır, dolayısıyla 5 ve 5 aynıdır. 2.5 sayısı, Şema uygulamanızda bir 'kayan nokta kaydının' tahsisini gerektirebilir, iki işaretçi aynı değildir.

1
Taslak R7RS Spesifikasyonunun bağlantısı 2018-02-04 itibariyle son bulmuştur
Jeremiah Peschka

2
Canlı bir bağlantıya güncellendi.
GoZoner

10

eq?olan #taynı adres / nesne olduğunda. Normalde biri farklı değerlere, ya da değil, aynı yapıyla, başka türden olduğu değerleri için aynı sembol, boolean ve nesne ve #f için #t bekliyor olabilir Şeması / Lisp-uygulamaları kendi işaretçiler ve gömmek üzere embed türe geleneğe sahiptir yeterli alan varsa aynı alandaki değerler. Bu nedenle bazı işaretçiler gerçekten adres değil, char Rveya Fixnum gibi değerlerdir 10. Bunlar eq?, "adres" gömülü bir tür + değer olduğu için olacaktır. Bazı uygulamalar ayrıca değişmez sabitleri yeniden kullanır. (eq? '(1 2 3)' (1 2 3)) yorumlandığında #f ama aynı adresi alabileceğinden derlendiğinde #t olabilir. (Java'daki sabit Dize havuzu gibi). Bu nedenle, aşağıdakileri içeren birçok ifadeeq? #t veya #f olarak değerlendirilip değerlendirilmediği takdirde uygulamaya bağlıdır.

eqv?#t ile aynı şeyler içindir eq?. Veriler bir işaretçiye sığamayacak kadar büyük olsa bile, bir sayı veya karakterse ve değeri aynıysa #t'dir . Bu nedenle, bu eqv?türün desteklenen türlerden biri olduğunu, her ikisinin de aynı türde olduğunu ve hedef nesnelerinin aynı veri değerine sahip olduğunu kontrol etmek için fazladan iş yapanlar için .

equal?#t ile aynı şeyler içindir eqv?ve çift, vektör, dize ve bytevector gibi bileşik bir tür equal?ise, parçalarla yinelemeli olarak yapar . Pratikte, iki nesne aynı görünüyorsa #t döndürür . R6RS'den önce equal?, dairesel yapılarda kullanmak güvenli değildir .

=gibidir, eqv?ancak yalnızca sayısal türler için çalışır . Daha verimli olabilir.

string=?gibidir equal?, ancak yalnızca dizeler için çalışır. Daha verimli olabilir.


6

equal? Eşitlik için iki nesneyi (her türden) yinelemeli olarak karşılaştırır.

  • Bu, büyük bir veri yapısı için pahalı olabilir, çünkü potansiyel olarak tüm liste, dizi, vektör, vb. Üzerinden geçilmelidir.

  • Nesne yalnızca tek bir öğe içeriyorsa (EG: sayı, karakter vb.), Bu aynıdır eqv?.


eqv? her ikisinin de "normalde aynı nesne olarak görülüp görülmediğini" belirlemek için iki nesneyi test eder.

  • eqv?ve eq?çok benzer işlemlerdir ve aralarındaki farklar bir şekilde uygulamaya özgü olacaktır.

eq?ile aynıdır, eqv?ancak daha ince ayrımları ayırt edebilir ve daha verimli bir şekilde uygulanabilir.

  • Spesifikasyona göre, bu, daha karmaşık bir işlemin aksine, hızlı ve verimli bir işaretçi karşılaştırması olarak uygulanabilir eqv?.


= sayısal eşitlik için sayıları karşılaştırır.

  • İkiden fazla sayı sağlanabileceğini unutmayın, örneğin: (= 1 1.0 1/1 2/2)

eq?Gerçek işaretçi eşitliği olduğunu düşündüm (değil eqv?). "En iyi veya en ayırt edici" dir. Örneğin (eqv? 2 2)olması garantilidir #t, ancak (eq? 2 2)"belirtilmemiştir". Yani, bir uygulamanın her yeni okunan numara için gerçek yeni bellek nesnesi oluşturup oluşturmadığına veya mümkünse önceden oluşturulmuş olanı yeniden kullanmasına bağlıdır.
Will Ness

@WillNess - İyi yakaladım, teşekkürler. Aradaki farklar eq?ve eqv?diğer işlemlerden daha incedir.
Justin Ethier

5

Bir şema uygulamasından bahsetmiyorsunuz, ancak Racket'te, eq?yalnızca argümanlar aynı nesneye atıfta bulunursa true döndürür. İkinci örneğiniz #f sonucunu veriyor çünkü sistem her bağımsız değişken için yeni bir kayan nokta sayısı oluşturuyor; aynı nesne değiller.

equal?ve =değer denkliğini kontrol ediyor, ancak =yalnızca sayılar için geçerli.

Racket kullanıyorsanız, daha fazla bilgi için burayı kontrol edin . Aksi takdirde, plan uygulamanızın belgelerine bakın.


3
Daha da iyisi ... Spesifikasyonu okuyun ... r6rs.org/final/html/r6rs/r6rs-ZH-14.html#node_sec_11.5
Dirk

3

eq?Gösterge eşitliği olarak düşünün . Raporun yazarları, olabildiğince genel olmasını isterler, bu nedenle, uygulamaya bağlı olduğu için bunu doğrudan söylemiyorlar ve bunu söylemek, işaretçi tabanlı uygulamaları tercih ediyor. Ama derler ki

Genellikle eq uygulamak mümkün olacaktır? eqv'den çok daha verimli, örneğin basit bir işaretçi karşılaştırması olarak

İşte demek istediğim. (eqv? 2 2)geri dönmesi garantilidir #tancak (eq? 2 2)belirtilmemiştir. Şimdi işaretçi tabanlı bir uygulama hayal edin. İçinde eq?sadece işaretçi karşılaştırması var. Yana (eq? 2 2)belirtilmemiş ise, bu uygulama sadece kaynak kodundan okur her yeni sayının yeni bellek nesnesi gösterimini oluşturmak için serbest olduğu anlamına gelir. eqv?aslında argümanlarını incelemelidir.

OTOH (eq 'a 'a)olduğunu #t. Böyle uygulama adı aynı olan sembolleri tanımak ve aynı kullanması gerektiğini bu araçlar biri hepsi için bellekte temsil nesnesi.

Bir uygulamanın işaretçi tabanlı olmadığını varsayalım. Rapora bağlı kaldığı sürece önemli değil. Yazarlar, uygulamaların özelliklerini uygulayıcılara dikte ediyormuş gibi görülmek istemiyorlar, bu yüzden ifadelerini dikkatlice seçiyorlar.

Zaten benim tahminim bu.

Çok kabaca, eq?işaretçi eşitliği, eqv?(atomik-) değerlerin farkında, equal?aynı zamanda yapı farkında (argümanlarını yinelemeli olarak kontrol eder, böylece sonunda (equal? '(a) '(a))olması gerekir #t), =sayılar içindir, string=?dizeler içindir ve ayrıntılar Raporda.


0

Önceki cevapların dışında bazı yorumlar ekleyeceğim.

Tüm bu yüklemler identity, bir nesnenin soyut işlevini farklı bağlamlarda tanımlamak ister .

EQ?uygulamaya bağlıdır ve soruyu are 2 objects the same?yalnızca sınırlı kullanımda yanıtlamaz . Uygulama açısından, bu yüklem sadece 2 sayıyı karşılaştırır (nesne gösterici), nesnelerin içeriğine bakmaz. Bu nedenle, örneğin, uygulamanız dizeleri benzersiz bir şekilde içinde tutmuyorsa, ancak her dizge için farklı bellek ayırıyorsa, (eq? "a" "a")bu durumda yanlış olacaktır.

EQV?- bu nesnelerin içine bakar, ancak kullanımı sınırlıdır. İçin true dönerse, uygulamaya bağlıdır (eqv? (lambda(x) x) (lambda(x) x)). Bugünlerde bazı işlevlerin işlevselliğini sınırlı kullanımla karşılaştırmak için bazı hızlı yöntemler olduğunu bildiğimiz için, bu yüklemi nasıl tanımlayacağımız tam bir felsefe. Ancak eqv?büyük sayılar, dizeler vb. İçin tutarlı yanıt sağlar.

Pratik olarak, bu yüklemlerden bazıları bir nesnenin soyut tanımını (matematiksel olarak) kullanmaya çalışırken, diğerleri bir nesnenin temsilini (gerçek bir makinede nasıl uygulandığını) kullanır. Kimliğin matematiksel tanımı Leibniz'den geliyor ve şöyle diyor:

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.

İdeal olarak, bu tam da bu tanımı bilgisayarda uygulayabilmektir, ancak kararsızlık ve / veya hız nedeniyle tam anlamıyla uygulanmaz. Bu nedenle, her biri bu tanım etrafında farklı bakış açılarına odaklanmaya çalışan çok sayıda operatör var.

Bir devam için bir kimliğin soyut tanımını hayal etmeye çalışın. Bir işlev alt kümesinin tanımını sağlayabilseniz bile ( sigma-yinelemeli işlevler sınıfı ), dil herhangi bir yüklemi doğru veya yanlış olarak kabul etmez. Hem dilin tanımını hem de uygulamayı çok daha karmaşık hale getirecektir.

Diğer yüklemlerin bağlamını analiz etmek daha kolaydır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.