Tcl Part I · Module 4 · Operators and Expressions
eq and ne: Comparing Strings on Purpose
The previous chapter established the problem: == asks a numeric question or a text question depending on what the data happens to look like, and nothing in the source says which. This chapter is the answer, and it is short. Tcl has two operators that always compare text and never change their minds. Once you have them, every comparison you write can say exactly which question it is asking — which turns a line that a reviewer has to think about into one they can simply read.
Foundation11 min readTclPart IOperatorsComparisonStrings
Part I · Module 4 · Chapter 4.4 · eq and ne: Comparing Strings on Purpose
1. The Two Operators
puts "\"abc\" eq \"abc\" = [expr {"abc" eq "abc"}]"
puts "\"abc\" eq \"abd\" = [expr {"abc" eq "abd"}]"
puts "\"abc\" ne \"abd\" = [expr {"abc" ne "abd"}]""abc" eq "abc" = 1
"abc" eq "abd" = 0
"abc" ne "abd" = 1eq is equal-as-text. ne is not-equal-as-text. That is the whole mechanism.
2. They Never Change Their Minds
This is the entire point. eq compares characters, always, regardless of what the operands look like:
puts "1 eq 1.0 = [expr {1 eq 1.0}]"
puts "100 eq 100 = [expr {100 eq 100}]"
puts "\"abc\" eq \"abc\" = [expr {"abc" eq "abc"}]"1 eq 1.0 = 0
100 eq 100 = 1
"abc" eq "abc" = 11 eq 1.0 is false, because the characters differ. That is not a limitation — it is the guarantee. You know what the operator will do before you know what the data holds.
3. Side by Side
foreach pair {{1 1.0} {1e2 100} {" 5" "5"} {0x10 16} {abc abc}} {
lassign $pair a b
puts [format "%-6s vs %-6s ==: %s eq: %s" $a $b \
[expr {$a == $b}] [expr {$a eq $b}]]
}1 vs 1.0 ==: 1 eq: 0
1e2 vs 100 ==: 1 eq: 0
5 vs 5 ==: 1 eq: 0
0x10 vs 16 ==: 1 eq: 0
abc vs abc ==: 1 eq: 1Four of the five disagree. In every case == said "the same number" and eq said "different characters", and both were telling the truth about different questions.
The last row agrees because abc is not numeric, so == fell back to a text comparison — Chapter 4.3 §3.
4. Why a Reader Can Trust eq
Return to the procedure from the previous chapter, written both ways:
proc same_number {a b} { return [expr {$a == $b}] }
proc same_text {a b} { return [expr {$a eq $b}] }
foreach pair {{10 10.0} {abc abc} {007 7} {v1.0 v1.00}} {
lassign $pair a b
puts [format "%-6s %-6s same_number:%s same_text:%s" \
$a $b [same_number $a $b] [same_text $a $b]]
}10 10.0 same_number:1 same_text:0
abc abc same_number:1 same_text:1
007 7 same_number:1 same_text:1
v1.0 v1.00 same_number:0 same_text:0same_text is predictable from its name on every row. same_number is not — it answered a numeric question on rows one and three and a text question on rows two and four.
Look at row three. 007 and 7: same_number says yes, and same_text also says yes — but for a reason worth understanding, which is the next section.
5. The Compound Trap
Row three deserves its own look, because two of this track's traps stack there.
set code "0010"
puts "\$code == 10 = [expr {$code == 10}]"
puts "\$code == 8 = [expr {$code == 8}]"
puts "\$code eq \"10\" = [expr {$code eq "10"}]"$code == 10 = 0
$code == 8 = 1
$code eq "10" = 1Read that carefully, because the first line is probably not what you expected.
"0010" compared numerically is 8, not 10 — a leading zero means octal, which is Chapter 3.5 §6. So $code == 10 is false, and $code == 8 is true.
Two independent surprises in one comparison: the operator chose a numeric reading, and the numeric reading chose base 8.
6. The Decision Rule
One question:
Am I asking about a quantity, or about characters?
- A quantity — a count, a size, a measurement, a threshold →
==and!=. - Characters — a name, a code, a status, a mode, an identifier, a version →
eqandne.
| You are comparing | Use |
|---|---|
| counts, sizes, totals | == != |
| a value against a numeric threshold | == != < > |
| names, modes, statuses | eq ne |
| identifiers and codes, especially zero-padded | eq ne |
| version strings | eq ne |
| anything from a file whose shape you do not control | eq ne |
| measured real numbers | neither — Chapter 4.10 |
When in doubt about non-numeric data, eq is the safer default, because its answer does not depend on what the input happens to contain.
7. A Worked Example
set mode "fast"
if {$mode eq "fast"} {
puts "running the quick path"
} elseif {$mode eq "careful"} {
puts "running all checks"
} else {
puts "unknown mode: $mode"
}running the quick pathEvery comparison here is about characters, so every one is eq. If a later release adds a mode called 2, nothing about these lines changes meaning — which is exactly the property == could not offer.
Chapter 4.9 shows a neater way to write the membership version of this, and switch in Module 8 is better again once there are several cases.
8. There Is Also a Command
For completeness, since you will meet it: string equal does the same job at command level, and adds options eq does not have.
puts "string equal: [string equal "abc" "abc"]"
puts "ignoring case: [string equal -nocase "ABC" "abc"]"
puts "first 2 characters: [string equal -length 2 "abcdef" "abxxxx"]"string equal: 1
ignoring case: 1
first 2 characters: 1Inside an expression, eq is shorter and reads better. Reach for string equal when you need -nocase or -length. Module 5 covers it properly.
9. Common Mistakes
Using == on codes and identifiers. §5. Zero-padded ones are read as octal.
Thinking eq is slower or less precise. It is a direct character comparison and is the simpler operation.
Expecting 1 eq 1.0 to be true. It is false by design. §2.
Using eq on measured numbers. 10.0 eq 10 is false even when the quantity is identical. Use == for quantities.
Forgetting ne exists and writing ![expr {$a eq $b}]. Write $a ne $b.
10. Exercises
Exercise 1 — disagree on purpose. Find three pairs of values where == and eq give different answers. Explain each in terms of what the two operators compared.
Exercise 2 — the octal code. Set a variable to "0012". Predict == 12, == 10, and eq "0012" before running all three. Which surprised you, and which two chapters does it combine?
Exercise 3 — pick the operator. For each, choose == or eq and justify it in a few words: retry count against a limit; a filename against report.txt; a temperature against 20; a user-supplied mode against verbose; a part number against 00417.
Exercise 4 — break a working script. Write a comparison with == that is correct for the values alpha and beta, then supply values that make the same line answer a different kind of question. Then fix it with eq.
Exercise 5 — the version check. Write a test for whether a version string is exactly 2.10. Show that == gets it wrong for the input 2.1, and that eq does not.
Exercise 6 — ignore case. Compare YES and yes so they count as equal. Which tool from §8 did you need, and why will eq not do it?
11. Remember
eqandnealways compare text, whatever the operands look like.- That predictability is the point. The question does not change with the data.
1 eq 1.0is false — different characters, same number.- Ask: quantity, or characters?
==for quantities,eqfor everything else. - Codes and identifiers are characters. Zero-padded ones are read as octal by
==. - For non-numeric data,
eqis the safer default. string equalis the command form, with-nocaseand-length.- Neither operator suits measured real numbers. Chapter 4.10.
Next: combining conditions, and the evaluation rule that makes a guard possible.
Where this fits
Part of the Tcl curriculum.
