Dan Ingalls is of course a big
NAME in the
world of Smalltalk, but the stated reason
for
changing the behaviour of #beginsWith:
and #endsWith: makes no
sense.
We have
many ways to define a partial order
on strings.
x <= y
iff y beginsWith: x
x <= y iff y endsWith:
x
x <= y iff y includesSubCollection: x
x <= y
iff y includesSubSequence: x
These things are supposed to obey
laws:
if a beginsWith: b , c
then a beginsWith: b
if a
endsWith: b , c
then a endsWith: c
if a
includesSubCollection: b , c
then a includesSubCollection:
b
and a includesSubCollection: c
if a includesSubSequence: b ,
c
then a includesSubSequence: b
and a includesSubSequence:
c.
We also expect the usual rules of
equality
to hold. So
(1) a beginsWith: a
(2) a = ''
, a
(3) THEREFORE a beginsWith: ''
(1) a
endsWith: a
(2) a = a , ''
(3) THEREFORE a endsWith:
''
(1) a includesSubCollection:
a
(2) a = '' , a
(3) THEREFORE a
includesSubCollect: ''
Reasoning
about strings (as values) gets
enormously more complicated if
the operations
do not follow simple sensible
rules, and
having '' be the least string under these
orderings
and having '' be a prefix and a
suffix of any string is essential
if the
rules are going to be simple and coherent.
'' is to strings (and more
generally
empty sequences are to sequences) pretty
much what 0
is to integers. Denying that
'abc' beginsWith: '' is
*structurally*
just like denying that 0 <=
123.
Now as it happens I *can* see a
use for
versions of #beginsWith: and #endsWith:
that
diverge from the ones we have, but
*this* is not where they need to
diverge.
a beginsWithGraphemesOf: b
iff a asGraphemes = b asGraphemes
, c asGraphemes
for some c, where s asGraphemes
returns a
sequence of strings each of which is a maximally
long
grapheme cluster, such that concatenating
s asGraphemes recovers s.
That is,
#beginsWithGraphemesOf: and
#endsWithGraphemesOf: would
respect the
Unicode Text Segmentation boundaries.
But s
beginsWithGraphemesOf: ''
would still need to be
true.
The thing is, in practice you
often DON'T
KNOW whether a potential affix is empty or
not.
Here are some of my test cases.
testTestData
"Ensure that the sample string
has no duplicates."
[(Set withAll: string) size =
stringSize] assert.
testBeginsWith
"Test that every prefix of the sample IS a prefix of it."
0 to: stringSize do: [:n |
[string beginsWith: (string copyFrom: 1 to: n)] assert].
testEndsWith
"Test that every suffix of the
sample IS a suffix of it."
0 to: stringSize do: [:n
|
[string endsWith: (string copyFrom:
stringSize - n + 1 to: stringSize)] assert].
testIndexOfSubCollectionAtBeginning
"Test that
every prefix of 'abcd' is found at the beginning."
0 to: stringSize do: [:n | |s i t|
s :=
string copyFrom: 1 to: n.
i := string
indexOfSubCollection: s startingAt: 1.
[1 =
i] assert.
t := string copyFrom: i to: i - 1
+ n.
[t = s] assert].
testIndexOfSubCollectionAtEnd
"Test that every
proper suffix of the sample is found at the end."
1
to: stringSize do: [:n | |s i t|
s := string
copyFrom: stringSize - n + 1 to: stringSize.
i := string indexOfSubCollection: s startingAt: 1.
[stringSize + 1 - n = i] assert.
t :=
string copyFrom: i to: i - 1 + n.
[t = s]
assert].
testLastIndexOfSubCollectionAtBeginning
"Test that
every proper prefix of the sample is found at the beginning."
1 to: stringSize do: [:n | |s i t|
s := string copyFrom: 1 to: n.
i :=
string lastIndexOfSubCollection: s startingAt: stringSize.
[1 = i] assert.
t := string
copyFrom: i to: i - 1 + n.
[t = s]
assert].
testLastIndexOfSubCollectionAtEnd
"Test that every suffix of the sample is found at the
end."
0 to: stringSize do: [:n | |s i t|
s := string copyFrom: stringSize - n + 1 to:
stringSize.
i := string
lastIndexOfSubCollection: s startingAt: stringSize.
[stringSize + 1 - n = i] assert.
t :=
string copyFrom: i to: i - 1 + n.
[t = s]
assert].
testOccurrencesOfEmptyCollection
"Test that the empty string occurs at the
beginning,
at the end, and in between every
pair of adjacent characters."
[(string
occurrencesOfSubCollection: '') = (stringSize + 1)] assert.
testOccurrencesOfUniqueParts
"Test that
unique parts occur as many times as they should."
|repeated|
repeated := string , string ,
string.
1 to: stringSize do: [:start |
start to: stringSize do: [:finish | |s n|
s := string copyFrom: start to:
finish.
n := string
occurrencesOfSubCollection: s.
[n =
1] assert.
n := repeated
occurrencesOfSubCollection: s.
[n =
3] assert]].