2
0
فهرست منبع

Fixes #155.

Also make `removeKey:` constant instead of linear. No need for splice.
Herbert Vojčík 11 سال پیش
والد
کامیت
a00b67c79d
6فایلهای تغییر یافته به همراه205 افزوده شده و 98 حذف شده
  1. 41 31
      js/Kernel-Collections.deploy.js
  2. 50 35
      js/Kernel-Collections.js
  3. 26 1
      js/Kernel-Tests.deploy.js
  4. 32 2
      js/Kernel-Tests.js
  5. 35 28
      st/Kernel-Collections.st
  6. 21 1
      st/Kernel-Tests.st

+ 41 - 31
js/Kernel-Collections.deploy.js

@@ -1275,15 +1275,8 @@ selector: "at:ifAbsent:",
 fn: function (aKey,aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-		var index;
-		for(var i=0;i<self['@keys'].length;i++){
-			if(self['@keys'][i].__eq(aKey)) {index = i;}
-		};
-		if(typeof index === 'undefined') {
-			return aBlock();
-		} else {
-			return self['@values'][index];
-		}
+		var index = self._positionOfKey_(aKey);
+		return index >=0 ? self['@values'][index] : aBlock();
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"at:ifAbsent:",{aKey:aKey,aBlock:aBlock}, smalltalk.Dictionary)})},
 messageSends: []}),
@@ -1296,15 +1289,14 @@ selector: "at:put:",
 fn: function (aKey,aValue){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-		var index = self['@keys'].indexOf(aKey);
+		var index = self._positionOfKey_(aKey);
 		if(index === -1) {
-			self['@values'].push(aValue);
-			self['@keys'].push(aKey);
-		} else {
-			self['@values'][index] = aValue;
-		};
+			var keys = self['@keys'];
+			index = keys.length;
+			keys.push(aKey);
+		}
 
-		return aValue;
+		return self['@values'][index] = aValue;
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"at:put:",{aKey:aKey,aValue:aValue}, smalltalk.Dictionary)})},
 messageSends: []}),
@@ -1316,11 +1308,9 @@ smalltalk.method({
 selector: "includesKey:",
 fn: function (aKey){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(self["@keys"])._includes_(aKey);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"includesKey:",{aKey:aKey}, smalltalk.Dictionary)})},
-messageSends: ["includes:"]}),
+return smalltalk.withContext(function($ctx1) { 
 return self._positionOfKey_(aKey) >= 0; ;
+return self}, function($ctx1) {$ctx1.fill(self,"includesKey:",{aKey:aKey}, smalltalk.Dictionary)})},
+messageSends: []}),
 smalltalk.Dictionary);
 
 smalltalk.addMethod(
@@ -1366,6 +1356,23 @@ return $1;
 messageSends: ["copy"]}),
 smalltalk.Dictionary);
 
+smalltalk.addMethod(
+"_positionOfKey_",
+smalltalk.method({
+selector: "positionOfKey:",
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+		var keys = self['@keys'];
+		for(var i=0;i<keys.length;i++){
+			if(keys[i].__eq(anObject)) { return i;}
+		}
+		return -1;
+	;
+return self}, function($ctx1) {$ctx1.fill(self,"positionOfKey:",{anObject:anObject}, smalltalk.Dictionary)})},
+messageSends: []}),
+smalltalk.Dictionary);
+
 smalltalk.addMethod(
 "_removeKey_ifAbsent_",
 smalltalk.method({
@@ -1373,16 +1380,19 @@ selector: "removeKey:ifAbsent:",
 fn: function (aKey,aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-            var index = self['@keys'].indexOf(aKey);
-            if(index === -1) {
-                return aBlock()
-            } else {
-                var value;
-                self['@keys'].splice(index, 1);
-                value = self['@values'].splice(index, 1);
-                return value[0];
-            };
-    ;
+		var index = self._positionOfKey_(aKey);
+		if(index === -1) {
+			return aBlock()
+		} else {
+			var keys = self['@keys'], values = self['@values'];
+			var value = values[index], l = keys.length;
+			keys[index] = keys[l-1];
+			keys.pop();
+			values[index] = values[l-1];
+			values.pop();
+			return value;
+		}
+	;
 return self}, function($ctx1) {$ctx1.fill(self,"removeKey:ifAbsent:",{aKey:aKey,aBlock:aBlock}, smalltalk.Dictionary)})},
 messageSends: []}),
 smalltalk.Dictionary);

