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