3

I am currently practicing the racket language and I ran into an interesting issue. I am trying to compare the elements of two lists. Normally if I compare two symbols, I'd get the following:

> (eq? 'leet 'leet)
#t
> (eq? 'let 'notleet)
#f

For some reason, when comparing the first element of two lists, I get false even though they're equal.

> (eq? (first '('leet 'a 'f)) (first '('leet 'coder 'a 'f 'f)))
#f

Why would that evaluate to false when I'm basically comparing the same two things?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
msafadieh
  • 33
  • 4
  • 1
    You're not comparing two symbols `'leet` and `'leet`, you're comparing two lists, `(list 'quote 'leet)` and `(list 'quote 'leet)`. I would recommend that you avoid using `quote` for lists and read [What is the difference between quote and list?](https://stackoverflow.com/questions/34984552/what-is-the-difference-between-quote-and-list). Your program works if you use `list` instead, as in `(first (list 'leet 'a 'f))` and `(first (list 'leet 'coder 'a 'f 'f))`. – Alex Knauth Apr 30 '18 at 12:43

3 Answers3

3

Sylwester's answer is correct and detailed, but I want to bring out the TL/DR; here:

Don't use eq?. Instead, use equal?.

Is this the whole story? No, of course not. But if you're looking for a one-liner to stick in your brain, it should be this one; equal? nearly always does what you want, and eq? often doesn't.

John Clements
  • 16,895
  • 3
  • 37
  • 52
2

when I'm basically comparing the same two things

You're not. (first '('leet 'a 'f)) is '(quote leet), not ‘leet. So you're comparing lists, not symbols.

'(...) already quotes the contents of the list. If you put additional 's inside the lists, these are quoted themselves. And since 'foo is a shortcut for (quote foo), quoting it gives you list containing those symbols.

If you just write '(leet a f) without the inner quotes, it will work as you expect.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
2

The expression 'expression is an abbreviation for (quote expression). When the expression is evaluated it evaluates to expression as a data structure or atomic value. It's important to know that nothing in expression gets evaluated further. Thus ''x, which is (quote (quote x)) becomes the list (quote x).

eq? is used to compare the same object. That means:

(eq? (list 'leet) (list 'leet)) ; ==> #f

Now both arguments look like (leet), but the two lists live in different memory locations in the computer and are thus not the same.

Constants like "string" and '(some list) might be created once and then referenced several times, but in a different implementation constants might be created newly for each location in the code. Thus:

(eq? "test" "test")   ; ==> #t or #f
(eq? '(leet) '(leet)) ; ==> #t or #f

In your code you have excess ' so (first '('leet 'a 'f)) is actually the data (quote leet), a list with two symbols. Thus you are applying the exact same as the last expression above and you can expect #f from some implementations and #t from a few other. Comparing lists is not the same as comparing symbols.

So you can fix this by removing the extra '. Then I assume you were not trying to make lists (quote leet).

(eq? (first '(leet a f)) (first '(leet coder a f f)))
; ==> #t

If you wanted to compare lists you should use equal?:

(equal? (first '('leet 'a 'f)) (first '('leet 'coder 'a 'f 'f)))
; ==> #t

And know that (first '('leet 'a 'f)) in #lang racket REPL prints ''leet with two '. The first ' is rackets funny way of printing values that evaluates to the value it should have printed, and perhaps the source of this confusion, and the second is the indicator that you have a list (quote leet), but many schemes abbreviate it to 'leet.

Sylwester
  • 47,942
  • 4
  • 47
  • 79