+ 50 - 35
js/Kernel-Collections.js

@@ -1692,19 +1692,12 @@ category: 'accessing',
 fn: function (aKey,aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-		var index;
-		for(var i=0;i<self['@keys'].length;i++){
-			if(self['@keys'][i].__eq(aKey)) {index = i;}
-		};
-		if(typeof index === 'undefined') {
-			return aBlock();
-		} else {
-			return self['@values'][index];
-		}
+		var index = self._positionOfKey_(aKey);
+		return index >=0 ? self['@values'][index] : aBlock();
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"at:ifAbsent:",{aKey:aKey,aBlock:aBlock}, smalltalk.Dictionary)})},
 args: ["aKey", "aBlock"],
-source: "at: aKey ifAbsent: aBlock\x0a\x09<\x0a\x09\x09var index;\x0a\x09\x09for(var i=0;i<self['@keys'].length;i++){\x0a\x09\x09\x09if(self['@keys'][i].__eq(aKey)) {index = i;}\x0a\x09\x09};\x0a\x09\x09if(typeof index === 'undefined') {\x0a\x09\x09\x09return aBlock();\x0a\x09\x09} else {\x0a\x09\x09\x09return self['@values'][index];\x0a\x09\x09}\x0a\x09>",
+source: "at: aKey ifAbsent: aBlock\x0a\x09<\x0a\x09\x09var index = self._positionOfKey_(aKey);\x0a\x09\x09return index >>=0 ? self['@values'][index] : aBlock();\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),
@@ -1718,19 +1711,18 @@ category: 'accessing',
 fn: function (aKey,aValue){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-		var index = self['@keys'].indexOf(aKey);
+		var index = self._positionOfKey_(aKey);
 		if(index === -1) {
-			self['@values'].push(aValue);
-			self['@keys'].push(aKey);
-		} else {
-			self['@values'][index] = aValue;
-		};
+			var keys = self['@keys'];
+			index = keys.length;
+			keys.push(aKey);
+		}
 
-		return aValue;
+		return self['@values'][index] = aValue;
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"at:put:",{aKey:aKey,aValue:aValue}, smalltalk.Dictionary)})},
 args: ["aKey", "aValue"],
-source: "at: aKey put: aValue\x0a\x09<\x0a\x09\x09var index = self['@keys'].indexOf(aKey);\x0a\x09\x09if(index === -1) {\x0a\x09\x09\x09self['@values'].push(aValue);\x0a\x09\x09\x09self['@keys'].push(aKey);\x0a\x09\x09} else {\x0a\x09\x09\x09self['@values'][index] = aValue;\x0a\x09\x09};\x0a\x0a\x09\x09return aValue;\x0a\x09>",
+source: "at: aKey put: aValue\x0a\x09<\x0a\x09\x09var index = self._positionOfKey_(aKey);\x0a\x09\x09if(index === -1) {\x0a\x09\x09\x09var keys = self['@keys'];\x0a\x09\x09\x09index = keys.length;\x0a\x09\x09\x09keys.push(aKey);\x0a\x09\x09}\x0a\x0a\x09\x09return self['@values'][index] = aValue;\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),
@@ -1743,13 +1735,11 @@ selector: "includesKey:",
 category: 'testing',
 fn: function (aKey){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(self["@keys"])._includes_(aKey);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"includesKey:",{aKey:aKey}, smalltalk.Dictionary)})},
+return smalltalk.withContext(function($ctx1) { 
 return self._positionOfKey_(aKey) >= 0; ;
+return self}, function($ctx1) {$ctx1.fill(self,"includesKey:",{aKey:aKey}, smalltalk.Dictionary)})},
 args: ["aKey"],
