The Trunk: Collections-nice.582.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
27 messages Options
12
Reply | Threaded
Open this post in threaded view
|

The Trunk: Collections-nice.582.mcz

commits-2
Nicolas Cellier uploaded a new version of Collections to project The Trunk:
http://source.squeak.org/trunk/Collections-nice.582.mcz

==================== Summary ====================

Name: Collections-nice.582
Author: nice
Time: 19 September 2014, 10:22:13.338 pm
UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
Ancestors: Collections-nice.581

Port TAG-SortFunctions of Travis Griggs from Cincom public store - version (11,tgriggs)

Note that no collation policy were used for String.
The spaceship operator <=> is also required in Kernel-Numbers.

See also the blog http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
 and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html

Note about the cost of these sort functions:
as shown by this mini-bench on cog, using a Symbol costs a bit more (perform:) than a block activation, and monadic block a bit more than dyadic one because activated twice more, but it seems acceptable to me with regard to the great simplification and expressiveness of code :

| collec1 collec2 collec3 |
collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
collec2 := collec1 copy.
collec3 := collec1 copy.
{
[collec1 sort: [:a :b | a abs < b abs]] timeToRun.
[collec2 sort: [:e | e abs] ascending] timeToRun.
[collec3 sort: #abs ascending] timeToRun.
}
 #(345 532 912)

=============== Diff against Collections-nice.581 ===============

Item was added:
+ SortFunction subclass: #ChainedSortFunction
+ instanceVariableNames: 'next'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Collections-Support'!
+
+ !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior: 0!
+ I add to my parent the idea of a "next" function to use when two objects are equal by my particular collator.
+
+ Usage
+
+ SortFunctions can be chained together in primary, secondary, tertiary, etc order using the comma method. Consider a sequence of customer objects, where each customer object responds to the messages firstName, lastName, and age. If we want to sort them lastName first, then firstName, and finally oldest first, we would use an expression like:
+
+ customers sort: #lastName ascending, #firstName ascending, #age descending
+
+ As noted in my super's comment, unary symbols or single arg blocks can be used. One can omit the the ascending methods on arguments (not the receiver), it will default blocks or symbols to be ascending if none is specified. In other words, the above expression could be simplified slightly as
+
+ customers sort: #lastName ascending, #firstName, #age descending
+
+ (note the missing ascending on the #firstName argument)
+
+ Instance Variables
+ next <SortFunction> the next SortFunction to evaluate in the event my evaluator results in equal values.
+
+ !

Item was added:
+ ----- Method: ChainedSortFunction>>, (in category 'converting') -----
+ , aSortFunction
+
+ self next: next , aSortFunction!

Item was added:
+ ----- Method: ChainedSortFunction>>next: (in category 'initialize-release') -----
+ next: anObject
+
+ next := anObject!

Item was added:
+ ----- Method: ChainedSortFunction>>value:value: (in category 'evaluating') -----
+ value: anObject value: bObject
+ "Refinement of the parent behavior. If the result of my collator is 0, then pass on to the next variable to work it out."
+
+ | result |
+ result := (collator value: anObject value: bObject) * direction.
+ ^result isZero
+ ifTrue: [next value: anObject value: bObject]
+ ifFalse: [result < 0]!

Item was added:
+ Object subclass: #SortFunction
+ instanceVariableNames: 'direction collator'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'Collections-Support'!
+
+ !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
+ I am intended to be used in place of two arg sort blocks.
+
+ Usage
+
+ In the following example, an ascending SortFunction is created based on the result of the #first message send to each object.
+ #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
+
+ To sort by the #last element, but descending, the following would be used:
+ #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
+
+ One can use blocks as well. The following sorts in descending order, the sub elements based on the sum of their values.
+ | sumBlock |
+ sumBlock := [:sequence | sequence inject: 0 into: [:sum :each | sum + each]].
+ #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
+
+ One can even use 2 arg blocks, for those cases where the function isn't expressible with objects that respond to < and =. The only catch, is that such a function has to return not true and false, but instead a collation order, values of -1 (for before), 0 (the same) or 1 (to follow). For example:
+
+ | oddBlock |
+ oddBlock :=
+ [:a :b |
+ a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue: [-1] ifFalse: [1]]].
+ #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
+
+ Instance Variables
+ collator <BlockClosure> This is the collation function that must return a -1, 0, or 1. It is usually composed by an initialization method such as sendMessage: or monadicBlock:, but may be set directly.
+ direction <SmallInteger> 1 for ascending, -1 for descending
+
+ !

Item was added:
+ ----- Method: SortFunction class>>ascend (in category 'instance creation') -----
+ ascend
+
+ ^self new ascend!

Item was added:
+ ----- Method: SortFunction class>>descend (in category 'instance creation') -----
+ descend
+
+ ^self new descend!

Item was added:
+ ----- Method: SortFunction>>, (in category 'converting') -----
+ , aSortFunction
+ "Return a new SortFunction which is the concatenation of aSortFunction to me, I will be the primary sort, but if I compare equal, I will defer to the argument."
+
+ ^(ChainedSortFunction new)
+ collator: collator;
+ direction: direction;
+ next: aSortFunction asSortFunction!

Item was added:
+ ----- Method: SortFunction>>asSortFunction (in category 'converting') -----
+ asSortFunction
+
+ ^self!

Item was added:
+ ----- Method: SortFunction>>ascend (in category 'initailize-release') -----
+ ascend
+
+ direction := 1!

Item was added:
+ ----- Method: SortFunction>>collator: (in category 'initailize-release') -----
+ collator: a2ArgBlock
+ "a2ArgBlock MUST return the collation order, -1, 0, or 1"
+
+ collator := a2ArgBlock!

Item was added:
+ ----- Method: SortFunction>>descend (in category 'initailize-release') -----
+ descend
+
+ direction := -1!

Item was added:
+ ----- Method: SortFunction>>direction: (in category 'initailize-release') -----
+ direction: anInteger
+
+ direction := anInteger!

Item was added:
+ ----- Method: SortFunction>>isAscending (in category 'testing') -----
+ isAscending
+
+ ^direction = 1!

Item was added:
+ ----- Method: SortFunction>>isDescending (in category 'testing') -----
+ isDescending
+
+ ^direction = -1!

Item was added:
+ ----- Method: SortFunction>>monadicBlock: (in category 'initailize-release') -----
+ monadicBlock: aSingleArgBlock
+ "Initialze the receiver's collation block to compare the results of evaluating aSingleArgBlock with each argument, and then collate the return values with the <=> method."
+
+ collator := [:a :b | (aSingleArgBlock value: a) <=> (aSingleArgBlock value: b)]!

Item was added:
+ ----- Method: SortFunction>>sendMessage: (in category 'initailize-release') -----
+ sendMessage: aUnarySymbol
+ "Initialze the receiver's collation block to compare the results of sending aUnarySymbol to each argument, and then collate them with the <=> method."
+
+ collator := [:a :b | (a perform: aUnarySymbol) <=> (b perform: aUnarySymbol)]!

Item was added:
+ ----- Method: SortFunction>>toggleDirection (in category 'converting') -----
+ toggleDirection
+ "Invert my current direction, if I'm currently ascending, this will cause me to be descending now, and vice-versa."
+
+ direction := direction * -1!

Item was added:
+ ----- Method: SortFunction>>value:value: (in category 'evaluating') -----
+ value: anObject value: bObject
+ "Masquerade as a two argument block, used by many of the sorting APIs, by returning whether anObject should be placed before bObject or not."
+
+ | result |
+ result := (collator value: anObject value: bObject) * direction.
+ ^result <= 0!

Item was added:
+ ----- Method: String>><=> (in category 'sorting') -----
+ <=> aCharacterArray
+ "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
+ See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
+
+ ^self = aCharacterArray
+ ifTrue: [ 0 ]
+ ifFalse: [self < aCharacterArray
+ ifTrue: [ -1 ]
+ ifFalse: [ 1 ]]!

Item was added:
+ ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
+ asSortFunction
+
+ ^self ascending!

Item was added:
+ ----- Method: Symbol>>ascending (in category 'sorting') -----
+ ascending
+ "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in ascending order using the <=> method."
+ "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
+
+ ^SortFunction ascend sendMessage: self!

Item was added:
+ ----- Method: Symbol>>descending (in category 'sorting') -----
+ descending
+ "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in descending order using the <=> method."
+ "Example: #('abc'  'de' 'fghi') sorted: #size descending"
+
+ ^SortFunction descend sendMessage: self!

Item was added:
+ ----- Method: Text>><=> (in category 'sorting') -----
+ <=> aCharacterArray
+ "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
+ See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
+
+ ^string = aCharacterArray
+ ifTrue: [ 0 ]
+ ifFalse: [string < aCharacterArray asString
+ ifTrue: [ -1 ]
+ ifFalse: [ 1 ]]!


Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Frank Shearar-3
On 19 September 2014 21:24,  <[hidden email]> wrote:

> Nicolas Cellier uploaded a new version of Collections to project The Trunk:
> http://source.squeak.org/trunk/Collections-nice.582.mcz
>
> ==================== Summary ====================
>
> Name: Collections-nice.582
> Author: nice
> Time: 19 September 2014, 10:22:13.338 pm
> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
> Ancestors: Collections-nice.581
>
> Port TAG-SortFunctions of Travis Griggs from Cincom public store - version (11,tgriggs)

Looks cool! What's its licence? MIT? (He says, hopefully)

frank

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Levente Uzonyi-2
In reply to this post by commits-2
Cool. I think the spaceship operator for strings should use
#compare:with:collated: to save a comparison in the most common case.

The performance of symbols would be close to dyadic blocks, if it were
cheap and easy to create a block on the fly.


Levente

On Fri, 19 Sep 2014, [hidden email] wrote:

> Nicolas Cellier uploaded a new version of Collections to project The Trunk:
> http://source.squeak.org/trunk/Collections-nice.582.mcz
>
> ==================== Summary ====================
>
> Name: Collections-nice.582
> Author: nice
> Time: 19 September 2014, 10:22:13.338 pm
> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
> Ancestors: Collections-nice.581
>
> Port TAG-SortFunctions of Travis Griggs from Cincom public store - version (11,tgriggs)
>
> Note that no collation policy were used for String.
> The spaceship operator <=> is also required in Kernel-Numbers.
>
> See also the blog http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
> and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>
> Note about the cost of these sort functions:
> as shown by this mini-bench on cog, using a Symbol costs a bit more (perform:) than a block activation, and monadic block a bit more than dyadic one because activated twice more, but it seems acceptable to me with regard to the great simplification and expressiveness of code :
>
> | collec1 collec2 collec3 |
> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
> collec2 := collec1 copy.
> collec3 := collec1 copy.
> {
> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
> [collec2 sort: [:e | e abs] ascending] timeToRun.
> [collec3 sort: #abs ascending] timeToRun.
> }
> #(345 532 912)
>

bpi
Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

bpi
In reply to this post by commits-2
This is API is really cool. Thanks Travis and Nicolas!

Cheers,
Bernhard

Am 19.09.2014 um 20:24 schrieb [hidden email]:

> Nicolas Cellier uploaded a new version of Collections to project The Trunk:
> http://source.squeak.org/trunk/Collections-nice.582.mcz
>
> ==================== Summary ====================
>
> Name: Collections-nice.582
> Author: nice
> Time: 19 September 2014, 10:22:13.338 pm
> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
> Ancestors: Collections-nice.581
>
> Port TAG-SortFunctions of Travis Griggs from Cincom public store - version (11,tgriggs)
>
> Note that no collation policy were used for String.
> The spaceship operator <=> is also required in Kernel-Numbers.
>
> See also the blog http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
> and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>
> Note about the cost of these sort functions:
> as shown by this mini-bench on cog, using a Symbol costs a bit more (perform:) than a block activation, and monadic block a bit more than dyadic one because activated twice more, but it seems acceptable to me with regard to the great simplification and expressiveness of code :
>
> | collec1 collec2 collec3 |
> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
> collec2 := collec1 copy.
> collec3 := collec1 copy.
> {
> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
> [collec2 sort: [:e | e abs] ascending] timeToRun.
> [collec3 sort: #abs ascending] timeToRun.
> }
> #(345 532 912)
>
> =============== Diff against Collections-nice.581 ===============
>
> Item was added:
> + SortFunction subclass: #ChainedSortFunction
> + instanceVariableNames: 'next'
> + classVariableNames: ''
> + poolDictionaries: ''
> + category: 'Collections-Support'!
> +
> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior: 0!
> + I add to my parent the idea of a "next" function to use when two objects are equal by my particular collator.
> +
> + Usage
> +
> + SortFunctions can be chained together in primary, secondary, tertiary, etc order using the comma method. Consider a sequence of customer objects, where each customer object responds to the messages firstName, lastName, and age. If we want to sort them lastName first, then firstName, and finally oldest first, we would use an expression like:
> +
> + customers sort: #lastName ascending, #firstName ascending, #age descending
> +
> + As noted in my super's comment, unary symbols or single arg blocks can be used. One can omit the the ascending methods on arguments (not the receiver), it will default blocks or symbols to be ascending if none is specified. In other words, the above expression could be simplified slightly as
> +
> + customers sort: #lastName ascending, #firstName, #age descending
> +
> + (note the missing ascending on the #firstName argument)
> +
> + Instance Variables
> + next <SortFunction> the next SortFunction to evaluate in the event my evaluator results in equal values.
> +
> + !
>
> Item was added:
> + ----- Method: ChainedSortFunction>>, (in category 'converting') -----
> + , aSortFunction
> +
> + self next: next , aSortFunction!
>
> Item was added:
> + ----- Method: ChainedSortFunction>>next: (in category 'initialize-release') -----
> + next: anObject
> +
> + next := anObject!
>
> Item was added:
> + ----- Method: ChainedSortFunction>>value:value: (in category 'evaluating') -----
> + value: anObject value: bObject
> + "Refinement of the parent behavior. If the result of my collator is 0, then pass on to the next variable to work it out."
> +
> + | result |
> + result := (collator value: anObject value: bObject) * direction.
> + ^result isZero
> + ifTrue: [next value: anObject value: bObject]
> + ifFalse: [result < 0]!
>
> Item was added:
> + Object subclass: #SortFunction
> + instanceVariableNames: 'direction collator'
> + classVariableNames: ''
> + poolDictionaries: ''
> + category: 'Collections-Support'!
> +
> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
> + I am intended to be used in place of two arg sort blocks.
> +
> + Usage
> +
> + In the following example, an ascending SortFunction is created based on the result of the #first message send to each object.
> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
> +
> + To sort by the #last element, but descending, the following would be used:
> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
> +
> + One can use blocks as well. The following sorts in descending order, the sub elements based on the sum of their values.
> + | sumBlock |
> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each | sum + each]].
> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
> +
> + One can even use 2 arg blocks, for those cases where the function isn't expressible with objects that respond to < and =. The only catch, is that such a function has to return not true and false, but instead a collation order, values of -1 (for before), 0 (the same) or 1 (to follow). For example:
> +
> + | oddBlock |
> + oddBlock :=
> + [:a :b |
> + a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue: [-1] ifFalse: [1]]].
> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
> +
> + Instance Variables
> + collator <BlockClosure> This is the collation function that must return a -1, 0, or 1. It is usually composed by an initialization method such as sendMessage: or monadicBlock:, but may be set directly.
> + direction <SmallInteger> 1 for ascending, -1 for descending
> +
> + !
>
> Item was added:
> + ----- Method: SortFunction class>>ascend (in category 'instance creation') -----
> + ascend
> +
> + ^self new ascend!
>
> Item was added:
> + ----- Method: SortFunction class>>descend (in category 'instance creation') -----
> + descend
> +
> + ^self new descend!
>
> Item was added:
> + ----- Method: SortFunction>>, (in category 'converting') -----
> + , aSortFunction
> + "Return a new SortFunction which is the concatenation of aSortFunction to me, I will be the primary sort, but if I compare equal, I will defer to the argument."
> +
> + ^(ChainedSortFunction new)
> + collator: collator;
> + direction: direction;
> + next: aSortFunction asSortFunction!
>
> Item was added:
> + ----- Method: SortFunction>>asSortFunction (in category 'converting') -----
> + asSortFunction
> +
> + ^self!
>
> Item was added:
> + ----- Method: SortFunction>>ascend (in category 'initailize-release') -----
> + ascend
> +
> + direction := 1!
>
> Item was added:
> + ----- Method: SortFunction>>collator: (in category 'initailize-release') -----
> + collator: a2ArgBlock
> + "a2ArgBlock MUST return the collation order, -1, 0, or 1"
> +
> + collator := a2ArgBlock!
>
> Item was added:
> + ----- Method: SortFunction>>descend (in category 'initailize-release') -----
> + descend
> +
> + direction := -1!
>
> Item was added:
> + ----- Method: SortFunction>>direction: (in category 'initailize-release') -----
> + direction: anInteger
> +
> + direction := anInteger!
>
> Item was added:
> + ----- Method: SortFunction>>isAscending (in category 'testing') -----
> + isAscending
> +
> + ^direction = 1!
>
> Item was added:
> + ----- Method: SortFunction>>isDescending (in category 'testing') -----
> + isDescending
> +
> + ^direction = -1!
>
> Item was added:
> + ----- Method: SortFunction>>monadicBlock: (in category 'initailize-release') -----
> + monadicBlock: aSingleArgBlock
> + "Initialze the receiver's collation block to compare the results of evaluating aSingleArgBlock with each argument, and then collate the return values with the <=> method."
> +
> + collator := [:a :b | (aSingleArgBlock value: a) <=> (aSingleArgBlock value: b)]!
>
> Item was added:
> + ----- Method: SortFunction>>sendMessage: (in category 'initailize-release') -----
> + sendMessage: aUnarySymbol
> + "Initialze the receiver's collation block to compare the results of sending aUnarySymbol to each argument, and then collate them with the <=> method."
> +
> + collator := [:a :b | (a perform: aUnarySymbol) <=> (b perform: aUnarySymbol)]!
>
> Item was added:
> + ----- Method: SortFunction>>toggleDirection (in category 'converting') -----
> + toggleDirection
> + "Invert my current direction, if I'm currently ascending, this will cause me to be descending now, and vice-versa."
> +
> + direction := direction * -1!
>
> Item was added:
> + ----- Method: SortFunction>>value:value: (in category 'evaluating') -----
> + value: anObject value: bObject
> + "Masquerade as a two argument block, used by many of the sorting APIs, by returning whether anObject should be placed before bObject or not."
> +
> + | result |
> + result := (collator value: anObject value: bObject) * direction.
> + ^result <= 0!
>
> Item was added:
> + ----- Method: String>><=> (in category 'sorting') -----
> + <=> aCharacterArray
> + "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
> + See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
> +
> + ^self = aCharacterArray
> + ifTrue: [ 0 ]
> + ifFalse: [self < aCharacterArray
> + ifTrue: [ -1 ]
> + ifFalse: [ 1 ]]!
>
> Item was added:
> + ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
> + asSortFunction
> +
> + ^self ascending!
>
> Item was added:
> + ----- Method: Symbol>>ascending (in category 'sorting') -----
> + ascending
> + "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in ascending order using the <=> method."
> + "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
> +
> + ^SortFunction ascend sendMessage: self!
>
> Item was added:
> + ----- Method: Symbol>>descending (in category 'sorting') -----
> + descending
> + "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in descending order using the <=> method."
> + "Example: #('abc'  'de' 'fghi') sorted: #size descending"
> +
> + ^SortFunction descend sendMessage: self!
>
> Item was added:
> + ----- Method: Text>><=> (in category 'sorting') -----
> + <=> aCharacterArray
> + "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
> + See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
> +
> + ^string = aCharacterArray
> + ifTrue: [ 0 ]
> + ifFalse: [string < aCharacterArray asString
> + ifTrue: [ -1 ]
> + ifFalse: [ 1 ]]!
>
>


Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Eliot Miranda-2
In reply to this post by Levente Uzonyi-2
Hi Levente,

On Sat, Sep 20, 2014 at 8:24 AM, Levente Uzonyi <[hidden email]> wrote:
Cool. I think the spaceship operator for strings should use #compare:with:collated: to save a comparison in the most common case.

The performance of symbols would be close to dyadic blocks, if it were cheap and easy to create a block on the fly.

I don't know what cheap enough in this case is but Spur is over 5 times faster at creating blocks than the existing Cog V3 VM:


[1 to: 1,000,000,000 do: [:i| [nil class]]] timeToRun

Spur: 5,950ms
Cog: 31,951ms

31951 / 5950.0                 5.37
5950 - 31951 / 319.51    -81.4%

So ignoring the loop overhead, Spur creates a block in less than 6 nanoseconds on a 2.2GHz MBP Core i7.


A slightly more realistic example which creates a new home context for every block created is


[1 to: 10,000,000 do: [:i| [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value.]] timeToRun 

Spur: 2,568ms
Cog: 11,601ms

11601 / 2568.0 4.51752336448598
2568 - 11601 / 116.01 -77.8639772433411

So here Spur creates two closures and a context in 2.568 / 100,000,000 = 26 nsecs, compared to 160 for Cog V3.

Levente

On Fri, 19 Sep 2014, [hidden email] wrote:

Nicolas Cellier uploaded a new version of Collections to project The Trunk:
http://source.squeak.org/trunk/Collections-nice.582.mcz

==================== Summary ====================

Name: Collections-nice.582
Author: nice
Time: 19 September 2014, 10:22:13.338 pm
UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
Ancestors: Collections-nice.581

Port TAG-SortFunctions of Travis Griggs from Cincom public store - version (11,tgriggs)

Note that no collation policy were used for String.
The spaceship operator <=> is also required in Kernel-Numbers.

See also the blog http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html

Note about the cost of these sort functions:
as shown by this mini-bench on cog, using a Symbol costs a bit more (perform:) than a block activation, and monadic block a bit more than dyadic one because activated twice more, but it seems acceptable to me with regard to the great simplification and expressiveness of code :

| collec1 collec2 collec3 |
collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
collec2 := collec1 copy.
collec3 := collec1 copy.
{
[collec1 sort: [:a :b | a abs < b abs]] timeToRun.
[collec2 sort: [:e | e abs] ascending] timeToRun.
[collec3 sort: #abs ascending] timeToRun.
}
#(345 532 912)





--
best,
Eliot


Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Levente Uzonyi-2
Hi Eliot,

On Sat, 20 Sep 2014, Eliot Miranda wrote:

> Hi Levente,
>
> On Sat, Sep 20, 2014 at 8:24 AM, Levente Uzonyi <[hidden email]> wrote:
>       Cool. I think the spaceship operator for strings should use #compare:with:collated: to save a comparison in the most common case.
>
>       The performance of symbols would be close to dyadic blocks, if it were cheap and easy to create a block on the fly.
>
>
> I don't know what cheap enough in this case is but Spur is over 5 times faster at creating blocks than the existing Cog V3 VM:

I meant compiling a sortblock, which can be passed to #sort: or #sorted:.
It seems to be too expensive to be used by default:

[ Compiler evaluate: ('[ :a :b | a {1} {2} b {1} ]' format: { #abs. #< }) ] bench. '3,560 per second.'

| c d |
c := (1 to: 20) collect: [:i | 1000000 atRandom-500000].
d := c copy.
{
[ d copyFrom: c ] bench.
[ d copyFrom: c; sort: [:a :b | a abs < b abs] ] bench.
[ d copyFrom: c; sort: (Compiler evaluate: ('[ :a :b | a {1} {2} b {1} ]' format: { #abs. #< })) ] bench.
}.
#('9,720,000 per second.' '125,000 per second.' '7,310 per second.')

But there could be a special message which compiles an optimized block.
Something like

#(-1 0 1 2) sort: #abs ascending compiled

where #compiled returns the block [ :a :b | a abs < b abs ].
It could be useful when the collection is large enough:

| c d |
c := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
d := c copy.
{
[ d copyFrom: c ] bench.
[ d copyFrom: c; sort: [:a :b | a abs < b abs] ] bench.
[ d copyFrom: c; sort: (Compiler evaluate: ('[ :a :b | a {1} {2} b {1} ]' format: { #abs. #< })) ] bench.
}.
#('1,390 per second.' '3.38 per second.' '3.58 per second.')


Levente

P.S.: The numbers below are impressive. :)

>
>
> [1 to: 1,000,000,000 do: [:i| [nil class]]] timeToRun
>
> Spur: 5,950ms
> Cog: 31,951ms
>
> 31951 / 5950.0                 5.37
> 5950 - 31951 / 319.51    -81.4%
>
> So ignoring the loop overhead, Spur creates a block in less than 6 nanoseconds on a 2.2GHz MBP Core i7.
>
>
> A slightly more realistic example which creates a new home context for every block created is
>
>
> [1 to: 10,000,000 do: [:i| [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]]
> value. [[nil class]] value.]] timeToRun 
>
> Spur: 2,568ms
> Cog: 11,601ms
>
> 11601 / 2568.0 4.51752336448598
> 2568 - 11601 / 116.01 -77.8639772433411
>
> So here Spur creates two closures and a context in 2.568 / 100,000,000 = 26 nsecs, compared to 160 for Cog V3.
>
>       Levente
>
>       On Fri, 19 Sep 2014, [hidden email] wrote:
>
>             Nicolas Cellier uploaded a new version of Collections to project The Trunk:
>             http://source.squeak.org/trunk/Collections-nice.582.mcz
>
>             ==================== Summary ====================
>
>             Name: Collections-nice.582
>             Author: nice
>             Time: 19 September 2014, 10:22:13.338 pm
>             UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
>             Ancestors: Collections-nice.581
>
>             Port TAG-SortFunctions of Travis Griggs from Cincom public store - version (11,tgriggs)
>
>             Note that no collation policy were used for String.
>             The spaceship operator <=> is also required in Kernel-Numbers.
>
>             See also the blog http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>             and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>
>             Note about the cost of these sort functions:
>             as shown by this mini-bench on cog, using a Symbol costs a bit more (perform:) than a block activation, and monadic block a bit more than dyadic one because activated twice more, but
>             it seems acceptable to me with regard to the great simplification and expressiveness of code :
>
>             | collec1 collec2 collec3 |
>             collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
>             collec2 := collec1 copy.
>             collec3 := collec1 copy.
>             {
>             [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
>             [collec2 sort: [:e | e abs] ascending] timeToRun.
>             [collec3 sort: #abs ascending] timeToRun.
>             }
>             #(345 532 912)
>
>
>
>
>
> --
> best,Eliot
>
>

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Eliot Miranda-2
Hi Levente,

On Sat, Sep 20, 2014 at 6:50 PM, Levente Uzonyi <[hidden email]> wrote:
Hi Eliot,

On Sat, 20 Sep 2014, Eliot Miranda wrote:

Hi Levente,

On Sat, Sep 20, 2014 at 8:24 AM, Levente Uzonyi <[hidden email]> wrote:
      Cool. I think the spaceship operator for strings should use #compare:with:collated: to save a comparison in the most common case.

      The performance of symbols would be close to dyadic blocks, if it were cheap and easy to create a block on the fly.


I don't know what cheap enough in this case is but Spur is over 5 times faster at creating blocks than the existing Cog V3 VM:

I meant compiling a sortblock, which can be passed to #sort: or #sorted:. It seems to be too expensive to be used by default:

[ Compiler evaluate: ('[ :a :b | a {1} {2} b {1} ]' format: { #abs. #< }) ] bench. '3,560 per second.'

ah, of course!  Now I understand.

If we drop down to the bytecode level, in this case using my MethodMassage bytecode (dis)assembler we can up the performance considerably.  First the baseline on my machine (in Spur):

[ Compiler evaluate: ('[ :a :b | a {1} {2} b {1} ]' format: { #abs. #< }) ] bench 11,800 per second.

(c.f. [ Compiler evaluate: ('[ :a :b | a {1} {2} b {1} ]' format: { #abs. #< }) ] bench 10409.9 per second. Cog)

Now using the assembler, where it is easy to construct the message sequence you want.  Since the assembler answers a method we need to evaluate it to produce the closure:

[nil
withArgs: #()
executeMethod:
(AssemblerMethod new
methodClass: UndefinedObject;
pushClosureCopyNumCopiedValues: 0 numArgs: 2 blockSize: 'L1';
pushTemporaryVariable: 0;
send: #abs;
pushTemporaryVariable: 1;
send: #abs;
send: #<;
blockReturnTop;
label: 'L1';
methodReturnTop;
yourself) assemble] bench '27,100 per second.'

So it's more than twice as fast but at ~ 36 microseconds per block creation it may well be too expensive.


| c d |
c := (1 to: 20) collect: [:i | 1000000 atRandom-500000].
d := c copy.
{
[ d copyFrom: c ] bench.
[ d copyFrom: c; sort: [:a :b | a abs < b abs] ] bench.
[ d copyFrom: c; sort: (Compiler evaluate: ('[ :a :b | a {1} {2} b {1} ]' format: { #abs. #< })) ] bench.
}.
#('9,720,000 per second.' '125,000 per second.' '7,310 per second.')

But there could be a special message which compiles an optimized block. Something like

#(-1 0 1 2) sort: #abs ascending compiled

where #compiled returns the block [ :a :b | a abs < b abs ].
It could be useful when the collection is large enough:

| c d |
c := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
d := c copy.
{
[ d copyFrom: c ] bench.
[ d copyFrom: c; sort: [:a :b | a abs < b abs] ] bench.
[ d copyFrom: c; sort: (Compiler evaluate: ('[ :a :b | a {1} {2} b {1} ]' format: { #abs. #< })) ] bench.
}.
#('1,390 per second.' '3.38 per second.' '3.58 per second.')


Levente

P.S.: The numbers below are impressive. :)




[1 to: 1,000,000,000 do: [:i| [nil class]]] timeToRun

Spur: 5,950ms
Cog: 31,951ms

<a href="tel:31951%20%2F%205950.0" value="+13195159500" target="_blank">31951 / 5950.0                 5.37
5950 - <a href="tel:31951%20%2F%20319.51" value="+13195131951" target="_blank">31951 / 319.51    -81.4%

So ignoring the loop overhead, Spur creates a block in less than 6 nanoseconds on a 2.2GHz MBP Core i7.


A slightly more realistic example which creates a new home context for every block created is


[1 to: 10,000,000 do: [:i| [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]] value. [[nil class]]
value. [[nil class]] value.]] timeToRun 

Spur: 2,568ms
Cog: 11,601ms

11601 / 2568.0 4.51752336448598
2568 - 11601 / 116.01 -77.8639772433411

So here Spur creates two closures and a context in 2.568 / 100,000,000 = 26 nsecs, compared to 160 for Cog V3.

      Levente

      On Fri, 19 Sep 2014, [hidden email] wrote:

            Nicolas Cellier uploaded a new version of Collections to project The Trunk:
            http://source.squeak.org/trunk/Collections-nice.582.mcz

            ==================== Summary ====================

            Name: Collections-nice.582
            Author: nice
            Time: 19 September 2014, 10:22:13.338 pm
            UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
            Ancestors: Collections-nice.581

            Port TAG-SortFunctions of Travis Griggs from Cincom public store - version (11,tgriggs)

            Note that no collation policy were used for String.
            The spaceship operator <=> is also required in Kernel-Numbers.

            See also the blog http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
            and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html

            Note about the cost of these sort functions:
            as shown by this mini-bench on cog, using a Symbol costs a bit more (perform:) than a block activation, and monadic block a bit more than dyadic one because activated twice more, but
            it seems acceptable to me with regard to the great simplification and expressiveness of code :

            | collec1 collec2 collec3 |
            collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
            collec2 := collec1 copy.
            collec3 := collec1 copy.
            {
            [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
            [collec2 sort: [:e | e abs] ascending] timeToRun.
            [collec3 sort: #abs ascending] timeToRun.
            }
            #(345 532 912)





--
best,Eliot







--
best,
Eliot


Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Chris Muller-3
In reply to this post by commits-2
What a *gorgeous* solution to multi-level sorting!  I love this!!

Now I need to update some of my images to latest trunk just so I can get this..

On Fri, Sep 19, 2014 at 3:24 PM,  <[hidden email]> wrote:

> Nicolas Cellier uploaded a new version of Collections to project The Trunk:
> http://source.squeak.org/trunk/Collections-nice.582.mcz
>
> ==================== Summary ====================
>
> Name: Collections-nice.582
> Author: nice
> Time: 19 September 2014, 10:22:13.338 pm
> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
> Ancestors: Collections-nice.581
>
> Port TAG-SortFunctions of Travis Griggs from Cincom public store - version (11,tgriggs)
>
> Note that no collation policy were used for String.
> The spaceship operator <=> is also required in Kernel-Numbers.
>
> See also the blog http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>  and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>
> Note about the cost of these sort functions:
> as shown by this mini-bench on cog, using a Symbol costs a bit more (perform:) than a block activation, and monadic block a bit more than dyadic one because activated twice more, but it seems acceptable to me with regard to the great simplification and expressiveness of code :
>
> | collec1 collec2 collec3 |
> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
> collec2 := collec1 copy.
> collec3 := collec1 copy.
> {
> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
> [collec2 sort: [:e | e abs] ascending] timeToRun.
> [collec3 sort: #abs ascending] timeToRun.
> }
>  #(345 532 912)
>
> =============== Diff against Collections-nice.581 ===============
>
> Item was added:
> + SortFunction subclass: #ChainedSortFunction
> +       instanceVariableNames: 'next'
> +       classVariableNames: ''
> +       poolDictionaries: ''
> +       category: 'Collections-Support'!
> +
> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior: 0!
> + I add to my parent the idea of a "next" function to use when two objects are equal by my particular collator.
> +
> + Usage
> +
> + SortFunctions can be chained together in primary, secondary, tertiary, etc order using the comma method. Consider a sequence of customer objects, where each customer object responds to the messages firstName, lastName, and age. If we want to sort them lastName first, then firstName, and finally oldest first, we would use an expression like:
> +
> + customers sort: #lastName ascending, #firstName ascending, #age descending
> +
> + As noted in my super's comment, unary symbols or single arg blocks can be used. One can omit the the ascending methods on arguments (not the receiver), it will default blocks or symbols to be ascending if none is specified. In other words, the above expression could be simplified slightly as
> +
> + customers sort: #lastName ascending, #firstName, #age descending
> +
> + (note the missing ascending on the #firstName argument)
> +
> + Instance Variables
> +       next    <SortFunction>  the next SortFunction to evaluate in the event my evaluator results in equal values.
> +
> + !
>
> Item was added:
> + ----- Method: ChainedSortFunction>>, (in category 'converting') -----
> + , aSortFunction
> +
> +       self next: next , aSortFunction!
>
> Item was added:
> + ----- Method: ChainedSortFunction>>next: (in category 'initialize-release') -----
> + next: anObject
> +
> +       next := anObject!
>
> Item was added:
> + ----- Method: ChainedSortFunction>>value:value: (in category 'evaluating') -----
> + value: anObject value: bObject
> +       "Refinement of the parent behavior. If the result of my collator is 0, then pass on to the next variable to work it out."
> +
> +       | result |
> +       result := (collator value: anObject value: bObject) * direction.
> +       ^result isZero
> +               ifTrue: [next value: anObject value: bObject]
> +               ifFalse: [result < 0]!
>
> Item was added:
> + Object subclass: #SortFunction
> +       instanceVariableNames: 'direction collator'
> +       classVariableNames: ''
> +       poolDictionaries: ''
> +       category: 'Collections-Support'!
> +
> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
> + I am intended to be used in place of two arg sort blocks.
> +
> + Usage
> +
> + In the following example, an ascending SortFunction is created based on the result of the #first message send to each object.
> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
> +
> + To sort by the #last element, but descending, the following would be used:
> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
> +
> + One can use blocks as well. The following sorts in descending order, the sub elements based on the sum of their values.
> + | sumBlock |
> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each | sum + each]].
> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
> +
> + One can even use 2 arg blocks, for those cases where the function isn't expressible with objects that respond to < and =. The only catch, is that such a function has to return not true and false, but instead a collation order, values of -1 (for before), 0 (the same) or 1 (to follow). For example:
> +
> + | oddBlock |
> + oddBlock :=
> +               [:a :b |
> +               a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue: [-1] ifFalse: [1]]].
> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
> +
> + Instance Variables
> +       collator        <BlockClosure>  This is the collation function that must return a -1, 0, or 1. It is usually composed by an initialization method such as sendMessage: or monadicBlock:, but may be set directly.
> +       direction       <SmallInteger>  1 for ascending, -1 for descending
> +
> + !
>
> Item was added:
> + ----- Method: SortFunction class>>ascend (in category 'instance creation') -----
> + ascend
> +
> +       ^self new ascend!
>
> Item was added:
> + ----- Method: SortFunction class>>descend (in category 'instance creation') -----
> + descend
> +
> +       ^self new descend!
>
> Item was added:
> + ----- Method: SortFunction>>, (in category 'converting') -----
> + , aSortFunction
> +       "Return a new SortFunction which is the concatenation of aSortFunction to me, I will be the primary sort, but if I compare equal, I will defer to the argument."
> +
> +       ^(ChainedSortFunction new)
> +               collator: collator;
> +               direction: direction;
> +               next: aSortFunction asSortFunction!
>
> Item was added:
> + ----- Method: SortFunction>>asSortFunction (in category 'converting') -----
> + asSortFunction
> +
> +       ^self!
>
> Item was added:
> + ----- Method: SortFunction>>ascend (in category 'initailize-release') -----
> + ascend
> +
> +       direction := 1!
>
> Item was added:
> + ----- Method: SortFunction>>collator: (in category 'initailize-release') -----
> + collator: a2ArgBlock
> +       "a2ArgBlock MUST return the collation order, -1, 0, or 1"
> +
> +       collator := a2ArgBlock!
>
> Item was added:
> + ----- Method: SortFunction>>descend (in category 'initailize-release') -----
> + descend
> +
> +       direction := -1!
>
> Item was added:
> + ----- Method: SortFunction>>direction: (in category 'initailize-release') -----
> + direction: anInteger
> +
> +       direction := anInteger!
>
> Item was added:
> + ----- Method: SortFunction>>isAscending (in category 'testing') -----
> + isAscending
> +
> +       ^direction = 1!
>
> Item was added:
> + ----- Method: SortFunction>>isDescending (in category 'testing') -----
> + isDescending
> +
> +       ^direction = -1!
>
> Item was added:
> + ----- Method: SortFunction>>monadicBlock: (in category 'initailize-release') -----
> + monadicBlock: aSingleArgBlock
> +       "Initialze the receiver's collation block to compare the results of evaluating aSingleArgBlock with each argument, and then collate the return values with the <=> method."
> +
> +       collator := [:a :b | (aSingleArgBlock value: a) <=> (aSingleArgBlock value: b)]!
>
> Item was added:
> + ----- Method: SortFunction>>sendMessage: (in category 'initailize-release') -----
> + sendMessage: aUnarySymbol
> +       "Initialze the receiver's collation block to compare the results of sending aUnarySymbol to each argument, and then collate them with the <=> method."
> +
> +       collator := [:a :b | (a perform: aUnarySymbol) <=> (b perform: aUnarySymbol)]!
>
> Item was added:
> + ----- Method: SortFunction>>toggleDirection (in category 'converting') -----
> + toggleDirection
> +       "Invert my current direction, if I'm currently ascending, this will cause me to be descending now, and vice-versa."
> +
> +       direction := direction * -1!
>
> Item was added:
> + ----- Method: SortFunction>>value:value: (in category 'evaluating') -----
> + value: anObject value: bObject
> +       "Masquerade as a two argument block, used by many of the sorting APIs, by returning whether anObject should be placed before bObject or not."
> +
> +       | result |
> +       result := (collator value: anObject value: bObject) * direction.
> +       ^result <= 0!
>
> Item was added:
> + ----- Method: String>><=> (in category 'sorting') -----
> + <=> aCharacterArray
> +       "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
> +
> +       ^self = aCharacterArray
> +               ifTrue: [ 0 ]
> +               ifFalse: [self < aCharacterArray
> +                       ifTrue: [ -1 ]
> +                       ifFalse: [ 1 ]]!
>
> Item was added:
> + ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
> + asSortFunction
> +
> +       ^self ascending!
>
> Item was added:
> + ----- Method: Symbol>>ascending (in category 'sorting') -----
> + ascending
> +       "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in ascending order using the <=> method."
> +       "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
> +
> +       ^SortFunction ascend sendMessage: self!
>
> Item was added:
> + ----- Method: Symbol>>descending (in category 'sorting') -----
> + descending
> +       "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in descending order using the <=> method."
> +       "Example: #('abc'  'de' 'fghi') sorted: #size descending"
> +
> +       ^SortFunction descend sendMessage: self!
>
> Item was added:
> + ----- Method: Text>><=> (in category 'sorting') -----
> + <=> aCharacterArray
> +       "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
> +
> +       ^string = aCharacterArray
> +               ifTrue: [ 0 ]
> +               ifFalse: [string < aCharacterArray asString
> +                       ifTrue: [ -1 ]
> +                       ifFalse: [ 1 ]]!
>
>

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Frank Shearar-3
Yes, it looks very very nice.

What's its licence again?

frank

On 21 September 2014 22:46, Chris Muller <[hidden email]> wrote:

> What a *gorgeous* solution to multi-level sorting!  I love this!!
>
> Now I need to update some of my images to latest trunk just so I can get this..
>
> On Fri, Sep 19, 2014 at 3:24 PM,  <[hidden email]> wrote:
>> Nicolas Cellier uploaded a new version of Collections to project The Trunk:
>> http://source.squeak.org/trunk/Collections-nice.582.mcz
>>
>> ==================== Summary ====================
>>
>> Name: Collections-nice.582
>> Author: nice
>> Time: 19 September 2014, 10:22:13.338 pm
>> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
>> Ancestors: Collections-nice.581
>>
>> Port TAG-SortFunctions of Travis Griggs from Cincom public store - version (11,tgriggs)
>>
>> Note that no collation policy were used for String.
>> The spaceship operator <=> is also required in Kernel-Numbers.
>>
>> See also the blog http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>>  and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>>
>> Note about the cost of these sort functions:
>> as shown by this mini-bench on cog, using a Symbol costs a bit more (perform:) than a block activation, and monadic block a bit more than dyadic one because activated twice more, but it seems acceptable to me with regard to the great simplification and expressiveness of code :
>>
>> | collec1 collec2 collec3 |
>> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
>> collec2 := collec1 copy.
>> collec3 := collec1 copy.
>> {
>> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
>> [collec2 sort: [:e | e abs] ascending] timeToRun.
>> [collec3 sort: #abs ascending] timeToRun.
>> }
>>  #(345 532 912)
>>
>> =============== Diff against Collections-nice.581 ===============
>>
>> Item was added:
>> + SortFunction subclass: #ChainedSortFunction
>> +       instanceVariableNames: 'next'
>> +       classVariableNames: ''
>> +       poolDictionaries: ''
>> +       category: 'Collections-Support'!
>> +
>> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior: 0!
>> + I add to my parent the idea of a "next" function to use when two objects are equal by my particular collator.
>> +
>> + Usage
>> +
>> + SortFunctions can be chained together in primary, secondary, tertiary, etc order using the comma method. Consider a sequence of customer objects, where each customer object responds to the messages firstName, lastName, and age. If we want to sort them lastName first, then firstName, and finally oldest first, we would use an expression like:
>> +
>> + customers sort: #lastName ascending, #firstName ascending, #age descending
>> +
>> + As noted in my super's comment, unary symbols or single arg blocks can be used. One can omit the the ascending methods on arguments (not the receiver), it will default blocks or symbols to be ascending if none is specified. In other words, the above expression could be simplified slightly as
>> +
>> + customers sort: #lastName ascending, #firstName, #age descending
>> +
>> + (note the missing ascending on the #firstName argument)
>> +
>> + Instance Variables
>> +       next    <SortFunction>  the next SortFunction to evaluate in the event my evaluator results in equal values.
>> +
>> + !
>>
>> Item was added:
>> + ----- Method: ChainedSortFunction>>, (in category 'converting') -----
>> + , aSortFunction
>> +
>> +       self next: next , aSortFunction!
>>
>> Item was added:
>> + ----- Method: ChainedSortFunction>>next: (in category 'initialize-release') -----
>> + next: anObject
>> +
>> +       next := anObject!
>>
>> Item was added:
>> + ----- Method: ChainedSortFunction>>value:value: (in category 'evaluating') -----
>> + value: anObject value: bObject
>> +       "Refinement of the parent behavior. If the result of my collator is 0, then pass on to the next variable to work it out."
>> +
>> +       | result |
>> +       result := (collator value: anObject value: bObject) * direction.
>> +       ^result isZero
>> +               ifTrue: [next value: anObject value: bObject]
>> +               ifFalse: [result < 0]!
>>
>> Item was added:
>> + Object subclass: #SortFunction
>> +       instanceVariableNames: 'direction collator'
>> +       classVariableNames: ''
>> +       poolDictionaries: ''
>> +       category: 'Collections-Support'!
>> +
>> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
>> + I am intended to be used in place of two arg sort blocks.
>> +
>> + Usage
>> +
>> + In the following example, an ascending SortFunction is created based on the result of the #first message send to each object.
>> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
>> +
>> + To sort by the #last element, but descending, the following would be used:
>> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
>> +
>> + One can use blocks as well. The following sorts in descending order, the sub elements based on the sum of their values.
>> + | sumBlock |
>> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each | sum + each]].
>> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
>> +
>> + One can even use 2 arg blocks, for those cases where the function isn't expressible with objects that respond to < and =. The only catch, is that such a function has to return not true and false, but instead a collation order, values of -1 (for before), 0 (the same) or 1 (to follow). For example:
>> +
>> + | oddBlock |
>> + oddBlock :=
>> +               [:a :b |
>> +               a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue: [-1] ifFalse: [1]]].
>> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
>> +
>> + Instance Variables
>> +       collator        <BlockClosure>  This is the collation function that must return a -1, 0, or 1. It is usually composed by an initialization method such as sendMessage: or monadicBlock:, but may be set directly.
>> +       direction       <SmallInteger>  1 for ascending, -1 for descending
>> +
>> + !
>>
>> Item was added:
>> + ----- Method: SortFunction class>>ascend (in category 'instance creation') -----
>> + ascend
>> +
>> +       ^self new ascend!
>>
>> Item was added:
>> + ----- Method: SortFunction class>>descend (in category 'instance creation') -----
>> + descend
>> +
>> +       ^self new descend!
>>
>> Item was added:
>> + ----- Method: SortFunction>>, (in category 'converting') -----
>> + , aSortFunction
>> +       "Return a new SortFunction which is the concatenation of aSortFunction to me, I will be the primary sort, but if I compare equal, I will defer to the argument."
>> +
>> +       ^(ChainedSortFunction new)
>> +               collator: collator;
>> +               direction: direction;
>> +               next: aSortFunction asSortFunction!
>>
>> Item was added:
>> + ----- Method: SortFunction>>asSortFunction (in category 'converting') -----
>> + asSortFunction
>> +
>> +       ^self!
>>
>> Item was added:
>> + ----- Method: SortFunction>>ascend (in category 'initailize-release') -----
>> + ascend
>> +
>> +       direction := 1!
>>
>> Item was added:
>> + ----- Method: SortFunction>>collator: (in category 'initailize-release') -----
>> + collator: a2ArgBlock
>> +       "a2ArgBlock MUST return the collation order, -1, 0, or 1"
>> +
>> +       collator := a2ArgBlock!
>>
>> Item was added:
>> + ----- Method: SortFunction>>descend (in category 'initailize-release') -----
>> + descend
>> +
>> +       direction := -1!
>>
>> Item was added:
>> + ----- Method: SortFunction>>direction: (in category 'initailize-release') -----
>> + direction: anInteger
>> +
>> +       direction := anInteger!
>>
>> Item was added:
>> + ----- Method: SortFunction>>isAscending (in category 'testing') -----
>> + isAscending
>> +
>> +       ^direction = 1!
>>
>> Item was added:
>> + ----- Method: SortFunction>>isDescending (in category 'testing') -----
>> + isDescending
>> +
>> +       ^direction = -1!
>>
>> Item was added:
>> + ----- Method: SortFunction>>monadicBlock: (in category 'initailize-release') -----
>> + monadicBlock: aSingleArgBlock
>> +       "Initialze the receiver's collation block to compare the results of evaluating aSingleArgBlock with each argument, and then collate the return values with the <=> method."
>> +
>> +       collator := [:a :b | (aSingleArgBlock value: a) <=> (aSingleArgBlock value: b)]!
>>
>> Item was added:
>> + ----- Method: SortFunction>>sendMessage: (in category 'initailize-release') -----
>> + sendMessage: aUnarySymbol
>> +       "Initialze the receiver's collation block to compare the results of sending aUnarySymbol to each argument, and then collate them with the <=> method."
>> +
>> +       collator := [:a :b | (a perform: aUnarySymbol) <=> (b perform: aUnarySymbol)]!
>>
>> Item was added:
>> + ----- Method: SortFunction>>toggleDirection (in category 'converting') -----
>> + toggleDirection
>> +       "Invert my current direction, if I'm currently ascending, this will cause me to be descending now, and vice-versa."
>> +
>> +       direction := direction * -1!
>>
>> Item was added:
>> + ----- Method: SortFunction>>value:value: (in category 'evaluating') -----
>> + value: anObject value: bObject
>> +       "Masquerade as a two argument block, used by many of the sorting APIs, by returning whether anObject should be placed before bObject or not."
>> +
>> +       | result |
>> +       result := (collator value: anObject value: bObject) * direction.
>> +       ^result <= 0!
>>
>> Item was added:
>> + ----- Method: String>><=> (in category 'sorting') -----
>> + <=> aCharacterArray
>> +       "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>> +
>> +       ^self = aCharacterArray
>> +               ifTrue: [ 0 ]
>> +               ifFalse: [self < aCharacterArray
>> +                       ifTrue: [ -1 ]
>> +                       ifFalse: [ 1 ]]!
>>
>> Item was added:
>> + ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
>> + asSortFunction
>> +
>> +       ^self ascending!
>>
>> Item was added:
>> + ----- Method: Symbol>>ascending (in category 'sorting') -----
>> + ascending
>> +       "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in ascending order using the <=> method."
>> +       "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
>> +
>> +       ^SortFunction ascend sendMessage: self!
>>
>> Item was added:
>> + ----- Method: Symbol>>descending (in category 'sorting') -----
>> + descending
>> +       "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in descending order using the <=> method."
>> +       "Example: #('abc'  'de' 'fghi') sorted: #size descending"
>> +
>> +       ^SortFunction descend sendMessage: self!
>>
>> Item was added:
>> + ----- Method: Text>><=> (in category 'sorting') -----
>> + <=> aCharacterArray
>> +       "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>> +
>> +       ^string = aCharacterArray
>> +               ifTrue: [ 0 ]
>> +               ifFalse: [string < aCharacterArray asString
>> +                       ifTrue: [ -1 ]
>> +                       ifFalse: [ 1 ]]!
>>
>>
>

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Casey Ransberger-2
Sorry. Vague recollection of a lot of work going into a clean relicense. What's the license again?

--Casey

> On Sep 22, 2014, at 1:26 AM, Frank Shearar <[hidden email]> wrote:
>
> Yes, it looks very very nice.
>
> What's its licence again?
>
> frank
>
>> On 21 September 2014 22:46, Chris Muller <[hidden email]> wrote:
>> What a *gorgeous* solution to multi-level sorting!  I love this!!
>>
>> Now I need to update some of my images to latest trunk just so I can get this..
>>
>>> On Fri, Sep 19, 2014 at 3:24 PM,  <[hidden email]> wrote:
>>> Nicolas Cellier uploaded a new version of Collections to project The Trunk:
>>> http://source.squeak.org/trunk/Collections-nice.582.mcz
>>>
>>> ==================== Summary ====================
>>>
>>> Name: Collections-nice.582
>>> Author: nice
>>> Time: 19 September 2014, 10:22:13.338 pm
>>> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
>>> Ancestors: Collections-nice.581
>>>
>>> Port TAG-SortFunctions of Travis Griggs from Cincom public store - version (11,tgriggs)
>>>
>>> Note that no collation policy were used for String.
>>> The spaceship operator <=> is also required in Kernel-Numbers.
>>>
>>> See also the blog http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>>> and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>>>
>>> Note about the cost of these sort functions:
>>> as shown by this mini-bench on cog, using a Symbol costs a bit more (perform:) than a block activation, and monadic block a bit more than dyadic one because activated twice more, but it seems acceptable to me with regard to the great simplification and expressiveness of code :
>>>
>>> | collec1 collec2 collec3 |
>>> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
>>> collec2 := collec1 copy.
>>> collec3 := collec1 copy.
>>> {
>>> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
>>> [collec2 sort: [:e | e abs] ascending] timeToRun.
>>> [collec3 sort: #abs ascending] timeToRun.
>>> }
>>> #(345 532 912)
>>>
>>> =============== Diff against Collections-nice.581 ===============
>>>
>>> Item was added:
>>> + SortFunction subclass: #ChainedSortFunction
>>> +       instanceVariableNames: 'next'
>>> +       classVariableNames: ''
>>> +       poolDictionaries: ''
>>> +       category: 'Collections-Support'!
>>> +
>>> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior: 0!
>>> + I add to my parent the idea of a "next" function to use when two objects are equal by my particular collator.
>>> +
>>> + Usage
>>> +
>>> + SortFunctions can be chained together in primary, secondary, tertiary, etc order using the comma method. Consider a sequence of customer objects, where each customer object responds to the messages firstName, lastName, and age. If we want to sort them lastName first, then firstName, and finally oldest first, we would use an expression like:
>>> +
>>> + customers sort: #lastName ascending, #firstName ascending, #age descending
>>> +
>>> + As noted in my super's comment, unary symbols or single arg blocks can be used. One can omit the the ascending methods on arguments (not the receiver), it will default blocks or symbols to be ascending if none is specified. In other words, the above expression could be simplified slightly as
>>> +
>>> + customers sort: #lastName ascending, #firstName, #age descending
>>> +
>>> + (note the missing ascending on the #firstName argument)
>>> +
>>> + Instance Variables
>>> +       next    <SortFunction>  the next SortFunction to evaluate in the event my evaluator results in equal values.
>>> +
>>> + !
>>>
>>> Item was added:
>>> + ----- Method: ChainedSortFunction>>, (in category 'converting') -----
>>> + , aSortFunction
>>> +
>>> +       self next: next , aSortFunction!
>>>
>>> Item was added:
>>> + ----- Method: ChainedSortFunction>>next: (in category 'initialize-release') -----
>>> + next: anObject
>>> +
>>> +       next := anObject!
>>>
>>> Item was added:
>>> + ----- Method: ChainedSortFunction>>value:value: (in category 'evaluating') -----
>>> + value: anObject value: bObject
>>> +       "Refinement of the parent behavior. If the result of my collator is 0, then pass on to the next variable to work it out."
>>> +
>>> +       | result |
>>> +       result := (collator value: anObject value: bObject) * direction.
>>> +       ^result isZero
>>> +               ifTrue: [next value: anObject value: bObject]
>>> +               ifFalse: [result < 0]!
>>>
>>> Item was added:
>>> + Object subclass: #SortFunction
>>> +       instanceVariableNames: 'direction collator'
>>> +       classVariableNames: ''
>>> +       poolDictionaries: ''
>>> +       category: 'Collections-Support'!
>>> +
>>> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
>>> + I am intended to be used in place of two arg sort blocks.
>>> +
>>> + Usage
>>> +
>>> + In the following example, an ascending SortFunction is created based on the result of the #first message send to each object.
>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
>>> +
>>> + To sort by the #last element, but descending, the following would be used:
>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
>>> +
>>> + One can use blocks as well. The following sorts in descending order, the sub elements based on the sum of their values.
>>> + | sumBlock |
>>> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each | sum + each]].
>>> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
>>> +
>>> + One can even use 2 arg blocks, for those cases where the function isn't expressible with objects that respond to < and =. The only catch, is that such a function has to return not true and false, but instead a collation order, values of -1 (for before), 0 (the same) or 1 (to follow). For example:
>>> +
>>> + | oddBlock |
>>> + oddBlock :=
>>> +               [:a :b |
>>> +               a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue: [-1] ifFalse: [1]]].
>>> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
>>> +
>>> + Instance Variables
>>> +       collator        <BlockClosure>  This is the collation function that must return a -1, 0, or 1. It is usually composed by an initialization method such as sendMessage: or monadicBlock:, but may be set directly.
>>> +       direction       <SmallInteger>  1 for ascending, -1 for descending
>>> +
>>> + !
>>>
>>> Item was added:
>>> + ----- Method: SortFunction class>>ascend (in category 'instance creation') -----
>>> + ascend
>>> +
>>> +       ^self new ascend!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction class>>descend (in category 'instance creation') -----
>>> + descend
>>> +
>>> +       ^self new descend!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>, (in category 'converting') -----
>>> + , aSortFunction
>>> +       "Return a new SortFunction which is the concatenation of aSortFunction to me, I will be the primary sort, but if I compare equal, I will defer to the argument."
>>> +
>>> +       ^(ChainedSortFunction new)
>>> +               collator: collator;
>>> +               direction: direction;
>>> +               next: aSortFunction asSortFunction!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>asSortFunction (in category 'converting') -----
>>> + asSortFunction
>>> +
>>> +       ^self!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>ascend (in category 'initailize-release') -----
>>> + ascend
>>> +
>>> +       direction := 1!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>collator: (in category 'initailize-release') -----
>>> + collator: a2ArgBlock
>>> +       "a2ArgBlock MUST return the collation order, -1, 0, or 1"
>>> +
>>> +       collator := a2ArgBlock!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>descend (in category 'initailize-release') -----
>>> + descend
>>> +
>>> +       direction := -1!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>direction: (in category 'initailize-release') -----
>>> + direction: anInteger
>>> +
>>> +       direction := anInteger!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>isAscending (in category 'testing') -----
>>> + isAscending
>>> +
>>> +       ^direction = 1!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>isDescending (in category 'testing') -----
>>> + isDescending
>>> +
>>> +       ^direction = -1!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>monadicBlock: (in category 'initailize-release') -----
>>> + monadicBlock: aSingleArgBlock
>>> +       "Initialze the receiver's collation block to compare the results of evaluating aSingleArgBlock with each argument, and then collate the return values with the <=> method."
>>> +
>>> +       collator := [:a :b | (aSingleArgBlock value: a) <=> (aSingleArgBlock value: b)]!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>sendMessage: (in category 'initailize-release') -----
>>> + sendMessage: aUnarySymbol
>>> +       "Initialze the receiver's collation block to compare the results of sending aUnarySymbol to each argument, and then collate them with the <=> method."
>>> +
>>> +       collator := [:a :b | (a perform: aUnarySymbol) <=> (b perform: aUnarySymbol)]!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>toggleDirection (in category 'converting') -----
>>> + toggleDirection
>>> +       "Invert my current direction, if I'm currently ascending, this will cause me to be descending now, and vice-versa."
>>> +
>>> +       direction := direction * -1!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>value:value: (in category 'evaluating') -----
>>> + value: anObject value: bObject
>>> +       "Masquerade as a two argument block, used by many of the sorting APIs, by returning whether anObject should be placed before bObject or not."
>>> +
>>> +       | result |
>>> +       result := (collator value: anObject value: bObject) * direction.
>>> +       ^result <= 0!
>>>
>>> Item was added:
>>> + ----- Method: String>><=> (in category 'sorting') -----
>>> + <=> aCharacterArray
>>> +       "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>> +
>>> +       ^self = aCharacterArray
>>> +               ifTrue: [ 0 ]
>>> +               ifFalse: [self < aCharacterArray
>>> +                       ifTrue: [ -1 ]
>>> +                       ifFalse: [ 1 ]]!
>>>
>>> Item was added:
>>> + ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
>>> + asSortFunction
>>> +
>>> +       ^self ascending!
>>>
>>> Item was added:
>>> + ----- Method: Symbol>>ascending (in category 'sorting') -----
>>> + ascending
>>> +       "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in ascending order using the <=> method."
>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
>>> +
>>> +       ^SortFunction ascend sendMessage: self!
>>>
>>> Item was added:
>>> + ----- Method: Symbol>>descending (in category 'sorting') -----
>>> + descending
>>> +       "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in descending order using the <=> method."
>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size descending"
>>> +
>>> +       ^SortFunction descend sendMessage: self!
>>>
>>> Item was added:
>>> + ----- Method: Text>><=> (in category 'sorting') -----
>>> + <=> aCharacterArray
>>> +       "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>> +
>>> +       ^string = aCharacterArray
>>> +               ifTrue: [ 0 ]
>>> +               ifFalse: [string < aCharacterArray asString
>>> +                       ifTrue: [ -1 ]
>>> +                       ifFalse: [ 1 ]]!
>

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Chris Muller-3
In reply to this post by Frank Shearar-3
It was ported from a "public repository" into trunk.  Everything
posted to trunk is MIT.


On Mon, Sep 22, 2014 at 3:26 AM, Frank Shearar <[hidden email]> wrote:

> Yes, it looks very very nice.
>
> What's its licence again?
>
> frank
>
> On 21 September 2014 22:46, Chris Muller <[hidden email]> wrote:
>> What a *gorgeous* solution to multi-level sorting!  I love this!!
>>
>> Now I need to update some of my images to latest trunk just so I can get this..
>>
>> On Fri, Sep 19, 2014 at 3:24 PM,  <[hidden email]> wrote:
>>> Nicolas Cellier uploaded a new version of Collections to project The Trunk:
>>> http://source.squeak.org/trunk/Collections-nice.582.mcz
>>>
>>> ==================== Summary ====================
>>>
>>> Name: Collections-nice.582
>>> Author: nice
>>> Time: 19 September 2014, 10:22:13.338 pm
>>> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
>>> Ancestors: Collections-nice.581
>>>
>>> Port TAG-SortFunctions of Travis Griggs from Cincom public store - version (11,tgriggs)
>>>
>>> Note that no collation policy were used for String.
>>> The spaceship operator <=> is also required in Kernel-Numbers.
>>>
>>> See also the blog http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>>>  and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>>>
>>> Note about the cost of these sort functions:
>>> as shown by this mini-bench on cog, using a Symbol costs a bit more (perform:) than a block activation, and monadic block a bit more than dyadic one because activated twice more, but it seems acceptable to me with regard to the great simplification and expressiveness of code :
>>>
>>> | collec1 collec2 collec3 |
>>> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
>>> collec2 := collec1 copy.
>>> collec3 := collec1 copy.
>>> {
>>> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
>>> [collec2 sort: [:e | e abs] ascending] timeToRun.
>>> [collec3 sort: #abs ascending] timeToRun.
>>> }
>>>  #(345 532 912)
>>>
>>> =============== Diff against Collections-nice.581 ===============
>>>
>>> Item was added:
>>> + SortFunction subclass: #ChainedSortFunction
>>> +       instanceVariableNames: 'next'
>>> +       classVariableNames: ''
>>> +       poolDictionaries: ''
>>> +       category: 'Collections-Support'!
>>> +
>>> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior: 0!
>>> + I add to my parent the idea of a "next" function to use when two objects are equal by my particular collator.
>>> +
>>> + Usage
>>> +
>>> + SortFunctions can be chained together in primary, secondary, tertiary, etc order using the comma method. Consider a sequence of customer objects, where each customer object responds to the messages firstName, lastName, and age. If we want to sort them lastName first, then firstName, and finally oldest first, we would use an expression like:
>>> +
>>> + customers sort: #lastName ascending, #firstName ascending, #age descending
>>> +
>>> + As noted in my super's comment, unary symbols or single arg blocks can be used. One can omit the the ascending methods on arguments (not the receiver), it will default blocks or symbols to be ascending if none is specified. In other words, the above expression could be simplified slightly as
>>> +
>>> + customers sort: #lastName ascending, #firstName, #age descending
>>> +
>>> + (note the missing ascending on the #firstName argument)
>>> +
>>> + Instance Variables
>>> +       next    <SortFunction>  the next SortFunction to evaluate in the event my evaluator results in equal values.
>>> +
>>> + !
>>>
>>> Item was added:
>>> + ----- Method: ChainedSortFunction>>, (in category 'converting') -----
>>> + , aSortFunction
>>> +
>>> +       self next: next , aSortFunction!
>>>
>>> Item was added:
>>> + ----- Method: ChainedSortFunction>>next: (in category 'initialize-release') -----
>>> + next: anObject
>>> +
>>> +       next := anObject!
>>>
>>> Item was added:
>>> + ----- Method: ChainedSortFunction>>value:value: (in category 'evaluating') -----
>>> + value: anObject value: bObject
>>> +       "Refinement of the parent behavior. If the result of my collator is 0, then pass on to the next variable to work it out."
>>> +
>>> +       | result |
>>> +       result := (collator value: anObject value: bObject) * direction.
>>> +       ^result isZero
>>> +               ifTrue: [next value: anObject value: bObject]
>>> +               ifFalse: [result < 0]!
>>>
>>> Item was added:
>>> + Object subclass: #SortFunction
>>> +       instanceVariableNames: 'direction collator'
>>> +       classVariableNames: ''
>>> +       poolDictionaries: ''
>>> +       category: 'Collections-Support'!
>>> +
>>> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
>>> + I am intended to be used in place of two arg sort blocks.
>>> +
>>> + Usage
>>> +
>>> + In the following example, an ascending SortFunction is created based on the result of the #first message send to each object.
>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
>>> +
>>> + To sort by the #last element, but descending, the following would be used:
>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
>>> +
>>> + One can use blocks as well. The following sorts in descending order, the sub elements based on the sum of their values.
>>> + | sumBlock |
>>> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each | sum + each]].
>>> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
>>> +
>>> + One can even use 2 arg blocks, for those cases where the function isn't expressible with objects that respond to < and =. The only catch, is that such a function has to return not true and false, but instead a collation order, values of -1 (for before), 0 (the same) or 1 (to follow). For example:
>>> +
>>> + | oddBlock |
>>> + oddBlock :=
>>> +               [:a :b |
>>> +               a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue: [-1] ifFalse: [1]]].
>>> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
>>> +
>>> + Instance Variables
>>> +       collator        <BlockClosure>  This is the collation function that must return a -1, 0, or 1. It is usually composed by an initialization method such as sendMessage: or monadicBlock:, but may be set directly.
>>> +       direction       <SmallInteger>  1 for ascending, -1 for descending
>>> +
>>> + !
>>>
>>> Item was added:
>>> + ----- Method: SortFunction class>>ascend (in category 'instance creation') -----
>>> + ascend
>>> +
>>> +       ^self new ascend!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction class>>descend (in category 'instance creation') -----
>>> + descend
>>> +
>>> +       ^self new descend!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>, (in category 'converting') -----
>>> + , aSortFunction
>>> +       "Return a new SortFunction which is the concatenation of aSortFunction to me, I will be the primary sort, but if I compare equal, I will defer to the argument."
>>> +
>>> +       ^(ChainedSortFunction new)
>>> +               collator: collator;
>>> +               direction: direction;
>>> +               next: aSortFunction asSortFunction!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>asSortFunction (in category 'converting') -----
>>> + asSortFunction
>>> +
>>> +       ^self!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>ascend (in category 'initailize-release') -----
>>> + ascend
>>> +
>>> +       direction := 1!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>collator: (in category 'initailize-release') -----
>>> + collator: a2ArgBlock
>>> +       "a2ArgBlock MUST return the collation order, -1, 0, or 1"
>>> +
>>> +       collator := a2ArgBlock!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>descend (in category 'initailize-release') -----
>>> + descend
>>> +
>>> +       direction := -1!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>direction: (in category 'initailize-release') -----
>>> + direction: anInteger
>>> +
>>> +       direction := anInteger!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>isAscending (in category 'testing') -----
>>> + isAscending
>>> +
>>> +       ^direction = 1!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>isDescending (in category 'testing') -----
>>> + isDescending
>>> +
>>> +       ^direction = -1!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>monadicBlock: (in category 'initailize-release') -----
>>> + monadicBlock: aSingleArgBlock
>>> +       "Initialze the receiver's collation block to compare the results of evaluating aSingleArgBlock with each argument, and then collate the return values with the <=> method."
>>> +
>>> +       collator := [:a :b | (aSingleArgBlock value: a) <=> (aSingleArgBlock value: b)]!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>sendMessage: (in category 'initailize-release') -----
>>> + sendMessage: aUnarySymbol
>>> +       "Initialze the receiver's collation block to compare the results of sending aUnarySymbol to each argument, and then collate them with the <=> method."
>>> +
>>> +       collator := [:a :b | (a perform: aUnarySymbol) <=> (b perform: aUnarySymbol)]!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>toggleDirection (in category 'converting') -----
>>> + toggleDirection
>>> +       "Invert my current direction, if I'm currently ascending, this will cause me to be descending now, and vice-versa."
>>> +
>>> +       direction := direction * -1!
>>>
>>> Item was added:
>>> + ----- Method: SortFunction>>value:value: (in category 'evaluating') -----
>>> + value: anObject value: bObject
>>> +       "Masquerade as a two argument block, used by many of the sorting APIs, by returning whether anObject should be placed before bObject or not."
>>> +
>>> +       | result |
>>> +       result := (collator value: anObject value: bObject) * direction.
>>> +       ^result <= 0!
>>>
>>> Item was added:
>>> + ----- Method: String>><=> (in category 'sorting') -----
>>> + <=> aCharacterArray
>>> +       "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>> +
>>> +       ^self = aCharacterArray
>>> +               ifTrue: [ 0 ]
>>> +               ifFalse: [self < aCharacterArray
>>> +                       ifTrue: [ -1 ]
>>> +                       ifFalse: [ 1 ]]!
>>>
>>> Item was added:
>>> + ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
>>> + asSortFunction
>>> +
>>> +       ^self ascending!
>>>
>>> Item was added:
>>> + ----- Method: Symbol>>ascending (in category 'sorting') -----
>>> + ascending
>>> +       "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in ascending order using the <=> method."
>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
>>> +
>>> +       ^SortFunction ascend sendMessage: self!
>>>
>>> Item was added:
>>> + ----- Method: Symbol>>descending (in category 'sorting') -----
>>> + descending
>>> +       "Return a SortFunction around the receiver, where the receiver will be used as a unary message to send to both a and b during sorting, and then the result of said send will be collated in descending order using the <=> method."
>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size descending"
>>> +
>>> +       ^SortFunction descend sendMessage: self!
>>>
>>> Item was added:
>>> + ----- Method: Text>><=> (in category 'sorting') -----
>>> + <=> aCharacterArray
>>> +       "Return a collation order of -1, 0, or 1, indicating whether I should be collated before the receiver, am equal, or after.
>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>> +
>>> +       ^string = aCharacterArray
>>> +               ifTrue: [ 0 ]
>>> +               ifFalse: [string < aCharacterArray asString
>>> +                       ifTrue: [ -1 ]
>>> +                       ifFalse: [ 1 ]]!
>>>
>>>
>>
>

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

David T. Lewis
We need positive confirmation that the code derived from that public
repository was licensed MIT, or a statement from the original author that
is now being released under MIT. I'm sure that the original author will be
happy to do that, but we do need a clear statement of license before we
can allow the code to be added to trunk.

Dave

> It was ported from a "public repository" into trunk.  Everything
> posted to trunk is MIT.
>
>
> On Mon, Sep 22, 2014 at 3:26 AM, Frank Shearar <[hidden email]>
> wrote:
>> Yes, it looks very very nice.
>>
>> What's its licence again?
>>
>> frank
>>
>> On 21 September 2014 22:46, Chris Muller <[hidden email]> wrote:
>>> What a *gorgeous* solution to multi-level sorting!  I love this!!
>>>
>>> Now I need to update some of my images to latest trunk just so I can
>>> get this..
>>>
>>> On Fri, Sep 19, 2014 at 3:24 PM,  <[hidden email]> wrote:
>>>> Nicolas Cellier uploaded a new version of Collections to project The
>>>> Trunk:
>>>> http://source.squeak.org/trunk/Collections-nice.582.mcz
>>>>
>>>> ==================== Summary ====================
>>>>
>>>> Name: Collections-nice.582
>>>> Author: nice
>>>> Time: 19 September 2014, 10:22:13.338 pm
>>>> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
>>>> Ancestors: Collections-nice.581
>>>>
>>>> Port TAG-SortFunctions of Travis Griggs from Cincom public store -
>>>> version (11,tgriggs)
>>>>
>>>> Note that no collation policy were used for String.
>>>> The spaceship operator <=> is also required in Kernel-Numbers.
>>>>
>>>> See also the blog
>>>> http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>>>>  and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>>>>
>>>> Note about the cost of these sort functions:
>>>> as shown by this mini-bench on cog, using a Symbol costs a bit more
>>>> (perform:) than a block activation, and monadic block a bit more than
>>>> dyadic one because activated twice more, but it seems acceptable to me
>>>> with regard to the great simplification and expressiveness of code :
>>>>
>>>> | collec1 collec2 collec3 |
>>>> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
>>>> collec2 := collec1 copy.
>>>> collec3 := collec1 copy.
>>>> {
>>>> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
>>>> [collec2 sort: [:e | e abs] ascending] timeToRun.
>>>> [collec3 sort: #abs ascending] timeToRun.
>>>> }
>>>>  #(345 532 912)
>>>>
>>>> =============== Diff against Collections-nice.581 ===============
>>>>
>>>> Item was added:
>>>> + SortFunction subclass: #ChainedSortFunction
>>>> +       instanceVariableNames: 'next'
>>>> +       classVariableNames: ''
>>>> +       poolDictionaries: ''
>>>> +       category: 'Collections-Support'!
>>>> +
>>>> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior: 0!
>>>> + I add to my parent the idea of a "next" function to use when two
>>>> objects are equal by my particular collator.
>>>> +
>>>> + Usage
>>>> +
>>>> + SortFunctions can be chained together in primary, secondary,
>>>> tertiary, etc order using the comma method. Consider a sequence of
>>>> customer objects, where each customer object responds to the messages
>>>> firstName, lastName, and age. If we want to sort them lastName first,
>>>> then firstName, and finally oldest first, we would use an expression
>>>> like:
>>>> +
>>>> + customers sort: #lastName ascending, #firstName ascending, #age
>>>> descending
>>>> +
>>>> + As noted in my super's comment, unary symbols or single arg blocks
>>>> can be used. One can omit the the ascending methods on arguments (not
>>>> the receiver), it will default blocks or symbols to be ascending if
>>>> none is specified. In other words, the above expression could be
>>>> simplified slightly as
>>>> +
>>>> + customers sort: #lastName ascending, #firstName, #age descending
>>>> +
>>>> + (note the missing ascending on the #firstName argument)
>>>> +
>>>> + Instance Variables
>>>> +       next    <SortFunction>  the next SortFunction to evaluate in
>>>> the event my evaluator results in equal values.
>>>> +
>>>> + !
>>>>
>>>> Item was added:
>>>> + ----- Method: ChainedSortFunction>>, (in category 'converting')
>>>> -----
>>>> + , aSortFunction
>>>> +
>>>> +       self next: next , aSortFunction!
>>>>
>>>> Item was added:
>>>> + ----- Method: ChainedSortFunction>>next: (in category
>>>> 'initialize-release') -----
>>>> + next: anObject
>>>> +
>>>> +       next := anObject!
>>>>
>>>> Item was added:
>>>> + ----- Method: ChainedSortFunction>>value:value: (in category
>>>> 'evaluating') -----
>>>> + value: anObject value: bObject
>>>> +       "Refinement of the parent behavior. If the result of my
>>>> collator is 0, then pass on to the next variable to work it out."
>>>> +
>>>> +       | result |
>>>> +       result := (collator value: anObject value: bObject) *
>>>> direction.
>>>> +       ^result isZero
>>>> +               ifTrue: [next value: anObject value: bObject]
>>>> +               ifFalse: [result < 0]!
>>>>
>>>> Item was added:
>>>> + Object subclass: #SortFunction
>>>> +       instanceVariableNames: 'direction collator'
>>>> +       classVariableNames: ''
>>>> +       poolDictionaries: ''
>>>> +       category: 'Collections-Support'!
>>>> +
>>>> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
>>>> + I am intended to be used in place of two arg sort blocks.
>>>> +
>>>> + Usage
>>>> +
>>>> + In the following example, an ascending SortFunction is created based
>>>> on the result of the #first message send to each object.
>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
>>>> +
>>>> + To sort by the #last element, but descending, the following would be
>>>> used:
>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
>>>> +
>>>> + One can use blocks as well. The following sorts in descending order,
>>>> the sub elements based on the sum of their values.
>>>> + | sumBlock |
>>>> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each | sum
>>>> + each]].
>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
>>>> +
>>>> + One can even use 2 arg blocks, for those cases where the function
>>>> isn't expressible with objects that respond to < and =. The only
>>>> catch, is that such a function has to return not true and false, but
>>>> instead a collation order, values of -1 (for before), 0 (the same) or
>>>> 1 (to follow). For example:
>>>> +
>>>> + | oddBlock |
>>>> + oddBlock :=
>>>> +               [:a :b |
>>>> +               a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue: [-1]
>>>> ifFalse: [1]]].
>>>> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
>>>> +
>>>> + Instance Variables
>>>> +       collator        <BlockClosure>  This is the collation function
>>>> that must return a -1, 0, or 1. It is usually composed by an
>>>> initialization method such as sendMessage: or monadicBlock:, but may
>>>> be set directly.
>>>> +       direction       <SmallInteger>  1 for ascending, -1 for
>>>> descending
>>>> +
>>>> + !
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction class>>ascend (in category 'instance
>>>> creation') -----
>>>> + ascend
>>>> +
>>>> +       ^self new ascend!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction class>>descend (in category 'instance
>>>> creation') -----
>>>> + descend
>>>> +
>>>> +       ^self new descend!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>, (in category 'converting') -----
>>>> + , aSortFunction
>>>> +       "Return a new SortFunction which is the concatenation of
>>>> aSortFunction to me, I will be the primary sort, but if I compare
>>>> equal, I will defer to the argument."
>>>> +
>>>> +       ^(ChainedSortFunction new)
>>>> +               collator: collator;
>>>> +               direction: direction;
>>>> +               next: aSortFunction asSortFunction!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>asSortFunction (in category
>>>> 'converting') -----
>>>> + asSortFunction
>>>> +
>>>> +       ^self!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>ascend (in category
>>>> 'initailize-release') -----
>>>> + ascend
>>>> +
>>>> +       direction := 1!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>collator: (in category
>>>> 'initailize-release') -----
>>>> + collator: a2ArgBlock
>>>> +       "a2ArgBlock MUST return the collation order, -1, 0, or 1"
>>>> +
>>>> +       collator := a2ArgBlock!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>descend (in category
>>>> 'initailize-release') -----
>>>> + descend
>>>> +
>>>> +       direction := -1!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>direction: (in category
>>>> 'initailize-release') -----
>>>> + direction: anInteger
>>>> +
>>>> +       direction := anInteger!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>isAscending (in category 'testing')
>>>> -----
>>>> + isAscending
>>>> +
>>>> +       ^direction = 1!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>isDescending (in category 'testing')
>>>> -----
>>>> + isDescending
>>>> +
>>>> +       ^direction = -1!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>monadicBlock: (in category
>>>> 'initailize-release') -----
>>>> + monadicBlock: aSingleArgBlock
>>>> +       "Initialze the receiver's collation block to compare the
>>>> results of evaluating aSingleArgBlock with each argument, and then
>>>> collate the return values with the <=> method."
>>>> +
>>>> +       collator := [:a :b | (aSingleArgBlock value: a) <=>
>>>> (aSingleArgBlock value: b)]!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>sendMessage: (in category
>>>> 'initailize-release') -----
>>>> + sendMessage: aUnarySymbol
>>>> +       "Initialze the receiver's collation block to compare the
>>>> results of sending aUnarySymbol to each argument, and then collate
>>>> them with the <=> method."
>>>> +
>>>> +       collator := [:a :b | (a perform: aUnarySymbol) <=> (b perform:
>>>> aUnarySymbol)]!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>toggleDirection (in category
>>>> 'converting') -----
>>>> + toggleDirection
>>>> +       "Invert my current direction, if I'm currently ascending, this
>>>> will cause me to be descending now, and vice-versa."
>>>> +
>>>> +       direction := direction * -1!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>value:value: (in category 'evaluating')
>>>> -----
>>>> + value: anObject value: bObject
>>>> +       "Masquerade as a two argument block, used by many of the
>>>> sorting APIs, by returning whether anObject should be placed before
>>>> bObject or not."
>>>> +
>>>> +       | result |
>>>> +       result := (collator value: anObject value: bObject) *
>>>> direction.
>>>> +       ^result <= 0!
>>>>
>>>> Item was added:
>>>> + ----- Method: String>><=> (in category 'sorting') -----
>>>> + <=> aCharacterArray
>>>> +       "Return a collation order of -1, 0, or 1, indicating whether I
>>>> should be collated before the receiver, am equal, or after.
>>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>>> +
>>>> +       ^self = aCharacterArray
>>>> +               ifTrue: [ 0 ]
>>>> +               ifFalse: [self < aCharacterArray
>>>> +                       ifTrue: [ -1 ]
>>>> +                       ifFalse: [ 1 ]]!
>>>>
>>>> Item was added:
>>>> + ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
>>>> + asSortFunction
>>>> +
>>>> +       ^self ascending!
>>>>
>>>> Item was added:
>>>> + ----- Method: Symbol>>ascending (in category 'sorting') -----
>>>> + ascending
>>>> +       "Return a SortFunction around the receiver, where the receiver
>>>> will be used as a unary message to send to both a and b during
>>>> sorting, and then the result of said send will be collated in
>>>> ascending order using the <=> method."
>>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
>>>> +
>>>> +       ^SortFunction ascend sendMessage: self!
>>>>
>>>> Item was added:
>>>> + ----- Method: Symbol>>descending (in category 'sorting') -----
>>>> + descending
>>>> +       "Return a SortFunction around the receiver, where the receiver
>>>> will be used as a unary message to send to both a and b during
>>>> sorting, and then the result of said send will be collated in
>>>> descending order using the <=> method."
>>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size descending"
>>>> +
>>>> +       ^SortFunction descend sendMessage: self!
>>>>
>>>> Item was added:
>>>> + ----- Method: Text>><=> (in category 'sorting') -----
>>>> + <=> aCharacterArray
>>>> +       "Return a collation order of -1, 0, or 1, indicating whether I
>>>> should be collated before the receiver, am equal, or after.
>>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>>> +
>>>> +       ^string = aCharacterArray
>>>> +               ifTrue: [ 0 ]
>>>> +               ifFalse: [string < aCharacterArray asString
>>>> +                       ifTrue: [ -1 ]
>>>> +                       ifFalse: [ 1 ]]!
>>>>
>>>>
>>>
>>
>



Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Chris Muller-4
I'm the last to ever pirate something, but I disagree.  The original
author already invited it to be ported, even specifically mentioning
"Squeak".

>From the last paragraph of:

  http://objology.blogspot.com/2010/11/tag-sortfunctions.html

--------------------
"If this is useful to others, I'll make it more usable, by pulling the
tests out of the package so it doesn't need SUnitToo. And you can
lobby Cincom to include it in VisualWorks. Or port it to your own
flavor of Smalltalk. I did my best to write it in such a way that it
would work in Squeak or Gemstone, or whatever."
---------------------

So, I would say, UNLESS the original author comes back and says, "Hey!
 You can't do that!" then it's okay to leave in trunk.

Funny, this is the first time in a long time anyone ever asked about
license, but on something that specifically was targeted for Squeak
and other Smalltalks.  If you want to audit Squeak, maybe you should
scrutinize some of the other contributions that _weren't_ already
specifically targeted for Squeak and multiple Smalltalk platforms..?

On Mon, Sep 22, 2014 at 10:51 AM, David T. Lewis <[hidden email]> wrote:

> We need positive confirmation that the code derived from that public
> repository was licensed MIT, or a statement from the original author that
> is now being released under MIT. I'm sure that the original author will be
> happy to do that, but we do need a clear statement of license before we
> can allow the code to be added to trunk.
>
> Dave
>
>> It was ported from a "public repository" into trunk.  Everything
>> posted to trunk is MIT.
>>
>>
>> On Mon, Sep 22, 2014 at 3:26 AM, Frank Shearar <[hidden email]>
>> wrote:
>>> Yes, it looks very very nice.
>>>
>>> What's its licence again?
>>>
>>> frank
>>>
>>> On 21 September 2014 22:46, Chris Muller <[hidden email]> wrote:
>>>> What a *gorgeous* solution to multi-level sorting!  I love this!!
>>>>
>>>> Now I need to update some of my images to latest trunk just so I can
>>>> get this..
>>>>
>>>> On Fri, Sep 19, 2014 at 3:24 PM,  <[hidden email]> wrote:
>>>>> Nicolas Cellier uploaded a new version of Collections to project The
>>>>> Trunk:
>>>>> http://source.squeak.org/trunk/Collections-nice.582.mcz
>>>>>
>>>>> ==================== Summary ====================
>>>>>
>>>>> Name: Collections-nice.582
>>>>> Author: nice
>>>>> Time: 19 September 2014, 10:22:13.338 pm
>>>>> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
>>>>> Ancestors: Collections-nice.581
>>>>>
>>>>> Port TAG-SortFunctions of Travis Griggs from Cincom public store -
>>>>> version (11,tgriggs)
>>>>>
>>>>> Note that no collation policy were used for String.
>>>>> The spaceship operator <=> is also required in Kernel-Numbers.
>>>>>
>>>>> See also the blog
>>>>> http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>>>>>  and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>>>>>
>>>>> Note about the cost of these sort functions:
>>>>> as shown by this mini-bench on cog, using a Symbol costs a bit more
>>>>> (perform:) than a block activation, and monadic block a bit more than
>>>>> dyadic one because activated twice more, but it seems acceptable to me
>>>>> with regard to the great simplification and expressiveness of code :
>>>>>
>>>>> | collec1 collec2 collec3 |
>>>>> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
>>>>> collec2 := collec1 copy.
>>>>> collec3 := collec1 copy.
>>>>> {
>>>>> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
>>>>> [collec2 sort: [:e | e abs] ascending] timeToRun.
>>>>> [collec3 sort: #abs ascending] timeToRun.
>>>>> }
>>>>>  #(345 532 912)
>>>>>
>>>>> =============== Diff against Collections-nice.581 ===============
>>>>>
>>>>> Item was added:
>>>>> + SortFunction subclass: #ChainedSortFunction
>>>>> +       instanceVariableNames: 'next'
>>>>> +       classVariableNames: ''
>>>>> +       poolDictionaries: ''
>>>>> +       category: 'Collections-Support'!
>>>>> +
>>>>> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior: 0!
>>>>> + I add to my parent the idea of a "next" function to use when two
>>>>> objects are equal by my particular collator.
>>>>> +
>>>>> + Usage
>>>>> +
>>>>> + SortFunctions can be chained together in primary, secondary,
>>>>> tertiary, etc order using the comma method. Consider a sequence of
>>>>> customer objects, where each customer object responds to the messages
>>>>> firstName, lastName, and age. If we want to sort them lastName first,
>>>>> then firstName, and finally oldest first, we would use an expression
>>>>> like:
>>>>> +
>>>>> + customers sort: #lastName ascending, #firstName ascending, #age
>>>>> descending
>>>>> +
>>>>> + As noted in my super's comment, unary symbols or single arg blocks
>>>>> can be used. One can omit the the ascending methods on arguments (not
>>>>> the receiver), it will default blocks or symbols to be ascending if
>>>>> none is specified. In other words, the above expression could be
>>>>> simplified slightly as
>>>>> +
>>>>> + customers sort: #lastName ascending, #firstName, #age descending
>>>>> +
>>>>> + (note the missing ascending on the #firstName argument)
>>>>> +
>>>>> + Instance Variables
>>>>> +       next    <SortFunction>  the next SortFunction to evaluate in
>>>>> the event my evaluator results in equal values.
>>>>> +
>>>>> + !
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: ChainedSortFunction>>, (in category 'converting')
>>>>> -----
>>>>> + , aSortFunction
>>>>> +
>>>>> +       self next: next , aSortFunction!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: ChainedSortFunction>>next: (in category
>>>>> 'initialize-release') -----
>>>>> + next: anObject
>>>>> +
>>>>> +       next := anObject!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: ChainedSortFunction>>value:value: (in category
>>>>> 'evaluating') -----
>>>>> + value: anObject value: bObject
>>>>> +       "Refinement of the parent behavior. If the result of my
>>>>> collator is 0, then pass on to the next variable to work it out."
>>>>> +
>>>>> +       | result |
>>>>> +       result := (collator value: anObject value: bObject) *
>>>>> direction.
>>>>> +       ^result isZero
>>>>> +               ifTrue: [next value: anObject value: bObject]
>>>>> +               ifFalse: [result < 0]!
>>>>>
>>>>> Item was added:
>>>>> + Object subclass: #SortFunction
>>>>> +       instanceVariableNames: 'direction collator'
>>>>> +       classVariableNames: ''
>>>>> +       poolDictionaries: ''
>>>>> +       category: 'Collections-Support'!
>>>>> +
>>>>> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
>>>>> + I am intended to be used in place of two arg sort blocks.
>>>>> +
>>>>> + Usage
>>>>> +
>>>>> + In the following example, an ascending SortFunction is created based
>>>>> on the result of the #first message send to each object.
>>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
>>>>> +
>>>>> + To sort by the #last element, but descending, the following would be
>>>>> used:
>>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
>>>>> +
>>>>> + One can use blocks as well. The following sorts in descending order,
>>>>> the sub elements based on the sum of their values.
>>>>> + | sumBlock |
>>>>> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each | sum
>>>>> + each]].
>>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
>>>>> +
>>>>> + One can even use 2 arg blocks, for those cases where the function
>>>>> isn't expressible with objects that respond to < and =. The only
>>>>> catch, is that such a function has to return not true and false, but
>>>>> instead a collation order, values of -1 (for before), 0 (the same) or
>>>>> 1 (to follow). For example:
>>>>> +
>>>>> + | oddBlock |
>>>>> + oddBlock :=
>>>>> +               [:a :b |
>>>>> +               a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue: [-1]
>>>>> ifFalse: [1]]].
>>>>> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
>>>>> +
>>>>> + Instance Variables
>>>>> +       collator        <BlockClosure>  This is the collation function
>>>>> that must return a -1, 0, or 1. It is usually composed by an
>>>>> initialization method such as sendMessage: or monadicBlock:, but may
>>>>> be set directly.
>>>>> +       direction       <SmallInteger>  1 for ascending, -1 for
>>>>> descending
>>>>> +
>>>>> + !
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction class>>ascend (in category 'instance
>>>>> creation') -----
>>>>> + ascend
>>>>> +
>>>>> +       ^self new ascend!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction class>>descend (in category 'instance
>>>>> creation') -----
>>>>> + descend
>>>>> +
>>>>> +       ^self new descend!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>, (in category 'converting') -----
>>>>> + , aSortFunction
>>>>> +       "Return a new SortFunction which is the concatenation of
>>>>> aSortFunction to me, I will be the primary sort, but if I compare
>>>>> equal, I will defer to the argument."
>>>>> +
>>>>> +       ^(ChainedSortFunction new)
>>>>> +               collator: collator;
>>>>> +               direction: direction;
>>>>> +               next: aSortFunction asSortFunction!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>asSortFunction (in category
>>>>> 'converting') -----
>>>>> + asSortFunction
>>>>> +
>>>>> +       ^self!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>ascend (in category
>>>>> 'initailize-release') -----
>>>>> + ascend
>>>>> +
>>>>> +       direction := 1!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>collator: (in category
>>>>> 'initailize-release') -----
>>>>> + collator: a2ArgBlock
>>>>> +       "a2ArgBlock MUST return the collation order, -1, 0, or 1"
>>>>> +
>>>>> +       collator := a2ArgBlock!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>descend (in category
>>>>> 'initailize-release') -----
>>>>> + descend
>>>>> +
>>>>> +       direction := -1!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>direction: (in category
>>>>> 'initailize-release') -----
>>>>> + direction: anInteger
>>>>> +
>>>>> +       direction := anInteger!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>isAscending (in category 'testing')
>>>>> -----
>>>>> + isAscending
>>>>> +
>>>>> +       ^direction = 1!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>isDescending (in category 'testing')
>>>>> -----
>>>>> + isDescending
>>>>> +
>>>>> +       ^direction = -1!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>monadicBlock: (in category
>>>>> 'initailize-release') -----
>>>>> + monadicBlock: aSingleArgBlock
>>>>> +       "Initialze the receiver's collation block to compare the
>>>>> results of evaluating aSingleArgBlock with each argument, and then
>>>>> collate the return values with the <=> method."
>>>>> +
>>>>> +       collator := [:a :b | (aSingleArgBlock value: a) <=>
>>>>> (aSingleArgBlock value: b)]!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>sendMessage: (in category
>>>>> 'initailize-release') -----
>>>>> + sendMessage: aUnarySymbol
>>>>> +       "Initialze the receiver's collation block to compare the
>>>>> results of sending aUnarySymbol to each argument, and then collate
>>>>> them with the <=> method."
>>>>> +
>>>>> +       collator := [:a :b | (a perform: aUnarySymbol) <=> (b perform:
>>>>> aUnarySymbol)]!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>toggleDirection (in category
>>>>> 'converting') -----
>>>>> + toggleDirection
>>>>> +       "Invert my current direction, if I'm currently ascending, this
>>>>> will cause me to be descending now, and vice-versa."
>>>>> +
>>>>> +       direction := direction * -1!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: SortFunction>>value:value: (in category 'evaluating')
>>>>> -----
>>>>> + value: anObject value: bObject
>>>>> +       "Masquerade as a two argument block, used by many of the
>>>>> sorting APIs, by returning whether anObject should be placed before
>>>>> bObject or not."
>>>>> +
>>>>> +       | result |
>>>>> +       result := (collator value: anObject value: bObject) *
>>>>> direction.
>>>>> +       ^result <= 0!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: String>><=> (in category 'sorting') -----
>>>>> + <=> aCharacterArray
>>>>> +       "Return a collation order of -1, 0, or 1, indicating whether I
>>>>> should be collated before the receiver, am equal, or after.
>>>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>>>> +
>>>>> +       ^self = aCharacterArray
>>>>> +               ifTrue: [ 0 ]
>>>>> +               ifFalse: [self < aCharacterArray
>>>>> +                       ifTrue: [ -1 ]
>>>>> +                       ifFalse: [ 1 ]]!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
>>>>> + asSortFunction
>>>>> +
>>>>> +       ^self ascending!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: Symbol>>ascending (in category 'sorting') -----
>>>>> + ascending
>>>>> +       "Return a SortFunction around the receiver, where the receiver
>>>>> will be used as a unary message to send to both a and b during
>>>>> sorting, and then the result of said send will be collated in
>>>>> ascending order using the <=> method."
>>>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
>>>>> +
>>>>> +       ^SortFunction ascend sendMessage: self!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: Symbol>>descending (in category 'sorting') -----
>>>>> + descending
>>>>> +       "Return a SortFunction around the receiver, where the receiver
>>>>> will be used as a unary message to send to both a and b during
>>>>> sorting, and then the result of said send will be collated in
>>>>> descending order using the <=> method."
>>>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size descending"
>>>>> +
>>>>> +       ^SortFunction descend sendMessage: self!
>>>>>
>>>>> Item was added:
>>>>> + ----- Method: Text>><=> (in category 'sorting') -----
>>>>> + <=> aCharacterArray
>>>>> +       "Return a collation order of -1, 0, or 1, indicating whether I
>>>>> should be collated before the receiver, am equal, or after.
>>>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>>>> +
>>>>> +       ^string = aCharacterArray
>>>>> +               ifTrue: [ 0 ]
>>>>> +               ifFalse: [string < aCharacterArray asString
>>>>> +                       ifTrue: [ -1 ]
>>>>> +                       ifFalse: [ 1 ]]!
>>>>>
>>>>>
>>>>
>>>
>>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

David T. Lewis
Frank asked what the license is. Sorry, but that is an important question
and it needs to be answered.

Perhaps someone with access to the Cincom public repository can check to
see what (if any) license was specified, otherwise I'm sure that a quick
note to Travis would do the trick.

Thanks,
Dave

> I'm the last to ever pirate something, but I disagree.  The original
> author already invited it to be ported, even specifically mentioning
> "Squeak".
>
>>From the last paragraph of:
>
>   http://objology.blogspot.com/2010/11/tag-sortfunctions.html
>
> --------------------
> "If this is useful to others, I'll make it more usable, by pulling the
> tests out of the package so it doesn't need SUnitToo. And you can
> lobby Cincom to include it in VisualWorks. Or port it to your own
> flavor of Smalltalk. I did my best to write it in such a way that it
> would work in Squeak or Gemstone, or whatever."
> ---------------------
>
> So, I would say, UNLESS the original author comes back and says, "Hey!
>  You can't do that!" then it's okay to leave in trunk.
>
> Funny, this is the first time in a long time anyone ever asked about
> license, but on something that specifically was targeted for Squeak
> and other Smalltalks.  If you want to audit Squeak, maybe you should
> scrutinize some of the other contributions that _weren't_ already
> specifically targeted for Squeak and multiple Smalltalk platforms..?
>
> On Mon, Sep 22, 2014 at 10:51 AM, David T. Lewis <[hidden email]>
> wrote:
>> We need positive confirmation that the code derived from that public
>> repository was licensed MIT, or a statement from the original author
>> that
>> is now being released under MIT. I'm sure that the original author will
>> be
>> happy to do that, but we do need a clear statement of license before we
>> can allow the code to be added to trunk.
>>
>> Dave
>>
>>> It was ported from a "public repository" into trunk.  Everything
>>> posted to trunk is MIT.
>>>
>>>
>>> On Mon, Sep 22, 2014 at 3:26 AM, Frank Shearar
>>> <[hidden email]>
>>> wrote:
>>>> Yes, it looks very very nice.
>>>>
>>>> What's its licence again?
>>>>
>>>> frank
>>>>
>>>> On 21 September 2014 22:46, Chris Muller <[hidden email]> wrote:
>>>>> What a *gorgeous* solution to multi-level sorting!  I love this!!
>>>>>
>>>>> Now I need to update some of my images to latest trunk just so I can
>>>>> get this..
>>>>>
>>>>> On Fri, Sep 19, 2014 at 3:24 PM,  <[hidden email]> wrote:
>>>>>> Nicolas Cellier uploaded a new version of Collections to project The
>>>>>> Trunk:
>>>>>> http://source.squeak.org/trunk/Collections-nice.582.mcz
>>>>>>
>>>>>> ==================== Summary ====================
>>>>>>
>>>>>> Name: Collections-nice.582
>>>>>> Author: nice
>>>>>> Time: 19 September 2014, 10:22:13.338 pm
>>>>>> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
>>>>>> Ancestors: Collections-nice.581
>>>>>>
>>>>>> Port TAG-SortFunctions of Travis Griggs from Cincom public store -
>>>>>> version (11,tgriggs)
>>>>>>
>>>>>> Note that no collation policy were used for String.
>>>>>> The spaceship operator <=> is also required in Kernel-Numbers.
>>>>>>
>>>>>> See also the blog
>>>>>> http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>>>>>>  and
>>>>>> http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>>>>>>
>>>>>> Note about the cost of these sort functions:
>>>>>> as shown by this mini-bench on cog, using a Symbol costs a bit more
>>>>>> (perform:) than a block activation, and monadic block a bit more
>>>>>> than
>>>>>> dyadic one because activated twice more, but it seems acceptable to
>>>>>> me
>>>>>> with regard to the great simplification and expressiveness of code :
>>>>>>
>>>>>> | collec1 collec2 collec3 |
>>>>>> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
>>>>>> collec2 := collec1 copy.
>>>>>> collec3 := collec1 copy.
>>>>>> {
>>>>>> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
>>>>>> [collec2 sort: [:e | e abs] ascending] timeToRun.
>>>>>> [collec3 sort: #abs ascending] timeToRun.
>>>>>> }
>>>>>>  #(345 532 912)
>>>>>>
>>>>>> =============== Diff against Collections-nice.581 ===============
>>>>>>
>>>>>> Item was added:
>>>>>> + SortFunction subclass: #ChainedSortFunction
>>>>>> +       instanceVariableNames: 'next'
>>>>>> +       classVariableNames: ''
>>>>>> +       poolDictionaries: ''
>>>>>> +       category: 'Collections-Support'!
>>>>>> +
>>>>>> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior:
>>>>>> 0!
>>>>>> + I add to my parent the idea of a "next" function to use when two
>>>>>> objects are equal by my particular collator.
>>>>>> +
>>>>>> + Usage
>>>>>> +
>>>>>> + SortFunctions can be chained together in primary, secondary,
>>>>>> tertiary, etc order using the comma method. Consider a sequence of
>>>>>> customer objects, where each customer object responds to the
>>>>>> messages
>>>>>> firstName, lastName, and age. If we want to sort them lastName
>>>>>> first,
>>>>>> then firstName, and finally oldest first, we would use an expression
>>>>>> like:
>>>>>> +
>>>>>> + customers sort: #lastName ascending, #firstName ascending, #age
>>>>>> descending
>>>>>> +
>>>>>> + As noted in my super's comment, unary symbols or single arg blocks
>>>>>> can be used. One can omit the the ascending methods on arguments
>>>>>> (not
>>>>>> the receiver), it will default blocks or symbols to be ascending if
>>>>>> none is specified. In other words, the above expression could be
>>>>>> simplified slightly as
>>>>>> +
>>>>>> + customers sort: #lastName ascending, #firstName, #age descending
>>>>>> +
>>>>>> + (note the missing ascending on the #firstName argument)
>>>>>> +
>>>>>> + Instance Variables
>>>>>> +       next    <SortFunction>  the next SortFunction to evaluate in
>>>>>> the event my evaluator results in equal values.
>>>>>> +
>>>>>> + !
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: ChainedSortFunction>>, (in category 'converting')
>>>>>> -----
>>>>>> + , aSortFunction
>>>>>> +
>>>>>> +       self next: next , aSortFunction!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: ChainedSortFunction>>next: (in category
>>>>>> 'initialize-release') -----
>>>>>> + next: anObject
>>>>>> +
>>>>>> +       next := anObject!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: ChainedSortFunction>>value:value: (in category
>>>>>> 'evaluating') -----
>>>>>> + value: anObject value: bObject
>>>>>> +       "Refinement of the parent behavior. If the result of my
>>>>>> collator is 0, then pass on to the next variable to work it out."
>>>>>> +
>>>>>> +       | result |
>>>>>> +       result := (collator value: anObject value: bObject) *
>>>>>> direction.
>>>>>> +       ^result isZero
>>>>>> +               ifTrue: [next value: anObject value: bObject]
>>>>>> +               ifFalse: [result < 0]!
>>>>>>
>>>>>> Item was added:
>>>>>> + Object subclass: #SortFunction
>>>>>> +       instanceVariableNames: 'direction collator'
>>>>>> +       classVariableNames: ''
>>>>>> +       poolDictionaries: ''
>>>>>> +       category: 'Collections-Support'!
>>>>>> +
>>>>>> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
>>>>>> + I am intended to be used in place of two arg sort blocks.
>>>>>> +
>>>>>> + Usage
>>>>>> +
>>>>>> + In the following example, an ascending SortFunction is created
>>>>>> based
>>>>>> on the result of the #first message send to each object.
>>>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
>>>>>> +
>>>>>> + To sort by the #last element, but descending, the following would
>>>>>> be
>>>>>> used:
>>>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
>>>>>> +
>>>>>> + One can use blocks as well. The following sorts in descending
>>>>>> order,
>>>>>> the sub elements based on the sum of their values.
>>>>>> + | sumBlock |
>>>>>> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each |
>>>>>> sum
>>>>>> + each]].
>>>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
>>>>>> +
>>>>>> + One can even use 2 arg blocks, for those cases where the function
>>>>>> isn't expressible with objects that respond to < and =. The only
>>>>>> catch, is that such a function has to return not true and false, but
>>>>>> instead a collation order, values of -1 (for before), 0 (the same)
>>>>>> or
>>>>>> 1 (to follow). For example:
>>>>>> +
>>>>>> + | oddBlock |
>>>>>> + oddBlock :=
>>>>>> +               [:a :b |
>>>>>> +               a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue:
>>>>>> [-1]
>>>>>> ifFalse: [1]]].
>>>>>> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
>>>>>> +
>>>>>> + Instance Variables
>>>>>> +       collator        <BlockClosure>  This is the collation
>>>>>> function
>>>>>> that must return a -1, 0, or 1. It is usually composed by an
>>>>>> initialization method such as sendMessage: or monadicBlock:, but may
>>>>>> be set directly.
>>>>>> +       direction       <SmallInteger>  1 for ascending, -1 for
>>>>>> descending
>>>>>> +
>>>>>> + !
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction class>>ascend (in category 'instance
>>>>>> creation') -----
>>>>>> + ascend
>>>>>> +
>>>>>> +       ^self new ascend!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction class>>descend (in category 'instance
>>>>>> creation') -----
>>>>>> + descend
>>>>>> +
>>>>>> +       ^self new descend!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>, (in category 'converting') -----
>>>>>> + , aSortFunction
>>>>>> +       "Return a new SortFunction which is the concatenation of
>>>>>> aSortFunction to me, I will be the primary sort, but if I compare
>>>>>> equal, I will defer to the argument."
>>>>>> +
>>>>>> +       ^(ChainedSortFunction new)
>>>>>> +               collator: collator;
>>>>>> +               direction: direction;
>>>>>> +               next: aSortFunction asSortFunction!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>asSortFunction (in category
>>>>>> 'converting') -----
>>>>>> + asSortFunction
>>>>>> +
>>>>>> +       ^self!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>ascend (in category
>>>>>> 'initailize-release') -----
>>>>>> + ascend
>>>>>> +
>>>>>> +       direction := 1!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>collator: (in category
>>>>>> 'initailize-release') -----
>>>>>> + collator: a2ArgBlock
>>>>>> +       "a2ArgBlock MUST return the collation order, -1, 0, or 1"
>>>>>> +
>>>>>> +       collator := a2ArgBlock!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>descend (in category
>>>>>> 'initailize-release') -----
>>>>>> + descend
>>>>>> +
>>>>>> +       direction := -1!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>direction: (in category
>>>>>> 'initailize-release') -----
>>>>>> + direction: anInteger
>>>>>> +
>>>>>> +       direction := anInteger!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>isAscending (in category 'testing')
>>>>>> -----
>>>>>> + isAscending
>>>>>> +
>>>>>> +       ^direction = 1!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>isDescending (in category 'testing')
>>>>>> -----
>>>>>> + isDescending
>>>>>> +
>>>>>> +       ^direction = -1!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>monadicBlock: (in category
>>>>>> 'initailize-release') -----
>>>>>> + monadicBlock: aSingleArgBlock
>>>>>> +       "Initialze the receiver's collation block to compare the
>>>>>> results of evaluating aSingleArgBlock with each argument, and then
>>>>>> collate the return values with the <=> method."
>>>>>> +
>>>>>> +       collator := [:a :b | (aSingleArgBlock value: a) <=>
>>>>>> (aSingleArgBlock value: b)]!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>sendMessage: (in category
>>>>>> 'initailize-release') -----
>>>>>> + sendMessage: aUnarySymbol
>>>>>> +       "Initialze the receiver's collation block to compare the
>>>>>> results of sending aUnarySymbol to each argument, and then collate
>>>>>> them with the <=> method."
>>>>>> +
>>>>>> +       collator := [:a :b | (a perform: aUnarySymbol) <=> (b
>>>>>> perform:
>>>>>> aUnarySymbol)]!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>toggleDirection (in category
>>>>>> 'converting') -----
>>>>>> + toggleDirection
>>>>>> +       "Invert my current direction, if I'm currently ascending,
>>>>>> this
>>>>>> will cause me to be descending now, and vice-versa."
>>>>>> +
>>>>>> +       direction := direction * -1!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: SortFunction>>value:value: (in category
>>>>>> 'evaluating')
>>>>>> -----
>>>>>> + value: anObject value: bObject
>>>>>> +       "Masquerade as a two argument block, used by many of the
>>>>>> sorting APIs, by returning whether anObject should be placed before
>>>>>> bObject or not."
>>>>>> +
>>>>>> +       | result |
>>>>>> +       result := (collator value: anObject value: bObject) *
>>>>>> direction.
>>>>>> +       ^result <= 0!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: String>><=> (in category 'sorting') -----
>>>>>> + <=> aCharacterArray
>>>>>> +       "Return a collation order of -1, 0, or 1, indicating whether
>>>>>> I
>>>>>> should be collated before the receiver, am equal, or after.
>>>>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>>>>> +
>>>>>> +       ^self = aCharacterArray
>>>>>> +               ifTrue: [ 0 ]
>>>>>> +               ifFalse: [self < aCharacterArray
>>>>>> +                       ifTrue: [ -1 ]
>>>>>> +                       ifFalse: [ 1 ]]!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
>>>>>> + asSortFunction
>>>>>> +
>>>>>> +       ^self ascending!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: Symbol>>ascending (in category 'sorting') -----
>>>>>> + ascending
>>>>>> +       "Return a SortFunction around the receiver, where the
>>>>>> receiver
>>>>>> will be used as a unary message to send to both a and b during
>>>>>> sorting, and then the result of said send will be collated in
>>>>>> ascending order using the <=> method."
>>>>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
>>>>>> +
>>>>>> +       ^SortFunction ascend sendMessage: self!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: Symbol>>descending (in category 'sorting') -----
>>>>>> + descending
>>>>>> +       "Return a SortFunction around the receiver, where the
>>>>>> receiver
>>>>>> will be used as a unary message to send to both a and b during
>>>>>> sorting, and then the result of said send will be collated in
>>>>>> descending order using the <=> method."
>>>>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size descending"
>>>>>> +
>>>>>> +       ^SortFunction descend sendMessage: self!
>>>>>>
>>>>>> Item was added:
>>>>>> + ----- Method: Text>><=> (in category 'sorting') -----
>>>>>> + <=> aCharacterArray
>>>>>> +       "Return a collation order of -1, 0, or 1, indicating whether
>>>>>> I
>>>>>> should be collated before the receiver, am equal, or after.
>>>>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>>>>> +
>>>>>> +       ^string = aCharacterArray
>>>>>> +               ifTrue: [ 0 ]
>>>>>> +               ifFalse: [string < aCharacterArray asString
>>>>>> +                       ifTrue: [ -1 ]
>>>>>> +                       ifFalse: [ 1 ]]!
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>>
>



Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Nicolas Cellier
In reply to this post by David T. Lewis


2014-09-22 17:51 GMT+02:00 David T. Lewis <[hidden email]>:
We need positive confirmation that the code derived from that public
repository was licensed MIT, or a statement from the original author that
is now being released under MIT. I'm sure that the original author will be
happy to do that, but we do need a clear statement of license before we
can allow the code to be added to trunk.

Dave


Sorry  for the late reply.
I mistakenly assumed the license problem was solved, but after verification, there is no mention of a license on the blog nor on the cincom public store repository.
I have sent private e-mail to Travis 2 days ago, but haven't received an answer yet.
I don't know him personnally, so I would welcome any one having a more direct channel to contact him.
Without a clear answer we'll have to retract these commits.
Unless you prefer a preventive retractation right now?

Nicolas


> It was ported from a "public repository" into trunk.  Everything
> posted to trunk is MIT.
>
>
> On Mon, Sep 22, 2014 at 3:26 AM, Frank Shearar <[hidden email]>
> wrote:
>> Yes, it looks very very nice.
>>
>> What's its licence again?
>>
>> frank
>>
>> On 21 September 2014 22:46, Chris Muller <[hidden email]> wrote:
>>> What a *gorgeous* solution to multi-level sorting!  I love this!!
>>>
>>> Now I need to update some of my images to latest trunk just so I can
>>> get this..
>>>
>>> On Fri, Sep 19, 2014 at 3:24 PM,  <[hidden email]> wrote:
>>>> Nicolas Cellier uploaded a new version of Collections to project The
>>>> Trunk:
>>>> http://source.squeak.org/trunk/Collections-nice.582.mcz
>>>>
>>>> ==================== Summary ====================
>>>>
>>>> Name: Collections-nice.582
>>>> Author: nice
>>>> Time: 19 September 2014, 10:22:13.338 pm
>>>> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
>>>> Ancestors: Collections-nice.581
>>>>
>>>> Port TAG-SortFunctions of Travis Griggs from Cincom public store -
>>>> version (11,tgriggs)
>>>>
>>>> Note that no collation policy were used for String.
>>>> The spaceship operator <=> is also required in Kernel-Numbers.
>>>>
>>>> See also the blog
>>>> http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>>>>  and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>>>>
>>>> Note about the cost of these sort functions:
>>>> as shown by this mini-bench on cog, using a Symbol costs a bit more
>>>> (perform:) than a block activation, and monadic block a bit more than
>>>> dyadic one because activated twice more, but it seems acceptable to me
>>>> with regard to the great simplification and expressiveness of code :
>>>>
>>>> | collec1 collec2 collec3 |
>>>> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
>>>> collec2 := collec1 copy.
>>>> collec3 := collec1 copy.
>>>> {
>>>> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
>>>> [collec2 sort: [:e | e abs] ascending] timeToRun.
>>>> [collec3 sort: #abs ascending] timeToRun.
>>>> }
>>>>  #(345 532 912)
>>>>
>>>> =============== Diff against Collections-nice.581 ===============
>>>>
>>>> Item was added:
>>>> + SortFunction subclass: #ChainedSortFunction
>>>> +       instanceVariableNames: 'next'
>>>> +       classVariableNames: ''
>>>> +       poolDictionaries: ''
>>>> +       category: 'Collections-Support'!
>>>> +
>>>> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior: 0!
>>>> + I add to my parent the idea of a "next" function to use when two
>>>> objects are equal by my particular collator.
>>>> +
>>>> + Usage
>>>> +
>>>> + SortFunctions can be chained together in primary, secondary,
>>>> tertiary, etc order using the comma method. Consider a sequence of
>>>> customer objects, where each customer object responds to the messages
>>>> firstName, lastName, and age. If we want to sort them lastName first,
>>>> then firstName, and finally oldest first, we would use an expression
>>>> like:
>>>> +
>>>> + customers sort: #lastName ascending, #firstName ascending, #age
>>>> descending
>>>> +
>>>> + As noted in my super's comment, unary symbols or single arg blocks
>>>> can be used. One can omit the the ascending methods on arguments (not
>>>> the receiver), it will default blocks or symbols to be ascending if
>>>> none is specified. In other words, the above expression could be
>>>> simplified slightly as
>>>> +
>>>> + customers sort: #lastName ascending, #firstName, #age descending
>>>> +
>>>> + (note the missing ascending on the #firstName argument)
>>>> +
>>>> + Instance Variables
>>>> +       next    <SortFunction>  the next SortFunction to evaluate in
>>>> the event my evaluator results in equal values.
>>>> +
>>>> + !
>>>>
>>>> Item was added:
>>>> + ----- Method: ChainedSortFunction>>, (in category 'converting')
>>>> -----
>>>> + , aSortFunction
>>>> +
>>>> +       self next: next , aSortFunction!
>>>>
>>>> Item was added:
>>>> + ----- Method: ChainedSortFunction>>next: (in category
>>>> 'initialize-release') -----
>>>> + next: anObject
>>>> +
>>>> +       next := anObject!
>>>>
>>>> Item was added:
>>>> + ----- Method: ChainedSortFunction>>value:value: (in category
>>>> 'evaluating') -----
>>>> + value: anObject value: bObject
>>>> +       "Refinement of the parent behavior. If the result of my
>>>> collator is 0, then pass on to the next variable to work it out."
>>>> +
>>>> +       | result |
>>>> +       result := (collator value: anObject value: bObject) *
>>>> direction.
>>>> +       ^result isZero
>>>> +               ifTrue: [next value: anObject value: bObject]
>>>> +               ifFalse: [result < 0]!
>>>>
>>>> Item was added:
>>>> + Object subclass: #SortFunction
>>>> +       instanceVariableNames: 'direction collator'
>>>> +       classVariableNames: ''
>>>> +       poolDictionaries: ''
>>>> +       category: 'Collections-Support'!
>>>> +
>>>> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
>>>> + I am intended to be used in place of two arg sort blocks.
>>>> +
>>>> + Usage
>>>> +
>>>> + In the following example, an ascending SortFunction is created based
>>>> on the result of the #first message send to each object.
>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
>>>> +
>>>> + To sort by the #last element, but descending, the following would be
>>>> used:
>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
>>>> +
>>>> + One can use blocks as well. The following sorts in descending order,
>>>> the sub elements based on the sum of their values.
>>>> + | sumBlock |
>>>> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each | sum
>>>> + each]].
>>>> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
>>>> +
>>>> + One can even use 2 arg blocks, for those cases where the function
>>>> isn't expressible with objects that respond to < and =. The only
>>>> catch, is that such a function has to return not true and false, but
>>>> instead a collation order, values of -1 (for before), 0 (the same) or
>>>> 1 (to follow). For example:
>>>> +
>>>> + | oddBlock |
>>>> + oddBlock :=
>>>> +               [:a :b |
>>>> +               a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue: [-1]
>>>> ifFalse: [1]]].
>>>> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
>>>> +
>>>> + Instance Variables
>>>> +       collator        <BlockClosure>  This is the collation function
>>>> that must return a -1, 0, or 1. It is usually composed by an
>>>> initialization method such as sendMessage: or monadicBlock:, but may
>>>> be set directly.
>>>> +       direction       <SmallInteger>  1 for ascending, -1 for
>>>> descending
>>>> +
>>>> + !
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction class>>ascend (in category 'instance
>>>> creation') -----
>>>> + ascend
>>>> +
>>>> +       ^self new ascend!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction class>>descend (in category 'instance
>>>> creation') -----
>>>> + descend
>>>> +
>>>> +       ^self new descend!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>, (in category 'converting') -----
>>>> + , aSortFunction
>>>> +       "Return a new SortFunction which is the concatenation of
>>>> aSortFunction to me, I will be the primary sort, but if I compare
>>>> equal, I will defer to the argument."
>>>> +
>>>> +       ^(ChainedSortFunction new)
>>>> +               collator: collator;
>>>> +               direction: direction;
>>>> +               next: aSortFunction asSortFunction!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>asSortFunction (in category
>>>> 'converting') -----
>>>> + asSortFunction
>>>> +
>>>> +       ^self!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>ascend (in category
>>>> 'initailize-release') -----
>>>> + ascend
>>>> +
>>>> +       direction := 1!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>collator: (in category
>>>> 'initailize-release') -----
>>>> + collator: a2ArgBlock
>>>> +       "a2ArgBlock MUST return the collation order, -1, 0, or 1"
>>>> +
>>>> +       collator := a2ArgBlock!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>descend (in category
>>>> 'initailize-release') -----
>>>> + descend
>>>> +
>>>> +       direction := -1!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>direction: (in category
>>>> 'initailize-release') -----
>>>> + direction: anInteger
>>>> +
>>>> +       direction := anInteger!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>isAscending (in category 'testing')
>>>> -----
>>>> + isAscending
>>>> +
>>>> +       ^direction = 1!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>isDescending (in category 'testing')
>>>> -----
>>>> + isDescending
>>>> +
>>>> +       ^direction = -1!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>monadicBlock: (in category
>>>> 'initailize-release') -----
>>>> + monadicBlock: aSingleArgBlock
>>>> +       "Initialze the receiver's collation block to compare the
>>>> results of evaluating aSingleArgBlock with each argument, and then
>>>> collate the return values with the <=> method."
>>>> +
>>>> +       collator := [:a :b | (aSingleArgBlock value: a) <=>
>>>> (aSingleArgBlock value: b)]!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>sendMessage: (in category
>>>> 'initailize-release') -----
>>>> + sendMessage: aUnarySymbol
>>>> +       "Initialze the receiver's collation block to compare the
>>>> results of sending aUnarySymbol to each argument, and then collate
>>>> them with the <=> method."
>>>> +
>>>> +       collator := [:a :b | (a perform: aUnarySymbol) <=> (b perform:
>>>> aUnarySymbol)]!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>toggleDirection (in category
>>>> 'converting') -----
>>>> + toggleDirection
>>>> +       "Invert my current direction, if I'm currently ascending, this
>>>> will cause me to be descending now, and vice-versa."
>>>> +
>>>> +       direction := direction * -1!
>>>>
>>>> Item was added:
>>>> + ----- Method: SortFunction>>value:value: (in category 'evaluating')
>>>> -----
>>>> + value: anObject value: bObject
>>>> +       "Masquerade as a two argument block, used by many of the
>>>> sorting APIs, by returning whether anObject should be placed before
>>>> bObject or not."
>>>> +
>>>> +       | result |
>>>> +       result := (collator value: anObject value: bObject) *
>>>> direction.
>>>> +       ^result <= 0!
>>>>
>>>> Item was added:
>>>> + ----- Method: String>><=> (in category 'sorting') -----
>>>> + <=> aCharacterArray
>>>> +       "Return a collation order of -1, 0, or 1, indicating whether I
>>>> should be collated before the receiver, am equal, or after.
>>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>>> +
>>>> +       ^self = aCharacterArray
>>>> +               ifTrue: [ 0 ]
>>>> +               ifFalse: [self < aCharacterArray
>>>> +                       ifTrue: [ -1 ]
>>>> +                       ifFalse: [ 1 ]]!
>>>>
>>>> Item was added:
>>>> + ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
>>>> + asSortFunction
>>>> +
>>>> +       ^self ascending!
>>>>
>>>> Item was added:
>>>> + ----- Method: Symbol>>ascending (in category 'sorting') -----
>>>> + ascending
>>>> +       "Return a SortFunction around the receiver, where the receiver
>>>> will be used as a unary message to send to both a and b during
>>>> sorting, and then the result of said send will be collated in
>>>> ascending order using the <=> method."
>>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
>>>> +
>>>> +       ^SortFunction ascend sendMessage: self!
>>>>
>>>> Item was added:
>>>> + ----- Method: Symbol>>descending (in category 'sorting') -----
>>>> + descending
>>>> +       "Return a SortFunction around the receiver, where the receiver
>>>> will be used as a unary message to send to both a and b during
>>>> sorting, and then the result of said send will be collated in
>>>> descending order using the <=> method."
>>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size descending"
>>>> +
>>>> +       ^SortFunction descend sendMessage: self!
>>>>
>>>> Item was added:
>>>> + ----- Method: Text>><=> (in category 'sorting') -----
>>>> + <=> aCharacterArray
>>>> +       "Return a collation order of -1, 0, or 1, indicating whether I
>>>> should be collated before the receiver, am equal, or after.
>>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>>> +
>>>> +       ^string = aCharacterArray
>>>> +               ifTrue: [ 0 ]
>>>> +               ifFalse: [string < aCharacterArray asString
>>>> +                       ifTrue: [ -1 ]
>>>> +                       ifFalse: [ 1 ]]!
>>>>
>>>>
>>>
>>
>






Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

David T. Lewis
No, no, sorry I did not mean to cause alarm! Thanks for contacting Travis.
I don't know him but I'm sure he will be happy to help us, so let's just
wait for his reply.

Thanks for following up on this, much appreciated.

Dave


> 2014-09-22 17:51 GMT+02:00 David T. Lewis <[hidden email]>:
>
>> We need positive confirmation that the code derived from that public
>> repository was licensed MIT, or a statement from the original author
>> that
>> is now being released under MIT. I'm sure that the original author will
>> be
>> happy to do that, but we do need a clear statement of license before we
>> can allow the code to be added to trunk.
>>
>> Dave
>>
>>
> Sorry  for the late reply.
> I mistakenly assumed the license problem was solved, but after
> verification, there is no mention of a license on the blog nor on the
> cincom public store repository.
> I have sent private e-mail to Travis 2 days ago, but haven't received an
> answer yet.
> I don't know him personnally, so I would welcome any one having a more
> direct channel to contact him.
> Without a clear answer we'll have to retract these commits.
> Unless you prefer a preventive retractation right now?
>
> Nicolas
>
>
>> It was ported from a "public repository" into trunk.  Everything
>> > posted to trunk is MIT.
>> >
>> >
>> > On Mon, Sep 22, 2014 at 3:26 AM, Frank Shearar
>> <[hidden email]>
>> > wrote:
>> >> Yes, it looks very very nice.
>> >>
>> >> What's its licence again?
>> >>
>> >> frank
>> >>
>> >> On 21 September 2014 22:46, Chris Muller <[hidden email]> wrote:
>> >>> What a *gorgeous* solution to multi-level sorting!  I love this!!
>> >>>
>> >>> Now I need to update some of my images to latest trunk just so I can
>> >>> get this..
>> >>>
>> >>> On Fri, Sep 19, 2014 at 3:24 PM,  <[hidden email]> wrote:
>> >>>> Nicolas Cellier uploaded a new version of Collections to project
>> The
>> >>>> Trunk:
>> >>>> http://source.squeak.org/trunk/Collections-nice.582.mcz
>> >>>>
>> >>>> ==================== Summary ====================
>> >>>>
>> >>>> Name: Collections-nice.582
>> >>>> Author: nice
>> >>>> Time: 19 September 2014, 10:22:13.338 pm
>> >>>> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
>> >>>> Ancestors: Collections-nice.581
>> >>>>
>> >>>> Port TAG-SortFunctions of Travis Griggs from Cincom public store -
>> >>>> version (11,tgriggs)
>> >>>>
>> >>>> Note that no collation policy were used for String.
>> >>>> The spaceship operator <=> is also required in Kernel-Numbers.
>> >>>>
>> >>>> See also the blog
>> >>>> http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>> >>>>  and
>> http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>> >>>>
>> >>>> Note about the cost of these sort functions:
>> >>>> as shown by this mini-bench on cog, using a Symbol costs a bit more
>> >>>> (perform:) than a block activation, and monadic block a bit more
>> than
>> >>>> dyadic one because activated twice more, but it seems acceptable to
>> me
>> >>>> with regard to the great simplification and expressiveness of code
>> :
>> >>>>
>> >>>> | collec1 collec2 collec3 |
>> >>>> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
>> >>>> collec2 := collec1 copy.
>> >>>> collec3 := collec1 copy.
>> >>>> {
>> >>>> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
>> >>>> [collec2 sort: [:e | e abs] ascending] timeToRun.
>> >>>> [collec3 sort: #abs ascending] timeToRun.
>> >>>> }
>> >>>>  #(345 532 912)
>> >>>>
>> >>>> =============== Diff against Collections-nice.581 ===============
>> >>>>
>> >>>> Item was added:
>> >>>> + SortFunction subclass: #ChainedSortFunction
>> >>>> +       instanceVariableNames: 'next'
>> >>>> +       classVariableNames: ''
>> >>>> +       poolDictionaries: ''
>> >>>> +       category: 'Collections-Support'!
>> >>>> +
>> >>>> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior:
>> 0!
>> >>>> + I add to my parent the idea of a "next" function to use when two
>> >>>> objects are equal by my particular collator.
>> >>>> +
>> >>>> + Usage
>> >>>> +
>> >>>> + SortFunctions can be chained together in primary, secondary,
>> >>>> tertiary, etc order using the comma method. Consider a sequence of
>> >>>> customer objects, where each customer object responds to the
>> messages
>> >>>> firstName, lastName, and age. If we want to sort them lastName
>> first,
>> >>>> then firstName, and finally oldest first, we would use an
>> expression
>> >>>> like:
>> >>>> +
>> >>>> + customers sort: #lastName ascending, #firstName ascending, #age
>> >>>> descending
>> >>>> +
>> >>>> + As noted in my super's comment, unary symbols or single arg
>> blocks
>> >>>> can be used. One can omit the the ascending methods on arguments
>> (not
>> >>>> the receiver), it will default blocks or symbols to be ascending if
>> >>>> none is specified. In other words, the above expression could be
>> >>>> simplified slightly as
>> >>>> +
>> >>>> + customers sort: #lastName ascending, #firstName, #age descending
>> >>>> +
>> >>>> + (note the missing ascending on the #firstName argument)
>> >>>> +
>> >>>> + Instance Variables
>> >>>> +       next    <SortFunction>  the next SortFunction to evaluate
>> in
>> >>>> the event my evaluator results in equal values.
>> >>>> +
>> >>>> + !
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: ChainedSortFunction>>, (in category 'converting')
>> >>>> -----
>> >>>> + , aSortFunction
>> >>>> +
>> >>>> +       self next: next , aSortFunction!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: ChainedSortFunction>>next: (in category
>> >>>> 'initialize-release') -----
>> >>>> + next: anObject
>> >>>> +
>> >>>> +       next := anObject!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: ChainedSortFunction>>value:value: (in category
>> >>>> 'evaluating') -----
>> >>>> + value: anObject value: bObject
>> >>>> +       "Refinement of the parent behavior. If the result of my
>> >>>> collator is 0, then pass on to the next variable to work it out."
>> >>>> +
>> >>>> +       | result |
>> >>>> +       result := (collator value: anObject value: bObject) *
>> >>>> direction.
>> >>>> +       ^result isZero
>> >>>> +               ifTrue: [next value: anObject value: bObject]
>> >>>> +               ifFalse: [result < 0]!
>> >>>>
>> >>>> Item was added:
>> >>>> + Object subclass: #SortFunction
>> >>>> +       instanceVariableNames: 'direction collator'
>> >>>> +       classVariableNames: ''
>> >>>> +       poolDictionaries: ''
>> >>>> +       category: 'Collections-Support'!
>> >>>> +
>> >>>> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
>> >>>> + I am intended to be used in place of two arg sort blocks.
>> >>>> +
>> >>>> + Usage
>> >>>> +
>> >>>> + In the following example, an ascending SortFunction is created
>> based
>> >>>> on the result of the #first message send to each object.
>> >>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
>> >>>> +
>> >>>> + To sort by the #last element, but descending, the following would
>> be
>> >>>> used:
>> >>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
>> >>>> +
>> >>>> + One can use blocks as well. The following sorts in descending
>> order,
>> >>>> the sub elements based on the sum of their values.
>> >>>> + | sumBlock |
>> >>>> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each |
>> sum
>> >>>> + each]].
>> >>>> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
>> >>>> +
>> >>>> + One can even use 2 arg blocks, for those cases where the function
>> >>>> isn't expressible with objects that respond to < and =. The only
>> >>>> catch, is that such a function has to return not true and false,
>> but
>> >>>> instead a collation order, values of -1 (for before), 0 (the same)
>> or
>> >>>> 1 (to follow). For example:
>> >>>> +
>> >>>> + | oddBlock |
>> >>>> + oddBlock :=
>> >>>> +               [:a :b |
>> >>>> +               a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue:
>> [-1]
>> >>>> ifFalse: [1]]].
>> >>>> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
>> >>>> +
>> >>>> + Instance Variables
>> >>>> +       collator        <BlockClosure>  This is the collation
>> function
>> >>>> that must return a -1, 0, or 1. It is usually composed by an
>> >>>> initialization method such as sendMessage: or monadicBlock:, but
>> may
>> >>>> be set directly.
>> >>>> +       direction       <SmallInteger>  1 for ascending, -1 for
>> >>>> descending
>> >>>> +
>> >>>> + !
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction class>>ascend (in category 'instance
>> >>>> creation') -----
>> >>>> + ascend
>> >>>> +
>> >>>> +       ^self new ascend!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction class>>descend (in category 'instance
>> >>>> creation') -----
>> >>>> + descend
>> >>>> +
>> >>>> +       ^self new descend!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>, (in category 'converting') -----
>> >>>> + , aSortFunction
>> >>>> +       "Return a new SortFunction which is the concatenation of
>> >>>> aSortFunction to me, I will be the primary sort, but if I compare
>> >>>> equal, I will defer to the argument."
>> >>>> +
>> >>>> +       ^(ChainedSortFunction new)
>> >>>> +               collator: collator;
>> >>>> +               direction: direction;
>> >>>> +               next: aSortFunction asSortFunction!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>asSortFunction (in category
>> >>>> 'converting') -----
>> >>>> + asSortFunction
>> >>>> +
>> >>>> +       ^self!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>ascend (in category
>> >>>> 'initailize-release') -----
>> >>>> + ascend
>> >>>> +
>> >>>> +       direction := 1!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>collator: (in category
>> >>>> 'initailize-release') -----
>> >>>> + collator: a2ArgBlock
>> >>>> +       "a2ArgBlock MUST return the collation order, -1, 0, or 1"
>> >>>> +
>> >>>> +       collator := a2ArgBlock!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>descend (in category
>> >>>> 'initailize-release') -----
>> >>>> + descend
>> >>>> +
>> >>>> +       direction := -1!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>direction: (in category
>> >>>> 'initailize-release') -----
>> >>>> + direction: anInteger
>> >>>> +
>> >>>> +       direction := anInteger!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>isAscending (in category 'testing')
>> >>>> -----
>> >>>> + isAscending
>> >>>> +
>> >>>> +       ^direction = 1!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>isDescending (in category 'testing')
>> >>>> -----
>> >>>> + isDescending
>> >>>> +
>> >>>> +       ^direction = -1!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>monadicBlock: (in category
>> >>>> 'initailize-release') -----
>> >>>> + monadicBlock: aSingleArgBlock
>> >>>> +       "Initialze the receiver's collation block to compare the
>> >>>> results of evaluating aSingleArgBlock with each argument, and then
>> >>>> collate the return values with the <=> method."
>> >>>> +
>> >>>> +       collator := [:a :b | (aSingleArgBlock value: a) <=>
>> >>>> (aSingleArgBlock value: b)]!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>sendMessage: (in category
>> >>>> 'initailize-release') -----
>> >>>> + sendMessage: aUnarySymbol
>> >>>> +       "Initialze the receiver's collation block to compare the
>> >>>> results of sending aUnarySymbol to each argument, and then collate
>> >>>> them with the <=> method."
>> >>>> +
>> >>>> +       collator := [:a :b | (a perform: aUnarySymbol) <=> (b
>> perform:
>> >>>> aUnarySymbol)]!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>toggleDirection (in category
>> >>>> 'converting') -----
>> >>>> + toggleDirection
>> >>>> +       "Invert my current direction, if I'm currently ascending,
>> this
>> >>>> will cause me to be descending now, and vice-versa."
>> >>>> +
>> >>>> +       direction := direction * -1!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>value:value: (in category
>> 'evaluating')
>> >>>> -----
>> >>>> + value: anObject value: bObject
>> >>>> +       "Masquerade as a two argument block, used by many of the
>> >>>> sorting APIs, by returning whether anObject should be placed before
>> >>>> bObject or not."
>> >>>> +
>> >>>> +       | result |
>> >>>> +       result := (collator value: anObject value: bObject) *
>> >>>> direction.
>> >>>> +       ^result <= 0!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: String>><=> (in category 'sorting') -----
>> >>>> + <=> aCharacterArray
>> >>>> +       "Return a collation order of -1, 0, or 1, indicating
>> whether I
>> >>>> should be collated before the receiver, am equal, or after.
>> >>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>> >>>> +
>> >>>> +       ^self = aCharacterArray
>> >>>> +               ifTrue: [ 0 ]
>> >>>> +               ifFalse: [self < aCharacterArray
>> >>>> +                       ifTrue: [ -1 ]
>> >>>> +                       ifFalse: [ 1 ]]!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: Symbol>>asSortFunction (in category 'sorting')
>> -----
>> >>>> + asSortFunction
>> >>>> +
>> >>>> +       ^self ascending!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: Symbol>>ascending (in category 'sorting') -----
>> >>>> + ascending
>> >>>> +       "Return a SortFunction around the receiver, where the
>> receiver
>> >>>> will be used as a unary message to send to both a and b during
>> >>>> sorting, and then the result of said send will be collated in
>> >>>> ascending order using the <=> method."
>> >>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
>> >>>> +
>> >>>> +       ^SortFunction ascend sendMessage: self!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: Symbol>>descending (in category 'sorting') -----
>> >>>> + descending
>> >>>> +       "Return a SortFunction around the receiver, where the
>> receiver
>> >>>> will be used as a unary message to send to both a and b during
>> >>>> sorting, and then the result of said send will be collated in
>> >>>> descending order using the <=> method."
>> >>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size descending"
>> >>>> +
>> >>>> +       ^SortFunction descend sendMessage: self!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: Text>><=> (in category 'sorting') -----
>> >>>> + <=> aCharacterArray
>> >>>> +       "Return a collation order of -1, 0, or 1, indicating
>> whether I
>> >>>> should be collated before the receiver, am equal, or after.
>> >>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>> >>>> +
>> >>>> +       ^string = aCharacterArray
>> >>>> +               ifTrue: [ 0 ]
>> >>>> +               ifFalse: [string < aCharacterArray asString
>> >>>> +                       ifTrue: [ -1 ]
>> >>>> +                       ifFalse: [ 1 ]]!
>> >>>>
>> >>>>
>> >>>
>> >>
>> >
>>
>>
>>
>>
>
>



Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Chris Muller-3
In reply to this post by Nicolas Cellier
On Mon, Sep 22, 2014 at 12:10 PM, Nicolas Cellier
<[hidden email]> wrote:

>
>
> 2014-09-22 17:51 GMT+02:00 David T. Lewis <[hidden email]>:
>>
>> We need positive confirmation that the code derived from that public
>> repository was licensed MIT, or a statement from the original author that
>> is now being released under MIT. I'm sure that the original author will be
>> happy to do that, but we do need a clear statement of license before we
>> can allow the code to be added to trunk.
>>
>> Dave
>>
>
> Sorry  for the late reply.
> I mistakenly assumed the license problem was solved, but after verification,
> there is no mention of a license on the blog nor on the cincom public store
> repository.
> I have sent private e-mail to Travis 2 days ago, but haven't received an
> answer yet.
> I don't know him personnally, so I would welcome any one having a more
> direct channel to contact him.
> Without a clear answer we'll have to retract these commits.
>
> Unless you prefer a preventive retractation right now?

Travis hasn't been involved with Smalltalk for over 2 years now

  http://objology.blogspot.com/2012/05/stepping-out-of-balloon.html

so its very possible you'll get no reply.

Note that Cincom reprinted his blog post under their own domain,
including the part that encourages to "port it to your own flavor of
Smalltalk".

   http://www.cincomsmalltalk.com/main/2010/12/tag-sortfunctions/

If they would object to this being ported, why would they reprint it
and not "clarify" that point?

To me, it seems clear that this was meant for adoption by the
Smalltalk community at large.  It would be a shame to subvert that by
some (mis)perceived legal technicality.  We are not lawyers, but in
case we get no reply, if we're that concerned about a lawsuit (waged
by Cincom?), the SFC does have a legal resource whom we could ask to
evaluate it.

> Nicolas
>
>
>> > It was ported from a "public repository" into trunk.  Everything
>> > posted to trunk is MIT.
>> >
>> >
>> > On Mon, Sep 22, 2014 at 3:26 AM, Frank Shearar <[hidden email]>
>> > wrote:
>> >> Yes, it looks very very nice.
>> >>
>> >> What's its licence again?
>> >>
>> >> frank
>> >>
>> >> On 21 September 2014 22:46, Chris Muller <[hidden email]> wrote:
>> >>> What a *gorgeous* solution to multi-level sorting!  I love this!!
>> >>>
>> >>> Now I need to update some of my images to latest trunk just so I can
>> >>> get this..
>> >>>
>> >>> On Fri, Sep 19, 2014 at 3:24 PM,  <[hidden email]> wrote:
>> >>>> Nicolas Cellier uploaded a new version of Collections to project The
>> >>>> Trunk:
>> >>>> http://source.squeak.org/trunk/Collections-nice.582.mcz
>> >>>>
>> >>>> ==================== Summary ====================
>> >>>>
>> >>>> Name: Collections-nice.582
>> >>>> Author: nice
>> >>>> Time: 19 September 2014, 10:22:13.338 pm
>> >>>> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
>> >>>> Ancestors: Collections-nice.581
>> >>>>
>> >>>> Port TAG-SortFunctions of Travis Griggs from Cincom public store -
>> >>>> version (11,tgriggs)
>> >>>>
>> >>>> Note that no collation policy were used for String.
>> >>>> The spaceship operator <=> is also required in Kernel-Numbers.
>> >>>>
>> >>>> See also the blog
>> >>>> http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>> >>>>  and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>> >>>>
>> >>>> Note about the cost of these sort functions:
>> >>>> as shown by this mini-bench on cog, using a Symbol costs a bit more
>> >>>> (perform:) than a block activation, and monadic block a bit more than
>> >>>> dyadic one because activated twice more, but it seems acceptable to
>> >>>> me
>> >>>> with regard to the great simplification and expressiveness of code :
>> >>>>
>> >>>> | collec1 collec2 collec3 |
>> >>>> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
>> >>>> collec2 := collec1 copy.
>> >>>> collec3 := collec1 copy.
>> >>>> {
>> >>>> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
>> >>>> [collec2 sort: [:e | e abs] ascending] timeToRun.
>> >>>> [collec3 sort: #abs ascending] timeToRun.
>> >>>> }
>> >>>>  #(345 532 912)
>> >>>>
>> >>>> =============== Diff against Collections-nice.581 ===============
>> >>>>
>> >>>> Item was added:
>> >>>> + SortFunction subclass: #ChainedSortFunction
>> >>>> +       instanceVariableNames: 'next'
>> >>>> +       classVariableNames: ''
>> >>>> +       poolDictionaries: ''
>> >>>> +       category: 'Collections-Support'!
>> >>>> +
>> >>>> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior: 0!
>> >>>> + I add to my parent the idea of a "next" function to use when two
>> >>>> objects are equal by my particular collator.
>> >>>> +
>> >>>> + Usage
>> >>>> +
>> >>>> + SortFunctions can be chained together in primary, secondary,
>> >>>> tertiary, etc order using the comma method. Consider a sequence of
>> >>>> customer objects, where each customer object responds to the messages
>> >>>> firstName, lastName, and age. If we want to sort them lastName first,
>> >>>> then firstName, and finally oldest first, we would use an expression
>> >>>> like:
>> >>>> +
>> >>>> + customers sort: #lastName ascending, #firstName ascending, #age
>> >>>> descending
>> >>>> +
>> >>>> + As noted in my super's comment, unary symbols or single arg blocks
>> >>>> can be used. One can omit the the ascending methods on arguments (not
>> >>>> the receiver), it will default blocks or symbols to be ascending if
>> >>>> none is specified. In other words, the above expression could be
>> >>>> simplified slightly as
>> >>>> +
>> >>>> + customers sort: #lastName ascending, #firstName, #age descending
>> >>>> +
>> >>>> + (note the missing ascending on the #firstName argument)
>> >>>> +
>> >>>> + Instance Variables
>> >>>> +       next    <SortFunction>  the next SortFunction to evaluate in
>> >>>> the event my evaluator results in equal values.
>> >>>> +
>> >>>> + !
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: ChainedSortFunction>>, (in category 'converting')
>> >>>> -----
>> >>>> + , aSortFunction
>> >>>> +
>> >>>> +       self next: next , aSortFunction!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: ChainedSortFunction>>next: (in category
>> >>>> 'initialize-release') -----
>> >>>> + next: anObject
>> >>>> +
>> >>>> +       next := anObject!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: ChainedSortFunction>>value:value: (in category
>> >>>> 'evaluating') -----
>> >>>> + value: anObject value: bObject
>> >>>> +       "Refinement of the parent behavior. If the result of my
>> >>>> collator is 0, then pass on to the next variable to work it out."
>> >>>> +
>> >>>> +       | result |
>> >>>> +       result := (collator value: anObject value: bObject) *
>> >>>> direction.
>> >>>> +       ^result isZero
>> >>>> +               ifTrue: [next value: anObject value: bObject]
>> >>>> +               ifFalse: [result < 0]!
>> >>>>
>> >>>> Item was added:
>> >>>> + Object subclass: #SortFunction
>> >>>> +       instanceVariableNames: 'direction collator'
>> >>>> +       classVariableNames: ''
>> >>>> +       poolDictionaries: ''
>> >>>> +       category: 'Collections-Support'!
>> >>>> +
>> >>>> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
>> >>>> + I am intended to be used in place of two arg sort blocks.
>> >>>> +
>> >>>> + Usage
>> >>>> +
>> >>>> + In the following example, an ascending SortFunction is created
>> >>>> based
>> >>>> on the result of the #first message send to each object.
>> >>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
>> >>>> +
>> >>>> + To sort by the #last element, but descending, the following would
>> >>>> be
>> >>>> used:
>> >>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
>> >>>> +
>> >>>> + One can use blocks as well. The following sorts in descending
>> >>>> order,
>> >>>> the sub elements based on the sum of their values.
>> >>>> + | sumBlock |
>> >>>> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each | sum
>> >>>> + each]].
>> >>>> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
>> >>>> +
>> >>>> + One can even use 2 arg blocks, for those cases where the function
>> >>>> isn't expressible with objects that respond to < and =. The only
>> >>>> catch, is that such a function has to return not true and false, but
>> >>>> instead a collation order, values of -1 (for before), 0 (the same) or
>> >>>> 1 (to follow). For example:
>> >>>> +
>> >>>> + | oddBlock |
>> >>>> + oddBlock :=
>> >>>> +               [:a :b |
>> >>>> +               a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue:
>> >>>> [-1]
>> >>>> ifFalse: [1]]].
>> >>>> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
>> >>>> +
>> >>>> + Instance Variables
>> >>>> +       collator        <BlockClosure>  This is the collation
>> >>>> function
>> >>>> that must return a -1, 0, or 1. It is usually composed by an
>> >>>> initialization method such as sendMessage: or monadicBlock:, but may
>> >>>> be set directly.
>> >>>> +       direction       <SmallInteger>  1 for ascending, -1 for
>> >>>> descending
>> >>>> +
>> >>>> + !
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction class>>ascend (in category 'instance
>> >>>> creation') -----
>> >>>> + ascend
>> >>>> +
>> >>>> +       ^self new ascend!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction class>>descend (in category 'instance
>> >>>> creation') -----
>> >>>> + descend
>> >>>> +
>> >>>> +       ^self new descend!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>, (in category 'converting') -----
>> >>>> + , aSortFunction
>> >>>> +       "Return a new SortFunction which is the concatenation of
>> >>>> aSortFunction to me, I will be the primary sort, but if I compare
>> >>>> equal, I will defer to the argument."
>> >>>> +
>> >>>> +       ^(ChainedSortFunction new)
>> >>>> +               collator: collator;
>> >>>> +               direction: direction;
>> >>>> +               next: aSortFunction asSortFunction!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>asSortFunction (in category
>> >>>> 'converting') -----
>> >>>> + asSortFunction
>> >>>> +
>> >>>> +       ^self!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>ascend (in category
>> >>>> 'initailize-release') -----
>> >>>> + ascend
>> >>>> +
>> >>>> +       direction := 1!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>collator: (in category
>> >>>> 'initailize-release') -----
>> >>>> + collator: a2ArgBlock
>> >>>> +       "a2ArgBlock MUST return the collation order, -1, 0, or 1"
>> >>>> +
>> >>>> +       collator := a2ArgBlock!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>descend (in category
>> >>>> 'initailize-release') -----
>> >>>> + descend
>> >>>> +
>> >>>> +       direction := -1!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>direction: (in category
>> >>>> 'initailize-release') -----
>> >>>> + direction: anInteger
>> >>>> +
>> >>>> +       direction := anInteger!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>isAscending (in category 'testing')
>> >>>> -----
>> >>>> + isAscending
>> >>>> +
>> >>>> +       ^direction = 1!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>isDescending (in category 'testing')
>> >>>> -----
>> >>>> + isDescending
>> >>>> +
>> >>>> +       ^direction = -1!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>monadicBlock: (in category
>> >>>> 'initailize-release') -----
>> >>>> + monadicBlock: aSingleArgBlock
>> >>>> +       "Initialze the receiver's collation block to compare the
>> >>>> results of evaluating aSingleArgBlock with each argument, and then
>> >>>> collate the return values with the <=> method."
>> >>>> +
>> >>>> +       collator := [:a :b | (aSingleArgBlock value: a) <=>
>> >>>> (aSingleArgBlock value: b)]!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>sendMessage: (in category
>> >>>> 'initailize-release') -----
>> >>>> + sendMessage: aUnarySymbol
>> >>>> +       "Initialze the receiver's collation block to compare the
>> >>>> results of sending aUnarySymbol to each argument, and then collate
>> >>>> them with the <=> method."
>> >>>> +
>> >>>> +       collator := [:a :b | (a perform: aUnarySymbol) <=> (b
>> >>>> perform:
>> >>>> aUnarySymbol)]!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>toggleDirection (in category
>> >>>> 'converting') -----
>> >>>> + toggleDirection
>> >>>> +       "Invert my current direction, if I'm currently ascending,
>> >>>> this
>> >>>> will cause me to be descending now, and vice-versa."
>> >>>> +
>> >>>> +       direction := direction * -1!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: SortFunction>>value:value: (in category 'evaluating')
>> >>>> -----
>> >>>> + value: anObject value: bObject
>> >>>> +       "Masquerade as a two argument block, used by many of the
>> >>>> sorting APIs, by returning whether anObject should be placed before
>> >>>> bObject or not."
>> >>>> +
>> >>>> +       | result |
>> >>>> +       result := (collator value: anObject value: bObject) *
>> >>>> direction.
>> >>>> +       ^result <= 0!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: String>><=> (in category 'sorting') -----
>> >>>> + <=> aCharacterArray
>> >>>> +       "Return a collation order of -1, 0, or 1, indicating whether
>> >>>> I
>> >>>> should be collated before the receiver, am equal, or after.
>> >>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>> >>>> +
>> >>>> +       ^self = aCharacterArray
>> >>>> +               ifTrue: [ 0 ]
>> >>>> +               ifFalse: [self < aCharacterArray
>> >>>> +                       ifTrue: [ -1 ]
>> >>>> +                       ifFalse: [ 1 ]]!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
>> >>>> + asSortFunction
>> >>>> +
>> >>>> +       ^self ascending!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: Symbol>>ascending (in category 'sorting') -----
>> >>>> + ascending
>> >>>> +       "Return a SortFunction around the receiver, where the
>> >>>> receiver
>> >>>> will be used as a unary message to send to both a and b during
>> >>>> sorting, and then the result of said send will be collated in
>> >>>> ascending order using the <=> method."
>> >>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
>> >>>> +
>> >>>> +       ^SortFunction ascend sendMessage: self!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: Symbol>>descending (in category 'sorting') -----
>> >>>> + descending
>> >>>> +       "Return a SortFunction around the receiver, where the
>> >>>> receiver
>> >>>> will be used as a unary message to send to both a and b during
>> >>>> sorting, and then the result of said send will be collated in
>> >>>> descending order using the <=> method."
>> >>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size descending"
>> >>>> +
>> >>>> +       ^SortFunction descend sendMessage: self!
>> >>>>
>> >>>> Item was added:
>> >>>> + ----- Method: Text>><=> (in category 'sorting') -----
>> >>>> + <=> aCharacterArray
>> >>>> +       "Return a collation order of -1, 0, or 1, indicating whether
>> >>>> I
>> >>>> should be collated before the receiver, am equal, or after.
>> >>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>> >>>> +
>> >>>> +       ^string = aCharacterArray
>> >>>> +               ifTrue: [ 0 ]
>> >>>> +               ifFalse: [string < aCharacterArray asString
>> >>>> +                       ifTrue: [ -1 ]
>> >>>> +                       ifFalse: [ 1 ]]!
>> >>>>
>> >>>>
>> >>>
>> >>
>> >
>>
>>
>>
>
>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Frank Shearar-3
On 22 September 2014 20:10, Chris Muller <[hidden email]> wrote:

> On Mon, Sep 22, 2014 at 12:10 PM, Nicolas Cellier
> <[hidden email]> wrote:
>>
>>
>> 2014-09-22 17:51 GMT+02:00 David T. Lewis <[hidden email]>:
>>>
>>> We need positive confirmation that the code derived from that public
>>> repository was licensed MIT, or a statement from the original author that
>>> is now being released under MIT. I'm sure that the original author will be
>>> happy to do that, but we do need a clear statement of license before we
>>> can allow the code to be added to trunk.
>>>
>>> Dave
>>>
>>
>> Sorry  for the late reply.
>> I mistakenly assumed the license problem was solved, but after verification,
>> there is no mention of a license on the blog nor on the cincom public store
>> repository.
>> I have sent private e-mail to Travis 2 days ago, but haven't received an
>> answer yet.
>> I don't know him personnally, so I would welcome any one having a more
>> direct channel to contact him.
>> Without a clear answer we'll have to retract these commits.
>>
>> Unless you prefer a preventive retractation right now?
>
> Travis hasn't been involved with Smalltalk for over 2 years now
>
>   http://objology.blogspot.com/2012/05/stepping-out-of-balloon.html
>
> so its very possible you'll get no reply.
>
> Note that Cincom reprinted his blog post under their own domain,
> including the part that encourages to "port it to your own flavor of
> Smalltalk".
>
>    http://www.cincomsmalltalk.com/main/2010/12/tag-sortfunctions/
>
> If they would object to this being ported, why would they reprint it
> and not "clarify" that point?
>
> To me, it seems clear that this was meant for adoption by the
> Smalltalk community at large.  It would be a shame to subvert that by
> some (mis)perceived legal technicality.  We are not lawyers, but in
> case we get no reply, if we're that concerned about a lawsuit (waged
> by Cincom?), the SFC does have a legal resource whom we could ask to
> evaluate it.

"Port" means "translate into your Smalltalk". It does not have
anything at all to do with licences. I didn't see Travis saying "this
is public domain", I didn't see a licence declaration, so... what?
Hopefully Travis will come back and say "hey, you know, I'm a
signed-up contributor to Squeak, why are you even asking?" and then we
can all be happy.

Until then, we _do not know_. 'It was ported from a "public
repository" into trunk.  Everything posted to trunk is MIT.' is not an
argument. And the second statement's not even true. Us saying "you
submitted it, therefore it's MIT" is bogus. The submitter needs to
have signed the contributor agreement, _and have the right_ to sign
such an agreement. Your work contract might not permit you to do such
a thing.

And I'm not suggesting ripping anything out. Let's wait for Travis to
respond. Hopefully someone has a more direct line to him, given that
Nicolas has waited two days already.

But this is a very real problem.

frank

>> Nicolas
>>
>>
>>> > It was ported from a "public repository" into trunk.  Everything
>>> > posted to trunk is MIT.
>>> >
>>> >
>>> > On Mon, Sep 22, 2014 at 3:26 AM, Frank Shearar <[hidden email]>
>>> > wrote:
>>> >> Yes, it looks very very nice.
>>> >>
>>> >> What's its licence again?
>>> >>
>>> >> frank
>>> >>
>>> >> On 21 September 2014 22:46, Chris Muller <[hidden email]> wrote:
>>> >>> What a *gorgeous* solution to multi-level sorting!  I love this!!
>>> >>>
>>> >>> Now I need to update some of my images to latest trunk just so I can
>>> >>> get this..
>>> >>>
>>> >>> On Fri, Sep 19, 2014 at 3:24 PM,  <[hidden email]> wrote:
>>> >>>> Nicolas Cellier uploaded a new version of Collections to project The
>>> >>>> Trunk:
>>> >>>> http://source.squeak.org/trunk/Collections-nice.582.mcz
>>> >>>>
>>> >>>> ==================== Summary ====================
>>> >>>>
>>> >>>> Name: Collections-nice.582
>>> >>>> Author: nice
>>> >>>> Time: 19 September 2014, 10:22:13.338 pm
>>> >>>> UUID: 52a598f1-224e-4649-a95f-4e78547b5ed5
>>> >>>> Ancestors: Collections-nice.581
>>> >>>>
>>> >>>> Port TAG-SortFunctions of Travis Griggs from Cincom public store -
>>> >>>> version (11,tgriggs)
>>> >>>>
>>> >>>> Note that no collation policy were used for String.
>>> >>>> The spaceship operator <=> is also required in Kernel-Numbers.
>>> >>>>
>>> >>>> See also the blog
>>> >>>> http://objology.blogspot.fr/2010/11/tag-sortfunctions.html
>>> >>>>  and http://objology.blogspot.fr/2010/11/tag-sortfunctions-redux.html
>>> >>>>
>>> >>>> Note about the cost of these sort functions:
>>> >>>> as shown by this mini-bench on cog, using a Symbol costs a bit more
>>> >>>> (perform:) than a block activation, and monadic block a bit more than
>>> >>>> dyadic one because activated twice more, but it seems acceptable to
>>> >>>> me
>>> >>>> with regard to the great simplification and expressiveness of code :
>>> >>>>
>>> >>>> | collec1 collec2 collec3 |
>>> >>>> collec1 := (1 to: 200000) collect: [:i | 1000000 atRandom-500000].
>>> >>>> collec2 := collec1 copy.
>>> >>>> collec3 := collec1 copy.
>>> >>>> {
>>> >>>> [collec1 sort: [:a :b | a abs < b abs]] timeToRun.
>>> >>>> [collec2 sort: [:e | e abs] ascending] timeToRun.
>>> >>>> [collec3 sort: #abs ascending] timeToRun.
>>> >>>> }
>>> >>>>  #(345 532 912)
>>> >>>>
>>> >>>> =============== Diff against Collections-nice.581 ===============
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + SortFunction subclass: #ChainedSortFunction
>>> >>>> +       instanceVariableNames: 'next'
>>> >>>> +       classVariableNames: ''
>>> >>>> +       poolDictionaries: ''
>>> >>>> +       category: 'Collections-Support'!
>>> >>>> +
>>> >>>> + !ChainedSortFunction commentStamp: 'nice 3/13/2014 22:25' prior: 0!
>>> >>>> + I add to my parent the idea of a "next" function to use when two
>>> >>>> objects are equal by my particular collator.
>>> >>>> +
>>> >>>> + Usage
>>> >>>> +
>>> >>>> + SortFunctions can be chained together in primary, secondary,
>>> >>>> tertiary, etc order using the comma method. Consider a sequence of
>>> >>>> customer objects, where each customer object responds to the messages
>>> >>>> firstName, lastName, and age. If we want to sort them lastName first,
>>> >>>> then firstName, and finally oldest first, we would use an expression
>>> >>>> like:
>>> >>>> +
>>> >>>> + customers sort: #lastName ascending, #firstName ascending, #age
>>> >>>> descending
>>> >>>> +
>>> >>>> + As noted in my super's comment, unary symbols or single arg blocks
>>> >>>> can be used. One can omit the the ascending methods on arguments (not
>>> >>>> the receiver), it will default blocks or symbols to be ascending if
>>> >>>> none is specified. In other words, the above expression could be
>>> >>>> simplified slightly as
>>> >>>> +
>>> >>>> + customers sort: #lastName ascending, #firstName, #age descending
>>> >>>> +
>>> >>>> + (note the missing ascending on the #firstName argument)
>>> >>>> +
>>> >>>> + Instance Variables
>>> >>>> +       next    <SortFunction>  the next SortFunction to evaluate in
>>> >>>> the event my evaluator results in equal values.
>>> >>>> +
>>> >>>> + !
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: ChainedSortFunction>>, (in category 'converting')
>>> >>>> -----
>>> >>>> + , aSortFunction
>>> >>>> +
>>> >>>> +       self next: next , aSortFunction!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: ChainedSortFunction>>next: (in category
>>> >>>> 'initialize-release') -----
>>> >>>> + next: anObject
>>> >>>> +
>>> >>>> +       next := anObject!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: ChainedSortFunction>>value:value: (in category
>>> >>>> 'evaluating') -----
>>> >>>> + value: anObject value: bObject
>>> >>>> +       "Refinement of the parent behavior. If the result of my
>>> >>>> collator is 0, then pass on to the next variable to work it out."
>>> >>>> +
>>> >>>> +       | result |
>>> >>>> +       result := (collator value: anObject value: bObject) *
>>> >>>> direction.
>>> >>>> +       ^result isZero
>>> >>>> +               ifTrue: [next value: anObject value: bObject]
>>> >>>> +               ifFalse: [result < 0]!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + Object subclass: #SortFunction
>>> >>>> +       instanceVariableNames: 'direction collator'
>>> >>>> +       classVariableNames: ''
>>> >>>> +       poolDictionaries: ''
>>> >>>> +       category: 'Collections-Support'!
>>> >>>> +
>>> >>>> + !SortFunction commentStamp: 'nice 3/13/2014 22:24' prior: 0!
>>> >>>> + I am intended to be used in place of two arg sort blocks.
>>> >>>> +
>>> >>>> + Usage
>>> >>>> +
>>> >>>> + In the following example, an ascending SortFunction is created
>>> >>>> based
>>> >>>> on the result of the #first message send to each object.
>>> >>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #first ascending.
>>> >>>> +
>>> >>>> + To sort by the #last element, but descending, the following would
>>> >>>> be
>>> >>>> used:
>>> >>>> + #(#(1 2) #(2 3) #(0 0)) sorted: #last descending.
>>> >>>> +
>>> >>>> + One can use blocks as well. The following sorts in descending
>>> >>>> order,
>>> >>>> the sub elements based on the sum of their values.
>>> >>>> + | sumBlock |
>>> >>>> + sumBlock := [:sequence | sequence inject: 0 into: [:sum :each | sum
>>> >>>> + each]].
>>> >>>> + #(#(1 2) #(2 3) #(0 0)) sorted: sumBlock descending.
>>> >>>> +
>>> >>>> + One can even use 2 arg blocks, for those cases where the function
>>> >>>> isn't expressible with objects that respond to < and =. The only
>>> >>>> catch, is that such a function has to return not true and false, but
>>> >>>> instead a collation order, values of -1 (for before), 0 (the same) or
>>> >>>> 1 (to follow). For example:
>>> >>>> +
>>> >>>> + | oddBlock |
>>> >>>> + oddBlock :=
>>> >>>> +               [:a :b |
>>> >>>> +               a odd = b odd ifTrue: [0] ifFalse: [a odd ifTrue:
>>> >>>> [-1]
>>> >>>> ifFalse: [1]]].
>>> >>>> + #(1 5 1 3 2 7 9 4 6) asSortedCollection: oddBlock descending
>>> >>>> +
>>> >>>> + Instance Variables
>>> >>>> +       collator        <BlockClosure>  This is the collation
>>> >>>> function
>>> >>>> that must return a -1, 0, or 1. It is usually composed by an
>>> >>>> initialization method such as sendMessage: or monadicBlock:, but may
>>> >>>> be set directly.
>>> >>>> +       direction       <SmallInteger>  1 for ascending, -1 for
>>> >>>> descending
>>> >>>> +
>>> >>>> + !
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction class>>ascend (in category 'instance
>>> >>>> creation') -----
>>> >>>> + ascend
>>> >>>> +
>>> >>>> +       ^self new ascend!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction class>>descend (in category 'instance
>>> >>>> creation') -----
>>> >>>> + descend
>>> >>>> +
>>> >>>> +       ^self new descend!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>, (in category 'converting') -----
>>> >>>> + , aSortFunction
>>> >>>> +       "Return a new SortFunction which is the concatenation of
>>> >>>> aSortFunction to me, I will be the primary sort, but if I compare
>>> >>>> equal, I will defer to the argument."
>>> >>>> +
>>> >>>> +       ^(ChainedSortFunction new)
>>> >>>> +               collator: collator;
>>> >>>> +               direction: direction;
>>> >>>> +               next: aSortFunction asSortFunction!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>asSortFunction (in category
>>> >>>> 'converting') -----
>>> >>>> + asSortFunction
>>> >>>> +
>>> >>>> +       ^self!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>ascend (in category
>>> >>>> 'initailize-release') -----
>>> >>>> + ascend
>>> >>>> +
>>> >>>> +       direction := 1!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>collator: (in category
>>> >>>> 'initailize-release') -----
>>> >>>> + collator: a2ArgBlock
>>> >>>> +       "a2ArgBlock MUST return the collation order, -1, 0, or 1"
>>> >>>> +
>>> >>>> +       collator := a2ArgBlock!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>descend (in category
>>> >>>> 'initailize-release') -----
>>> >>>> + descend
>>> >>>> +
>>> >>>> +       direction := -1!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>direction: (in category
>>> >>>> 'initailize-release') -----
>>> >>>> + direction: anInteger
>>> >>>> +
>>> >>>> +       direction := anInteger!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>isAscending (in category 'testing')
>>> >>>> -----
>>> >>>> + isAscending
>>> >>>> +
>>> >>>> +       ^direction = 1!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>isDescending (in category 'testing')
>>> >>>> -----
>>> >>>> + isDescending
>>> >>>> +
>>> >>>> +       ^direction = -1!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>monadicBlock: (in category
>>> >>>> 'initailize-release') -----
>>> >>>> + monadicBlock: aSingleArgBlock
>>> >>>> +       "Initialze the receiver's collation block to compare the
>>> >>>> results of evaluating aSingleArgBlock with each argument, and then
>>> >>>> collate the return values with the <=> method."
>>> >>>> +
>>> >>>> +       collator := [:a :b | (aSingleArgBlock value: a) <=>
>>> >>>> (aSingleArgBlock value: b)]!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>sendMessage: (in category
>>> >>>> 'initailize-release') -----
>>> >>>> + sendMessage: aUnarySymbol
>>> >>>> +       "Initialze the receiver's collation block to compare the
>>> >>>> results of sending aUnarySymbol to each argument, and then collate
>>> >>>> them with the <=> method."
>>> >>>> +
>>> >>>> +       collator := [:a :b | (a perform: aUnarySymbol) <=> (b
>>> >>>> perform:
>>> >>>> aUnarySymbol)]!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>toggleDirection (in category
>>> >>>> 'converting') -----
>>> >>>> + toggleDirection
>>> >>>> +       "Invert my current direction, if I'm currently ascending,
>>> >>>> this
>>> >>>> will cause me to be descending now, and vice-versa."
>>> >>>> +
>>> >>>> +       direction := direction * -1!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: SortFunction>>value:value: (in category 'evaluating')
>>> >>>> -----
>>> >>>> + value: anObject value: bObject
>>> >>>> +       "Masquerade as a two argument block, used by many of the
>>> >>>> sorting APIs, by returning whether anObject should be placed before
>>> >>>> bObject or not."
>>> >>>> +
>>> >>>> +       | result |
>>> >>>> +       result := (collator value: anObject value: bObject) *
>>> >>>> direction.
>>> >>>> +       ^result <= 0!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: String>><=> (in category 'sorting') -----
>>> >>>> + <=> aCharacterArray
>>> >>>> +       "Return a collation order of -1, 0, or 1, indicating whether
>>> >>>> I
>>> >>>> should be collated before the receiver, am equal, or after.
>>> >>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>> >>>> +
>>> >>>> +       ^self = aCharacterArray
>>> >>>> +               ifTrue: [ 0 ]
>>> >>>> +               ifFalse: [self < aCharacterArray
>>> >>>> +                       ifTrue: [ -1 ]
>>> >>>> +                       ifFalse: [ 1 ]]!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: Symbol>>asSortFunction (in category 'sorting') -----
>>> >>>> + asSortFunction
>>> >>>> +
>>> >>>> +       ^self ascending!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: Symbol>>ascending (in category 'sorting') -----
>>> >>>> + ascending
>>> >>>> +       "Return a SortFunction around the receiver, where the
>>> >>>> receiver
>>> >>>> will be used as a unary message to send to both a and b during
>>> >>>> sorting, and then the result of said send will be collated in
>>> >>>> ascending order using the <=> method."
>>> >>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size asscending"
>>> >>>> +
>>> >>>> +       ^SortFunction ascend sendMessage: self!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: Symbol>>descending (in category 'sorting') -----
>>> >>>> + descending
>>> >>>> +       "Return a SortFunction around the receiver, where the
>>> >>>> receiver
>>> >>>> will be used as a unary message to send to both a and b during
>>> >>>> sorting, and then the result of said send will be collated in
>>> >>>> descending order using the <=> method."
>>> >>>> +       "Example: #('abc'  'de' 'fghi') sorted: #size descending"
>>> >>>> +
>>> >>>> +       ^SortFunction descend sendMessage: self!
>>> >>>>
>>> >>>> Item was added:
>>> >>>> + ----- Method: Text>><=> (in category 'sorting') -----
>>> >>>> + <=> aCharacterArray
>>> >>>> +       "Return a collation order of -1, 0, or 1, indicating whether
>>> >>>> I
>>> >>>> should be collated before the receiver, am equal, or after.
>>> >>>> +       See also:  http://en.wikipedia.org/wiki/Spaceship_operator"
>>> >>>> +
>>> >>>> +       ^string = aCharacterArray
>>> >>>> +               ifTrue: [ 0 ]
>>> >>>> +               ifFalse: [string < aCharacterArray asString
>>> >>>> +                       ifTrue: [ -1 ]
>>> >>>> +                       ifFalse: [ 1 ]]!
>>> >>>>
>>> >>>>
>>> >>>
>>> >>
>>> >
>>>
>>>
>>>
>>
>>
>>
>>
>

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Chris Muller-3
On Mon, Sep 22, 2014 at 3:38 PM, Frank Shearar <[hidden email]> wrote:

> On 22 September 2014 20:10, Chris Muller <[hidden email]> wrote:
>> On Mon, Sep 22, 2014 at 12:10 PM, Nicolas Cellier
>> <[hidden email]> wrote:
>>>
>>>
>>> 2014-09-22 17:51 GMT+02:00 David T. Lewis <[hidden email]>:
>>>>
>>>> We need positive confirmation that the code derived from that public
>>>> repository was licensed MIT, or a statement from the original author that
>>>> is now being released under MIT. I'm sure that the original author will be
>>>> happy to do that, but we do need a clear statement of license before we
>>>> can allow the code to be added to trunk.
>>>>
>>>> Dave
>>>>
>>>
>>> Sorry  for the late reply.
>>> I mistakenly assumed the license problem was solved, but after verification,
>>> there is no mention of a license on the blog nor on the cincom public store
>>> repository.
>>> I have sent private e-mail to Travis 2 days ago, but haven't received an
>>> answer yet.
>>> I don't know him personnally, so I would welcome any one having a more
>>> direct channel to contact him.
>>> Without a clear answer we'll have to retract these commits.
>>>
>>> Unless you prefer a preventive retractation right now?
>>
>> Travis hasn't been involved with Smalltalk for over 2 years now
>>
>>   http://objology.blogspot.com/2012/05/stepping-out-of-balloon.html
>>
>> so its very possible you'll get no reply.
>>
>> Note that Cincom reprinted his blog post under their own domain,
>> including the part that encourages to "port it to your own flavor of
>> Smalltalk".
>>
>>    http://www.cincomsmalltalk.com/main/2010/12/tag-sortfunctions/
>>
>> If they would object to this being ported, why would they reprint it
>> and not "clarify" that point?
>>
>> To me, it seems clear that this was meant for adoption by the
>> Smalltalk community at large.  It would be a shame to subvert that by
>> some (mis)perceived legal technicality.  We are not lawyers, but in
>> case we get no reply, if we're that concerned about a lawsuit (waged
>> by Cincom?), the SFC does have a legal resource whom we could ask to
>> evaluate it.
>
> "Port" means "translate into your Smalltalk". It does not have
> anything at all to do with licences. I didn't see Travis saying "this
> is public domain",

He didn't?  Then what does this mean?

  "And you can lobby Cincom to include it in VisualWorks. Or port it
to your own flavor of Smalltalk."

Followed by:

  "I did my best to write it in such a way that it would work in
Squeak or Gemstone, or whatever."

Geez its even better than MIT because he explicitly mentioned Squeak
and Gemstone, followed by "whatever".

Taken as a whole, the meaning is clear.  You're hung up because it
wasn't drafted by a lawyer, approved by parliament, and stamped by the
queen..?

> I didn't see a licence declaration, so... what?
> Hopefully Travis will come back and say "hey, you know, I'm a
> signed-up contributor to Squeak, why are you even asking?" and then we
> can all be happy.
>
> Until then, we _do not know_. 'It was ported from a "public
> repository" into trunk.  Everything posted to trunk is MIT.' is not an
> argument.

I was simply trying to double-answer your ambiguous question, which
didn't specify whether you were asking about the license of the
original work or the derivative work.  The derivative work is
MIT-licensed by virtue of it being part of Squeak.

> And the second statement's not even true. Us saying "you
> submitted it, therefore it's MIT" is bogus.

Log into source.squeak.org and click on "The Trunk" on the left side.
What do you see under "License?"

> The submitter needs to
> have signed the contributor agreement, _and have the right_ to sign
> such an agreement. Your work contract might not permit you to do such
> a thing.
>
> And I'm not suggesting ripping anything out. Let's wait for Travis to
> respond. Hopefully someone has a more direct line to him, given that
> Nicolas has waited two days already.

Respond with what?  Something like, "Yes, I really did mean what I
wrote in black and white that 'You can port it to your own flavor of
Smalltalk.'"  Or do we need him to officially "stamp it" with some
kind of "MIT" stamp?  And, if he's too busy to do your paperwork then
throw it out?

> But this is a very real problem.

No it isn't.  I think you're making a problem out of nothing.  What do
you think could happen?  A lawsuit?

Reply | Threaded
Open this post in threaded view
|

Re: The Trunk: Collections-nice.582.mcz

Frank Shearar-3
On 23 September 2014 00:04, Chris Muller <[hidden email]> wrote:

> On Mon, Sep 22, 2014 at 3:38 PM, Frank Shearar <[hidden email]> wrote:
>> On 22 September 2014 20:10, Chris Muller <[hidden email]> wrote:
>>> On Mon, Sep 22, 2014 at 12:10 PM, Nicolas Cellier
>>> <[hidden email]> wrote:
>>>>
>>>>
>>>> 2014-09-22 17:51 GMT+02:00 David T. Lewis <[hidden email]>:
>>>>>
>>>>> We need positive confirmation that the code derived from that public
>>>>> repository was licensed MIT, or a statement from the original author that
>>>>> is now being released under MIT. I'm sure that the original author will be
>>>>> happy to do that, but we do need a clear statement of license before we
>>>>> can allow the code to be added to trunk.
>>>>>
>>>>> Dave
>>>>>
>>>>
>>>> Sorry  for the late reply.
>>>> I mistakenly assumed the license problem was solved, but after verification,
>>>> there is no mention of a license on the blog nor on the cincom public store
>>>> repository.
>>>> I have sent private e-mail to Travis 2 days ago, but haven't received an
>>>> answer yet.
>>>> I don't know him personnally, so I would welcome any one having a more
>>>> direct channel to contact him.
>>>> Without a clear answer we'll have to retract these commits.
>>>>
>>>> Unless you prefer a preventive retractation right now?
>>>
>>> Travis hasn't been involved with Smalltalk for over 2 years now
>>>
>>>   http://objology.blogspot.com/2012/05/stepping-out-of-balloon.html
>>>
>>> so its very possible you'll get no reply.
>>>
>>> Note that Cincom reprinted his blog post under their own domain,
>>> including the part that encourages to "port it to your own flavor of
>>> Smalltalk".
>>>
>>>    http://www.cincomsmalltalk.com/main/2010/12/tag-sortfunctions/
>>>
>>> If they would object to this being ported, why would they reprint it
>>> and not "clarify" that point?
>>>
>>> To me, it seems clear that this was meant for adoption by the
>>> Smalltalk community at large.  It would be a shame to subvert that by
>>> some (mis)perceived legal technicality.  We are not lawyers, but in
>>> case we get no reply, if we're that concerned about a lawsuit (waged
>>> by Cincom?), the SFC does have a legal resource whom we could ask to
>>> evaluate it.
>>
>> "Port" means "translate into your Smalltalk". It does not have
>> anything at all to do with licences. I didn't see Travis saying "this
>> is public domain",
>
> He didn't?  Then what does this mean?

If he doesn't say "in the public domain" then I fail to see how we can
infer that the original code is in the public domain.

>   "And you can lobby Cincom to include it in VisualWorks. Or port it
> to your own flavor of Smalltalk."
>
> Followed by:
>
>   "I did my best to write it in such a way that it would work in
> Squeak or Gemstone, or whatever."
>
> Geez its even better than MIT because he explicitly mentioned Squeak
> and Gemstone, followed by "whatever".
>
> Taken as a whole, the meaning is clear.  You're hung up because it
> wasn't drafted by a lawyer, approved by parliament, and stamped by the
> queen..?

I'd like clarity as to the licence.

>> I didn't see a licence declaration, so... what?
>> Hopefully Travis will come back and say "hey, you know, I'm a
>> signed-up contributor to Squeak, why are you even asking?" and then we
>> can all be happy.
>>
>> Until then, we _do not know_. 'It was ported from a "public
>> repository" into trunk.  Everything posted to trunk is MIT.' is not an
>> argument.
>
> I was simply trying to double-answer your ambiguous question, which
> didn't specify whether you were asking about the license of the
> original work or the derivative work.  The derivative work is
> MIT-licensed by virtue of it being part of Squeak.

You're saying that Nicolas' implementation is the derivative work,
right? That's fine.

>> And the second statement's not even true. Us saying "you
>> submitted it, therefore it's MIT" is bogus.
>
> Log into source.squeak.org and click on "The Trunk" on the left side.
> What do you see under "License?"

That's not at all the same thing. If you have an MIT licensed
codebase, and copy a bunch of code in there from a GPL licensed
codebase, guess what? Your codebase isn't MIT licensed anymore. (This
is not a theoretical concern. It happens to real projects.)

>> The submitter needs to
>> have signed the contributor agreement, _and have the right_ to sign
>> such an agreement. Your work contract might not permit you to do such
>> a thing.
>>
>> And I'm not suggesting ripping anything out. Let's wait for Travis to
>> respond. Hopefully someone has a more direct line to him, given that
>> Nicolas has waited two days already.
>
> Respond with what?  Something like, "Yes, I really did mean what I
> wrote in black and white that 'You can port it to your own flavor of
> Smalltalk.'"  Or do we need him to officially "stamp it" with some
> kind of "MIT" stamp?

"Hi, that code I wrote that Nicolas ported? Public domain." That would
suit me just fine.

>  And, if he's too busy to do your paperwork then
> throw it out?

Yes, to put it bluntly. I don't understand why this upsets you so
much, Chris. Yes, it's annoying boring paperwork nonsense that keeps
lawyers employed. It still needs doing.

>> But this is a very real problem.
>
> No it isn't.  I think you're making a problem out of nothing.  What do
> you think could happen?  A lawsuit?

I'd like to be able to say, with reasonable certainty, that all the
code in Trunk landed there because someone who agreed to licence their
code as MIT, did so.

Since Travis has actually signed the contributor agreement
(http://netjam.org/squeak/contributors/contributors under the initials
TAG), checking with him is (a) just plain polite and (b) should not be
a problem. (And I'd like to see his positive response in this case
because his signing the agreement doesn't mean that all Smalltalk code
he ever wrote that we can find is now suddenly MIT licensed.)

frank

12