Eu me pergunto qual é a diferença entre essas operações no Scheme. Eu vi perguntas semelhantes no Stack Overflow, mas elas são sobre Lisp, e não há uma comparação entre três desses operadores.
Estou escrevendo os diferentes tipos de comandos no Scheme e obtenho as seguintes saídas:
(eq? 5 5) -->#t
(eq? 2.5 2.5) -->#f
(equal? 2.5 2.5) --> #t
(= 2.5 2.5) --> #t
Por que isso acontece?
functional-programming
scheme
yrazlik
fonte
fonte
eqv?
, o que significa algo diferente deeq?
ouequal?
Respostas:
Vou responder a essa pergunta aos poucos. Vamos começar com o
=
predicado de equivalência. O=
predicado é usado para verificar se dois números são iguais. Se você fornecer qualquer coisa além de um número, ele gerará um erro:(= 2 3) => #f (= 2.5 2.5) => #t (= '() '()) => error
O
eq?
predicado é usado para verificar se seus dois parâmetros representam o mesmo objeto na memória. Por exemplo:(define x '(2 3)) (define y '(2 3)) (eq? x y) => #f (define y x) (eq? x y) => #t
Observe, entretanto, que há apenas uma lista vazia
'()
na memória (na verdade, a lista vazia não existe na memória, mas um ponteiro para o local da memória0
é considerado a lista vazia). Portanto, ao comparar listas vaziaseq?
sempre retornará#t
(porque representam o mesmo objeto na memória):(define x '()) (define y '()) (eq? x y) => #t
Agora, dependendo da implementação
eq?
pode ou não retornar#t
para valores primitivos, como números, strings, etc. Por exemplo:(eq? 2 2) => depends upon the implementation (eq? "a" "a") => depends upon the implementation
É aqui que o
eqv?
predicado entra em cena. Oeqv?
é exatamente o mesmo que oeq?
predicado, exceto que sempre retornará#t
para os mesmos valores primitivos. Por exemplo:(eqv? 2 2) => #t (eqv? "a" "a") => depends upon the implementation
Portanto,
eqv?
é um superconjunto deeq?
e, na maioria dos casos, você deve usar emeqv?
vez deeq?
.Finalmente chegamos ao
equal?
predicado. Oequal?
predicado é exatamente o mesmo que oeqv?
predicado, exceto que também pode ser usado para testar se duas listas, vetores, etc. têm elementos correspondentes que satisfazem oeqv?
predicado. Por exemplo:(define x '(2 3)) (define y '(2 3)) (equal? x y) => #t (eqv? x y) => #f
Em geral:
=
predicado quando quiser testar se dois números são equivalentes.eqv?
predicado quando desejar testar se dois valores não numéricos são equivalentes.equal?
predicado quando quiser testar se duas listas, vetores, etc. são equivalentes.eq?
predicado, a menos que saiba exatamente o que está fazendo.fonte
(eqv? "a" "a") ==> unspecified
. Você terá que usarequal?
ou (o possivelmente mais otimizado)string=?
(eq? '(1) '(1))
é especificado , portanto sua(define x '(1 2))
ilustração pode não funcionar.(eq? 'foo 'foo) -> #t
,(eq? 'foo 'bar)
-> false`. Eu li isso aqui e aquiExistem duas páginas completas na especificação RnRS relacionadas a
eq?, eqv?, equal? and =
. Aqui está a especificação preliminar do R7RS . Confira!Explicação:
=
compara números, 2,5 e 2,5 são numericamente iguais.equal?
para números reduzidos=
, 2,5 e 2,5 são numericamente iguais.eq?
compara 'ponteiros'. O número 5, em sua implementação do Scheme, é implementado como um 'imediato' (provável), portanto, 5 e 5 são idênticos. O número 2,5 pode exigir uma alocação de um 'registro de ponto flutuante' em sua implementação de Scheme, os dois ponteiros não são idênticos.fonte
eq?
é#t
quando é o mesmo endereço / objeto. Normalmente, pode-se esperar #t para o mesmo símbolo, booleano e objeto e #f para valores que são de tipo diferente, com valores diferentes, ou não a mesma estrutura Scheme / Lisp-implementations tem uma tradição de incorporar tipo em seus ponteiros e incorporar valores no mesmo espaço se houver espaço suficiente. Assim, alguns ponteiros realmente não são endereços, mas valores, como o charR
ou o Fixnum10
. Isso aconteceráeq?
porque o "endereço" é um tipo + valor incorporado. Algumas implementações também reutilizam constantes imutáveis. (eq? '(1 2 3)' (1 2 3)) pode ser #f quando interpretado, mas #t quando compilado, pois pode obter o mesmo endereço. (Como o pool String constante em Java). Por causa disso, muitas expressões envolvendoeq?
não são especificados, portanto, se for avaliado como #t ou #f, depende da implementação.eqv?
são #t para as mesmas coisas queeq?
. Também é #t se for um número ou caractere e seu valor for o mesmo , mesmo quando os dados são muito grandes para caber em um ponteiro. Assim, para esseseqv?
, o trabalho extra de verificar se o tipo é um dos suportados, se ambos são do mesmo tipo e se os objetos de destino têm o mesmo valor de dados.equal?
é #t para as mesmas coisas queeqv?
e se for um tipo composto como par, vetor, string e bytevector que ele faz recursivamenteequal?
com as partes. Na prática, ele retornará #t se os dois objetos forem iguais . Antes do R6RS, não era seguro usarequal?
em estruturas circulares.=
é semelhante,eqv?
mas só funciona para tipos numéricos . Pode ser mais eficiente.string=?
é parecidoequal?
, mas só funciona para strings. Pode ser mais eficiente.fonte
equal?
compara recursivamente dois objetos (de qualquer tipo) para igualdade.Observe que isso pode ser caro para uma grande estrutura de dados, já que potencialmente toda a lista, string, vetor, etc. deve ser percorrida.
Se o objeto contém apenas um único elemento (por exemplo: número, caractere, etc), é o mesmo que
eqv?
.eqv?
testa dois objetos para determinar se ambos são "normalmente considerados o mesmo objeto".eqv?
eeq?
são operações muito semelhantes, e as diferenças entre eles serão um tanto específicas da implementação.eq?
é o mesmo que,eqv?
mas pode ser capaz de discernir distinções mais sutis e pode ser implementado com mais eficiência.eqv?
.=
compara números para igualdade numérica.(= 1 1.0 1/1 2/2)
fonte
eq?
era a igualdade real do ponteiro (nãoeqv?
). É "o melhor ou o mais discriminador". Por exemplo,(eqv? 2 2)
é garantido que#t
sim, mas(eq? 2 2)
é "não especificado". Ou seja, depende se uma implementação cria um novo objeto de memória real para cada número recém-lido ou reutiliza um previamente criado, se puder.eq?
eeqv?
são mais sutis do que as outras operações.Você não menciona uma implementação de esquema, mas em Racket,
eq?
só retorna verdadeiro se os argumentos se referirem ao mesmo objeto. Seu segundo exemplo está produzindo #f porque o sistema está criando um novo número de ponto flutuante para cada argumento; eles não são o mesmo objeto.equal?
e=
estão verificando a equivalência de valor, mas=
é aplicável apenas a números.Se você estiver usando o Racket, verifique aqui para mais informações. Caso contrário, verifique a documentação da implementação do seu esquema.
fonte
Pense
eq?
em uma igualdade de ponteiro. Os autores do Relatório querem que seja o mais geral possível, então eles não dizem isso abertamente porque é dependente da implementação, e dizer isso favoreceria as implementações baseadas em ponteiros. Mas eles dizemAqui está o que quero dizer.
(eqv? 2 2)
tem garantia de retorno,#t
mas(eq? 2 2)
não é especificado. Agora imagine uma implementação baseada em ponteiro. Neleeq?
é apenas uma comparação de ponteiro. Como(eq? 2 2)
não é especificado, significa que essa implementação está livre para apenas criar uma nova representação do objeto de memória de cada novo número que lê do código-fonte.eqv?
deve realmente inspecionar seus argumentos.OTOH
(eq 'a 'a)
é#t
. Isso significa que essa aplicação terá de reconhecer símbolos com nomes duplicados e usam o mesmo um objeto de representação na memória de todos eles.Suponha que uma implementação não seja baseada em ponteiros. Desde que esteja de acordo com o Relatório, não importa. Os autores simplesmente não querem ser vistos como ditando as especificações das implementações para os implementadores, então eles escolhem suas palavras com cuidado.
Este é o meu palpite de qualquer maneira.
Muito grosseiramente,
eq?
é a igualdade de ponteiro,eqv?
é (atômica-) ciente de valores,equal?
também é ciente de estrutura (verifica seus argumentos recursivamente, de modo que finalmente(equal? '(a) '(a))
é necessário ser#t
),=
é para números,string=?
é para strings e os detalhes são no Relatório.fonte
Além das respostas anteriores, acrescentarei alguns comentários.
Todos esses predicados desejam definir a função abstrata de
identity
para um objeto, mas em contextos diferentes.EQ?
é dependente da implementação e não responde à perguntaare 2 objects the same?
apenas em uso limitado. Do ponto de vista da implementação, este predicado apenas compara 2 números (ponteiro para objetos), ele não olha para o conteúdo dos objetos. Portanto, por exemplo, se sua implementação não mantiver exclusivamente as strings dentro, mas alocar memória diferente para cada string, então(eq? "a" "a")
será falso.EQV?
- olha dentro dos objetos, mas com uso limitado. É dependente da implementação se retornar verdadeiro para(eqv? (lambda(x) x) (lambda(x) x))
. Aqui está uma filosofia completa de como definir este predicado, pois sabemos hoje que existem alguns métodos rápidos para comparar a funcionalidade de algumas funções, com uso limitado. Maseqv?
fornece uma resposta coerente para grandes números, strings, etc.Praticamente, alguns desses predicados tentam usar a definição abstrata de um objeto (matematicamente), enquanto outros usam a representação de um objeto (como ele é implementado em uma máquina real). A definição matemática de identidade vem de Leibniz e diz:
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.
Idealmente, seria ser capaz de implementar esta definição no computador, mas por razões de indecidibilidade e / ou velocidade ela não é implementada literalmente. É por isso que existem muitos operadores que tentam, cada um, focar em diferentes pontos de vista em torno dessa definição.
Tente imaginar a definição abstrata de uma identidade para uma continuação. Mesmo que você possa fornecer uma definição de um subconjunto de funções ( classe sigma-recursiva de funções ), a linguagem não impõe que nenhum predicado seja verdadeiro ou falso. Isso complicaria muito tanto a definição da linguagem quanto muito mais a implementação.
O contexto para os outros predicados é mais fácil de analisar.
fonte