-source: "includesKey: aKey\x0a\x09^keys includes: aKey",
-messageSends: ["includes:"],
+source: "includesKey: aKey\x0a\x09< return self._positionOfKey_(aKey) >>= 0; >",
+messageSends: [],
 referencedClasses: []
 }),
 smalltalk.Dictionary);
@@ -1812,6 +1802,28 @@ referencedClasses: []
 }),
 smalltalk.Dictionary);
 
+smalltalk.addMethod(
+"_positionOfKey_",
+smalltalk.method({
+selector: "positionOfKey:",
+category: 'private',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+		var keys = self['@keys'];
+		for(var i=0;i<keys.length;i++){
+			if(keys[i].__eq(anObject)) { return i;}
+		}
+		return -1;
+	;
+return self}, function($ctx1) {$ctx1.fill(self,"positionOfKey:",{anObject:anObject}, smalltalk.Dictionary)})},
+args: ["anObject"],
+source: "positionOfKey: anObject\x0a\x09<\x0a\x09\x09var keys = self['@keys'];\x0a\x09\x09for(var i=0;i<keys.length;i++){\x0a\x09\x09\x09if(keys[i].__eq(anObject)) { return i;}\x0a\x09\x09}\x0a\x09\x09return -1;\x0a\x09>",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Dictionary);
+
 smalltalk.addMethod(
 "_removeKey_ifAbsent_",
 smalltalk.method({
@@ -1820,19 +1832,22 @@ category: 'adding/removing',
 fn: function (aKey,aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-            var index = self['@keys'].indexOf(aKey);
-            if(index === -1) {
-                return aBlock()
-            } else {
-                var value;
-                self['@keys'].splice(index, 1);
-                value = self['@values'].splice(index, 1);
-                return value[0];
-            };
-    ;
+		var index = self._positionOfKey_(aKey);
+		if(index === -1) {
+			return aBlock()
+		} else {
+			var keys = self['@keys'], values = self['@values'];
+			var value = values[index], l = keys.length;
+			keys[index] = keys[l-1];
+			keys.pop();
+			values[index] = values[l-1];
+			values.pop();
+			return value;
+		}
+	;
 return self}, function($ctx1) {$ctx1.fill(self,"removeKey:ifAbsent:",{aKey:aKey,aBlock:aBlock}, smalltalk.Dictionary)})},
 args: ["aKey", "aBlock"],
-source: "removeKey: aKey ifAbsent: aBlock\x0a    <\x0a            var index = self['@keys'].indexOf(aKey);\x0a            if(index === -1) {\x0a                return aBlock()\x0a            } else {\x0a                var value;\x0a                self['@keys'].splice(index, 1);\x0a                value = self['@values'].splice(index, 1);\x0a                return value[0];\x0a            };\x0a    >",
+source: "removeKey: aKey ifAbsent: aBlock\x0a\x09<\x0a\x09\x09var index = self._positionOfKey_(aKey);\x0a\x09\x09if(index === -1) {\x0a\x09\x09\x09return aBlock()\x0a\x09\x09} else {\x0a\x09\x09\x09var keys = self['@keys'], values = self['@values'];\x0a\x09\x09\x09var value = values[index], l = keys.length;\x0a\x09\x09\x09keys[index] = keys[l-1];\x0a\x09\x09\x09keys.pop();\x0a\x09\x09\x09values[index] = values[l-1];\x0a\x09\x09\x09values.pop();\x0a\x09\x09\x09return value;\x0a\x09\x09}\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),

+ 26 - 1
js/Kernel-Tests.deploy.js

@@ -962,12 +962,16 @@ return smalltalk.withContext(function($ctx2) {
return nil;
 _st(self)._deny_(_st(_st(d)._at_ifAbsent_("foo",(function(){
 return smalltalk.withContext(function($ctx2) {
return nil;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))).__eq("world"));
+_st(self)._assert_(_st(d)._includesKey_("hello"));
+_st(self)._deny_(_st(d)._includesKey_("foo"));
 _st(d)._at_put_((1),(2));
 _st(self)._assert_equals_(_st(d)._at_((1)),(2));
 _st(d)._at_put_(_st((1)).__at((3)),(3));
 _st(self)._assert_equals_(_st(d)._at_(_st((1)).__at((3))),(3));
+_st(self)._assert_(_st(d)._includesKey_(_st((1)).__at((3))));
+_st(self)._deny_(_st(d)._includesKey_(_st((3)).__at((1))));
 return self}, function($ctx1) {$ctx1.fill(self,"testAccessing",{d:d}, smalltalk.DictionaryTest)})},
-messageSends: ["new", "at:put:", "assert:equals:", "at:", "at:ifAbsent:", "deny:", "=", "@"]}),
+messageSends: ["new", "at:put:", "assert:equals:", "at:", "at:ifAbsent:", "deny:", "=", "assert:", "includesKey:", "@"]}),
 smalltalk.DictionaryTest);
 
 smalltalk.addMethod(
@@ -1107,6 +1111,27 @@ return self}, function($ctx1) {$ctx1.fill(self,"testKeys",{d:d}, smalltalk.Dicti
 messageSends: ["new", "at:put:", "assert:equals:", "keys"]}),
 smalltalk.DictionaryTest);
 
