MarkingKey
Richard A. O'Keefe
ok at cs.otago.ac.nz
Tue Jun 11 02:40:59 UTC 2002
Niko Schwarz <niko.schwarz at gmx.net> wrote:
In a lot of algorithms it is useful to have a marking key that
is bigger than all other elements of a list or another data
structure.
I have never EVER heard the term "marking key" used before, and I've
been studying and teaching algorithms for longer than I care to remember.
The term that is almost universally used is "sentinel",
and planting such a value into a sequence is called "posting a sentinel".
It is important to understand that
- it is quite as common to have a use for "minus infinity" as
for "plus infinity"
- in many cases a sentinel is NOT an extreme value at all, but
something having a particular property (being a blank, not being
a letter, being divisible by something, having an area which is
extreme, being the name of an empty file you are sure to be able
to open, ...)
- in practically every case I can call to mind there is another
algorithm which manages perfectly well without any sentinel at all.
In other languages you usually use maxInt and so on
for that, but that is not appropriate for squeak.
It is very seldom appropriate in the other languages either.
A sentinel should be a value that you KNOW CANNOT POSSIBLY occur as
legitimate data. As such, sentinel values tend to be extremely
problem-specific.
I was very surprised that I didn't see any in the class browser
-- how come?
Because of the extremely problem-specific nature of sentinels.
There is no one value which is generally satisfactory.
In fact there really isn't anything, I attached my fix for it.
Btw, can you help me out?
In Java you have getters and setters.
In ruby you have ? and plain methods.
Whats the counterpart in squeak?
I don't know about Ruby. In Java, "getters" and "setters" are just
methods. There's a convention they follow, that's all.
I want to two methods that:
a) set a flag
b) return the flag.
Run, do not walk, to your nearest technical library or technical bookshop,
and buy or borrow a book about Smalltalk. Such methods are called
"accessors" in Smalltalk. They go in the message category "accessing",
and they look like
flag
^flag
flag: aBoolean
flag := aBoolean
(I find a method called smallest: aBoolean ridiculous. I want
two: smallest and biggest, one to write and one to read. How
to do that?)
Er, which of 'smallest', 'biggest' writes, and what does it write?
Which of them reads, and what does it read?
Magnitude subclass: #MarkingKey
instanceVariableNames: 'biggest'
classVariableNames: ''
poolDictionaries: ''
category: 'Kernel-Magnitudes'
"Sometimes it's useful to have a key bigger or smaller than any
other thing in an array to avoid checks. I perform that."
No, not "perform", "am".
"accessing"
biggest
biggest := true
smallest
biggest := false
Names such as 'beBiggest', 'beSmallest' might be clearer.
beBiggest
biggest := true
isBiggest
^biggest
beSmallest
biggest := false
isSmallest
^biggest not
"comparing"
< aMagnitude
^biggest
= aMagnitude
^false
> aMagnitude
^biggest not
These methods are obviously buggy, because
b := MarkingKey "feh!" biggest.
b < 0
comes out true. Correct them to
< aMagnitude
^biggest not "or ^self isSmallest"
> aMagnitude
^biggest "or ^self isBiggest"
These methods still violate the "contract" for magnitudes.
Consider
b := MarkingKey "feh!" biggest.
b < b "is false and should be"
b = b "is false, should be true"
b > b "is true, should be false"
b <= b "is false, should be true"
b >= b "is true and should be"
b ~= b "is true, should be false"
s := MarkingKey "feh!0" smallest.
s < s "is true, should be false"
s = s "is false, should be true"
s > s "is false and should be"
s <= s "is true and should be"
s >= s "is false, should be true"
s ~= s "is true, should be false"
How could I possibly use such a thing in an ordered collection
of some kind when it doesn't respect the rules for ordering?
"class, instance creation"
biggest
^self new beBiggest
smallest
^self new beSmallest
Equality should be
= aMagnitude
^self class = aMagnitude class
and: [self isBiggest = aMagnitude biggest]
Even renamed to Sentinel and with some of the problems above addressed,
this still won't help me if I am searching a sequenceable collection for
a string; the sentinel I want then is '' copy or something like that.
There still remains a huge problem with MarkingKey "feh!".
Consider
b := MarkingKey biggest.
b > 1 "true, as it should be"
1 < b "Oh dear oh dear oh dear"
The result of 1 < b is a "MessageNotUnderstoof: adaptToNumber:andSend:"
window. OK, let's whack another method into MarkingKey "feh!".
adaptToNumber: aNumber andSend: selector
^selector caseOf: {
[#<] -> [self > aNumber].
[#=] -> [self = aNumber].
[#>] -> [self < aNumber].
[#>=] -> [self <= aNumber].
[#~=] -> [self ~= aNumber].
[#<=] -> [self >= aNumber]
} otherwise: [self error: 'cannot do arithmetic on MarkingKeys']
Now we can freely compare numbers with sentinels as well as sentinels
with numbers. But we are not out of the woods yet.
b > 'a' "true, as it should be"
'a' < b "Oh dear oh dear oh dear"
The result of 'a' < b is an "Error: Sentinels are not indexable"
window. Papering over this problem is going to require abdominal
surgery on String, and I really don't fancy doing that, because we
still have Date, Time, and a bunch of things that do not inherit
from Magnitude but do support the magnitude protocol to worry about.
Sentinels: either learn to do without them, or put up with the fact
that they are highly problem-specific. In this case, one size gives
all fits.
More information about the Squeak-dev
mailing list
|