2011/10/5 Nicolas Cellier <[email protected]>: > 2011/10/5 Michael Roberts <[email protected]>: >> I find the thread a bit confused in the sense >> >> Dictionary *new* at: x ifAbsentPut: y >> >> does not make sense (is academic), because the new dictionary will >> never have x as a key. So why profile it and use that as reasoning?... >> >> whereas >> >> myDict at:x ifAbsentPut: y >> >> is more interesting. >> > > I guess in some examples, like when you want to serialize an object > graph with as many nodes as branches (no or few sharing), then the > case of absence is dominating. > Knowing that Mariano is working on Fuel, i guess that could have > biased his judgement. > >> In my experience i am biased by the use of the message in the systems >> that already use it heavily...however it always feels that the use of >> ifAbsentXX is intentionally deferring some code because it expresses >> exceptional cases in the application logic. Especially if the block >> makes an object you don't want to be doing that every time and >> discarding the result irrespective of the presence of the key. It does >> not have to be expensive to make, just not good style near any kind of >> loop. >> >> I know some people that actually don't like the ifAbsentPut: [] >> variant. and prefer to be explicit, of the form >> >> (myDict includesKey: x) ifFalse: [ >> myDict at: x put: y] >> > > Both codes are valid. But note that at:ifAbsentPut: will return the > value, or the newly put value. > So the exact equivalent is ((myDict includesKey: x) > ifTrue: [myDict at: x] > ifFalse: [myDict at: x put: y]) > > However, maybe Mariano is searching for tight inner loop optimization > (we can only guess, because he didn't tell, he should have). > In this case, using ifFalse: is good: because it is inlined by > Compiler, it avoids a BlockClosure creation. > But above code will cost two lookup in both branch, so it will be > pretty bad too, depending on hash evaluation cost and collision rate, > in a majority of cases, worse than block creation time. > I would suggest to Mariano to implement in the Dictionary class of his > choice the single lookup, block free creation code: > > at: key ifAbsentPutValue: aValue > "Answer the value associated with the key. > if key isn't found, associate it first with aValue." > > | index | > ^((array at: (index := self scanFor: key)) > ifNil: [ array at: index put: (key -> aValue). aValue ] > ifNotNil: [:value | value] >
And please, correct me, above code is plain false :) > But, please, please, Mariano, don't change #at:ifAbsentPut: > As other said, conditional execution is useful both for speed and > because in Object world, the state can change. > aBagOfKeys do: [:key | myDict at: key ifAbsentPut: (myStream next)]. > aBagOfKeys do: [:key | myDict at: key ifAbsentPut: [myStream next]]. > won't behave the same if aBagOfKeys has duplicates, will they ? > > Last thing, #at:ifAbsentPut: is absolutely decoupled from > Object>>#value, it does rely on Object>>#value > So don't throw #at:ifAbsentPut: because you don't like Object>>#value. > If you don't like it, don't use it, use a BlockClosure instead. > > Anyway, thanks to Mariano for these questions, they were biased, but > not silly, no question is :) > > Nicolas > >> where y is either a simple value or some expression. >> >> here the test on the key has the same effect as expecting a block in >> the inline ifAbsentPut: [] . It makes both forms basically the same, >> with one being more compact if you prefer that. If ifAbsentPut: did >> not expect the block then i think it would be a bit odd because you >> would have to unroll the code in order to achieve the deferred case >> which i feel is more common. >> >> cheers, >> Mike >> >> On Tue, Oct 4, 2011 at 2:44 PM, Henrik Sperre Johansen >> <[email protected]> wrote: >>> On 04.10.2011 12:52, Mariano Martinez Peck wrote: >>> >>> Hi guys. If I tell you the selector is Dictionary >> #at:ifAbsentPut: what >>> would you expect the second parameter to be? the value. >>> So, one would do: >>> Dictionary new at: #foo ifAbsentPut: 4 >>> >>> But if you see Dictionary >> >>> >>> at: key ifAbsentPut: aBlock >>> "Return the value at the given key. >>> If key is not included in the receiver store the result >>> of evaluating aBlock as new value." >>> >>> ^ self at: key ifAbsent: [self at: key put: aBlock value] >>> >>> so it expects a Block. Ok, we are in Smalltalk, so implementing #value is >>> enough. >>> >>> Well..the previous example works, but only because we have an ugly Object >> >>> value that returns self. >>> If I put instances of subclasses from ProtoObjects (proxies), that do not >>> work anymore. >>> >>> So...my question is we do Dictionary at: #foo put: 4, why #at:ifAbsentPut: >>> expects a block and not directly the value? >>> >>> Sven's reasons are correct I think, it's for efficiency, and elegant lazy >>> initialization of a cache. >>> The same reasons will never hold true when using #at:put:. >>> >>> in which case I need a block instead of the value object directly ? >>> >>> In addition to when you want delayed computation, when you have value >>> objects who redefine/do not define #value ;) >>> >>> Cheers, >>> Henry >>> >> >> >