+smalltalk.addMethod(
+"_testPointKey",
+smalltalk.method({
+selector: "testPointKey",
+fn: function (){
+var self=this;
+var d;
+return smalltalk.withContext(function($ctx1) { 
d=_st((smalltalk.Dictionary || Dictionary))._new();
+_st(d)._at_put_(_st((1)).__at((1)),"foo");
+_st(self)._assert_equals_(_st(d)._at_(_st((1)).__at((1))),"foo");
+_st(d)._at_put_(_st((1)).__at((1)),"bar");
+_st(self)._assert_equals_(_st(d)._at_(_st((1)).__at((1))),"bar");
+_st(d)._removeKey_(_st((1)).__at((1)));
+_st(self)._assert_equals_(_st(d)._at_ifAbsent_(_st((1)).__at((1)),(function(){
+return smalltalk.withContext(function($ctx2) {
return "baz";
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})})),"baz");
+_st(self)._deny_(_st(d)._includesKey_(_st((1)).__at((1))));
+return self}, function($ctx1) {$ctx1.fill(self,"testPointKey",{d:d}, smalltalk.DictionaryTest)})},
+messageSends: ["new", "at:put:", "@", "assert:equals:", "at:", "removeKey:", "at:ifAbsent:", "deny:", "includesKey:"]}),
+smalltalk.DictionaryTest);
+
 smalltalk.addMethod(
 "_testPrintString",
 smalltalk.method({

+ 32 - 2
js/Kernel-Tests.js

@@ -1208,14 +1208,18 @@ return smalltalk.withContext(function($ctx2) {
return nil;
 _st(self)._deny_(_st(_st(d)._at_ifAbsent_("foo",(function(){
 return smalltalk.withContext(function($ctx2) {
return nil;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))).__eq("world"));
+_st(self)._assert_(_st(d)._includesKey_("hello"));
+_st(self)._deny_(_st(d)._includesKey_("foo"));
 _st(d)._at_put_((1),(2));
 _st(self)._assert_equals_(_st(d)._at_((1)),(2));
 _st(d)._at_put_(_st((1)).__at((3)),(3));
 _st(self)._assert_equals_(_st(d)._at_(_st((1)).__at((3))),(3));
+_st(self)._assert_(_st(d)._includesKey_(_st((1)).__at((3))));
+_st(self)._deny_(_st(d)._includesKey_(_st((3)).__at((1))));
 return self}, function($ctx1) {$ctx1.fill(self,"testAccessing",{d:d}, smalltalk.DictionaryTest)})},
 args: [],
-source: "testAccessing\x0a\x09| d |\x0a\x0a\x09d := Dictionary new.\x0a\x0a\x09d at: 'hello' put: 'world'.\x0a\x09self assert: (d at: 'hello') equals: 'world'.\x0a\x09self assert: (d at: 'hello' ifAbsent: [nil]) equals: 'world'.\x0a\x09self deny: (d at: 'foo' ifAbsent: [nil]) = 'world'.\x0a\x0a\x09d at: 1 put: 2.\x0a\x09self assert: (d at: 1) equals: 2.\x0a\x0a\x09d at: 1@3 put: 3.\x0a\x09self assert: (d at: 1@3) equals: 3",
-messageSends: ["new", "at:put:", "assert:equals:", "at:", "at:ifAbsent:", "deny:", "=", "@"],
+source: "testAccessing\x0a\x09| d |\x0a\x0a\x09d := Dictionary new.\x0a\x0a\x09d at: 'hello' put: 'world'.\x0a\x09self assert: (d at: 'hello') equals: 'world'.\x0a\x09self assert: (d at: 'hello' ifAbsent: [nil]) equals: 'world'.\x0a\x09self deny: (d at: 'foo' ifAbsent: [nil]) = 'world'.\x0a\x0a\x09self assert: (d includesKey: 'hello').\x0a\x09self deny: (d includesKey: 'foo').\x0a\x0a\x09d at: 1 put: 2.\x0a\x09self assert: (d at: 1) equals: 2.\x0a\x0a\x09d at: 1@3 put: 3.\x0a\x09self assert: (d at: 1@3) equals: 3.\x0a\x0a\x09self assert: (d includesKey: 1@3).\x0a\x09self deny: (d includesKey: 3@1)\x0a",
+messageSends: ["new", "at:put:", "assert:equals:", "at:", "at:ifAbsent:", "deny:", "=", "assert:", "includesKey:", "@"],
 referencedClasses: ["Dictionary"]
 }),
 smalltalk.DictionaryTest);
@@ -1387,6 +1391,32 @@ referencedClasses: ["Dictionary"]
 }),
 smalltalk.DictionaryTest);
 
+smalltalk.addMethod(
+"_testPointKey",
+smalltalk.method({
+selector: "testPointKey",
+category: 'tests',
+fn: function (){
+var self=this;
+var d;
+return smalltalk.withContext(function($ctx1) { 
d=_st((smalltalk.Dictionary || Dictionary))._new();
+_st(d)._at_put_(_st((1)).__at((1)),"foo");
+_st(self)._assert_equals_(_st(d)._at_(_st((1)).__at((1))),"foo");
+_st(d)._at_put_(_st((1)).__at((1)),"bar");
+_st(self)._assert_equals_(_st(d)._at_(_st((1)).__at((1))),"bar");
+_st(d)._removeKey_(_st((1)).__at((1)));
+_st(self)._assert_equals_(_st(d)._at_ifAbsent_(_st((1)).__at((1)),(function(){
+return smalltalk.withContext(function($ctx2) {
return "baz";
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})})),"baz");
+_st(self)._deny_(_st(d)._includesKey_(_st((1)).__at((1))));
+return self}, function($ctx1) {$ctx1.fill(self,"testPointKey",{d:d}, smalltalk.DictionaryTest)})},
+args: [],
+source: "testPointKey\x0a\x09| d |\x0a\x0a\x09d := Dictionary new.\x0a    \x0a    d at: 1@1 put: 'foo'.\x0a\x09self assert: (d at: 1@1) equals: 'foo'.\x0a    d at: 1@1 put: 'bar'.\x0a\x09self assert: (d at: 1@1) equals: 'bar'.\x0a    d removeKey: 1@1.\x0a    self assert: (d at: 1@1 ifAbsent: [ 'baz' ]) equals: 'baz'.\x0a    self deny: (d includesKey: 1@1)",
+messageSends: ["new", "at:put:", "@", "assert:equals:", "at:", "removeKey:", "at:ifAbsent:", "deny:", "includesKey:"],
+referencedClasses: ["Dictionary"]
+}),
+smalltalk.DictionaryTest);
+
 smalltalk.addMethod(
 "_testPrintString",
 smalltalk.method({

+ 35 - 28
st/Kernel-Collections.st

@@ -540,29 +540,21 @@ HashedCollection subclass: #Dictionary
 
 at: aKey ifAbsent: aBlock
 	<
-		var index;
-		for(var i=0;i<self['@keys'].length;i++){
-			if(self['@keys'][i].__eq(aKey)) {index = i;}
-		};
-		if(typeof index === 'undefined') {
-			return aBlock();
-		} else {
-			return self['@values'][index];
-		}
+		var index = self._positionOfKey_(aKey);
+		return index >>=0 ? self['@values'][index] : aBlock();
 	>
 !
 
 at: aKey put: aValue
 	<
-		var index = self['@keys'].indexOf(aKey);
+		var index = self._positionOfKey_(aKey);
 		if(index === -1) {
-			self['@values'].push(aValue);
-			self['@keys'].push(aKey);
-		} else {
-			self['@values'][index] = aValue;
-		};
+			var keys = self['@keys'];
+			index = keys.length;
+			keys.push(aKey);
+		}
 
-		return aValue;
+		return self['@values'][index] = aValue;
 	>
 !
 
@@ -589,17 +581,20 @@ values
 !Dictionary methodsFor: 'adding/removing'!
 
 removeKey: aKey ifAbsent: aBlock
-    <
-            var index = self['@keys'].indexOf(aKey);
-            if(index === -1) {
-                return aBlock()
-            } else {
-                var value;
-                self['@keys'].splice(index, 1);
-                value = self['@values'].splice(index, 1);
-                return value[0];
-            };
-    >
+	<
+		var index = self._positionOfKey_(aKey);
+		if(index === -1) {
+			return aBlock()
+		} else {
+			var keys = self['@keys'], values = self['@values'];
+			var value = values[index], l = keys.length;
+			keys[index] = keys[l-1];
+			keys.pop();
+			values[index] = values[l-1];
+			values.pop();
+			return value;
+		}
+	>
 ! !
 
 !Dictionary methodsFor: 'converting'!
@@ -620,10 +615,22 @@ initialize
 	values := #()
 ! !
 
+!Dictionary methodsFor: 'private'!
+
+positionOfKey: anObject
+	<
+		var keys = self['@keys'];
+		for(var i=0;i<keys.length;i++){
+			if(keys[i].__eq(anObject)) { return i;}
+		}
+		return -1;
+	>
+! !
+
 !Dictionary methodsFor: 'testing'!
 
 includesKey: aKey
-	^keys includes: aKey
+	< return self._positionOfKey_(aKey) >>= 0; >
 ! !
 
 Collection subclass: #SequenceableCollection

+ 21 - 1
st/Kernel-Tests.st

@@ -479,11 +479,17 @@ testAccessing
 	self assert: (d at: 'hello' ifAbsent: [nil]) equals: 'world'.
 	self deny: (d at: 'foo' ifAbsent: [nil]) = 'world'.
 
+	self assert: (d includesKey: 'hello').
+	self deny: (d includesKey: 'foo').
+
 	d at: 1 put: 2.
 	self assert: (d at: 1) equals: 2.
 
 	d at: 1@3 put: 3.
-	self assert: (d at: 1@3) equals: 3
+	self assert: (d at: 1@3) equals: 3.
+
+	self assert: (d includesKey: 1@3).
+	self deny: (d includesKey: 3@1)
 !
 
 testDynamicDictionaries
@@ -558,6 +564,20 @@ testKeys
 	self assert: d keys equals: #(1 2 3)
 !
 
+testPointKey
+	| d |
+
+	d := Dictionary new.
+    
+    d at: 1@1 put: 'foo'.
+	self assert: (d at: 1@1) equals: 'foo'.
+    d at: 1@1 put: 'bar'.
+	self assert: (d at: 1@1) equals: 'bar'.
+    d removeKey: 1@1.
+    self assert: (d at: 1@1 ifAbsent: [ 'baz' ]) equals: 'baz'.
+    self deny: (d includesKey: 1@1)
+!
+
 testPrintString
 	self
 		assert: (Dictionary new