SequenceableCollectionSorter.st
author Claus Gittinger <cg@exept.de>
Sat, 02 May 2020 21:40:13 +0200
changeset 5476 7355a4b11cb6
parent 5209 6ea076e6515d
permissions -rw-r--r--
#FEATURE by cg class: Socket class added: #newTCPclientToHost:port:domain:domainOrder:withTimeout: changed: #newTCPclientToHost:port:domain:withTimeout:

"{ Encoding: utf8 }"

"
 COPYRIGHT (c) 1996 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libbasic2' }"

"{ NameSpace: Smalltalk }"

Object subclass:#SequenceableCollectionSorter
	instanceVariableNames:'collection atSelector putSelector sizeSelector sortBlock'
	classVariableNames:'DefaultSortBlock'
	poolDictionaries:''
	category:'Collections-Support'
!

!SequenceableCollectionSorter class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1996 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    a SequenceableCollectionSorter allows for anything which responds to
    keyed at/atPut messages to be sorted just like a SequenceableCollection.
    Since the access messages can be customized, even non collections
    (or collection simulators, models etc.) can be sorted with these sorters.
    (use #atSelector: / #putSelector: and #sizeSelector: for customization).

    As with collection sorting, the sortBlock can be specified and defaults to
    a block which compares using #< messages.
"
!

examples
"
    |a sorter|

    a := #(10 5 2 17 5 99 -5).
    sorter := SequenceableCollectionSorter on:a.
    sorter sort.
    Transcript showCR:a printString
"
! !

!SequenceableCollectionSorter class methodsFor:'initialization'!

initialize
    DefaultSortBlock := [:a :b | a <= b]

    "
     SequenceableCollectionSorter initialize
    "

    "Modified: 6.2.1996 / 15:47:48 / cg"
! !

!SequenceableCollectionSorter class methodsFor:'instance creation'!

on:aCollection
    ^ self new collection:aCollection; sortBlock:DefaultSortBlock

    "Created: 6.2.1996 / 15:39:09 / cg"
    "Modified: 6.2.1996 / 16:07:36 / cg"
!

on:aCollection using:aTwoArgBlock
    ^ self new collection:aCollection; sortBlock:aTwoArgBlock

    "Created: 6.2.1996 / 15:39:32 / cg"
!

sort:aCollection
    (self new collection:aCollection; sortBlock:DefaultSortBlock) sort

    "Created: 6.2.1996 / 15:40:04 / cg"
    "Modified: 6.2.1996 / 16:07:42 / cg"
!

sort:aCollection using:aTwoArgBlock
    (self new collection:aCollection; sortBlock:aTwoArgBlock) sort

    "Created: 6.2.1996 / 15:39:58 / cg"
! !

!SequenceableCollectionSorter methodsFor:'accessing'!

atSelector:aSymbol
    "set the selector to access elements.
     If nil (the default), elements are accessed via #at:"

    atSelector := aSymbol

    "Created: 6.2.1996 / 15:35:49 / cg"
!

collection
    "return the collections being sorted."

    ^ collection

    "Created: 6.2.1996 / 15:37:02 / cg"
!

collection:aCollection
    "set the collections being sorted."

    collection := aCollection

    "Created: 6.2.1996 / 15:37:20 / cg"
!

putSelector:aSymbol
    "set the selector to access elements.
     If nil (the default), elements are accessed via #at:put:"

    putSelector := aSymbol

    "Created: 6.2.1996 / 15:36:14 / cg"
!

sizeSelector:aSymbol
    "set the selector to get the collections size.
     If nil (the default), elements are accessed via #size"

    sizeSelector := aSymbol

    "Created: 6.2.1996 / 15:36:33 / cg"
!

sortBlock
    "return the sortBlock which is used to compare two elements.
     The default sortBlock compares elements by sending the #< message."

    ^ sortBlock

    "Created: 6.2.1996 / 15:38:32 / cg"
!

sortBlock:aTwoArgBlock 
    "set the sortBlock which is used to compare two elements.
     The default sortBlock compares elements by sending the #< message."

    sortBlock := aTwoArgBlock

    "Created: 6.2.1996 / 15:38:20 / cg"
! !

!SequenceableCollectionSorter methodsFor:'sorting'!

defaultSort:inBegin to:inEnd
    "actual sort worker for sorting when the sortBlock is nil (or the default)
     and default atSelector/putSelectors are to be used.
     This will execute slightly faster, since no #perform-indirection is needed."

    |begin   "{ Class: SmallInteger }"
     end     "{ Class: SmallInteger }"
     b       "{ Class: SmallInteger }"
     e       "{ Class: SmallInteger }"
     middleElement temp1 temp2 |

    begin := inBegin.   "/ this also does a type-check
    end := inEnd.

    b := begin.
    e := end.
    middleElement := collection at:((b + e) // 2).

    [b < e] whileTrue:[
        [b < end and:[(collection at:b) <= middleElement]] whileTrue:[b := b + 1].
        [e > begin and:[middleElement <= (collection at:e)]] whileTrue:[e := e - 1].

        (b <= e) ifTrue:[
            (b == e) ifFalse:[
                temp1 := collection at:b. temp2 := collection at:e. 
                collection at:b put:temp2. collection at:e put:temp1
            ].
            b := b + 1.
            e := e - 1
        ]
    ].
    (begin < e) ifTrue:[self defaultSort:begin to:e].
    (b < end) ifTrue:[self defaultSort:b to:end]

    "Created: 6.2.1996 / 15:44:37 / cg"
    "Modified: 6.2.1996 / 18:06:23 / cg"
!

nonDefaultSort3:inBegin to:inEnd with:p
    "actual sort worker for sorting when a non default sortBlock
     with 3 args (stringSort) is used."

    |begin   "{ Class: SmallInteger }"
     end     "{ Class: SmallInteger }"
     b       "{ Class: SmallInteger }"
     e       "{ Class: SmallInteger }"
     middleElement temp1 temp2 
     atSel putSel|

    atSel := atSelector ? #at:.
    putSel := putSelector ? #at:put:.

    begin := inBegin.   "/ this also does a type-check
    end := inEnd.

    b := begin.
    e := end.
    middleElement := collection perform:atSel with:((b + e) // 2).

    [b < e] whileTrue:[
        [b < end 
        and:[sortBlock 
                value:p
                value:(collection perform:atSel with:b)
                value:middleElement]] 
        whileTrue:[b := b + 1].
        [e > begin 
        and:[sortBlock
                value:p
                value:middleElement
                value:(collection perform:atSel with:e)]] 
        whileTrue:[e := e - 1].

        (b <= e) ifTrue:[
            (b == e) ifFalse:[
                temp1 := (collection perform:atSel with:b). 
                temp2 := (collection perform:atSel with:e). 
                collection perform:putSel with:b with:temp2. 
                collection perform:putSel with:e with:temp1.
            ].
            b := b + 1.
            e := e - 1
        ]
    ].
    (begin < e) ifTrue:[self nonDefaultSort3:begin to:e with:p].
    (b < end) ifTrue:[self nonDefaultSort3:b to:end with:p]

    "Modified: / 27.10.1997 / 04:52:41 / cg"
    "Created: / 27.10.1997 / 18:54:54 / cg"
!

nonDefaultSort:inBegin to:inEnd
    "actual sort worker for sorting when a non default sortBlock
     or nonNil access selectors are used."

    |begin   "{ Class: SmallInteger }"
     end     "{ Class: SmallInteger }"
     b       "{ Class: SmallInteger }"
     e       "{ Class: SmallInteger }"
     middleElement temp1 temp2 
     atSel putSel|

    atSel := atSelector ? #at:.
    putSel := putSelector ? #at:put:.

    begin := inBegin.   "/ this also does a type-check
    end := inEnd.

    b := begin.
    e := end.
    middleElement := collection perform:atSel with:((b + e) // 2).

    [b < e] whileTrue:[
        [b < end 
        and:[sortBlock 
                value:(collection perform:atSel with:b)
                value:middleElement]] 
        whileTrue:[b := b + 1].
        [e > begin 
        and:[sortBlock
                value:middleElement
                value:(collection perform:atSel with:e)]] 
        whileTrue:[e := e - 1].

        (b <= e) ifTrue:[
            (b == e) ifFalse:[
                temp1 := (collection perform:atSel with:b). 
                temp2 := (collection perform:atSel with:e). 
                collection perform:putSel with:b with:temp2. 
                collection perform:putSel with:e with:temp1.
            ].
            b := b + 1.
            e := e - 1
        ]
    ].
    (begin < e) ifTrue:[self nonDefaultSort:begin to:e].
    (b < end) ifTrue:[self nonDefaultSort:b to:end]

    "Created: / 6.2.1996 / 16:06:46 / cg"
    "Modified: / 27.10.1997 / 04:52:41 / cg"
!

sort
    |sz|

    sizeSelector isNil ifTrue:[
        sz := collection size
    ] ifFalse:[
        sz := collection perform:sizeSelector
    ].
    self sort:1 to:sz

    "Created: 6.2.1996 / 15:41:21 / cg"
!

sort:from to:to 
    "actual sort worker for sort-messages"

    (atSelector isNil
    and:[putSelector isNil
    and:[sortBlock isNil or:[sortBlock == DefaultSortBlock]]]) ifTrue:[
        self defaultSort:from to:to 
    ] ifFalse:[
        sortBlock argumentCount == 3 ifTrue:[
            self nonDefaultSort3:from to:to with:nil "p"
        ] ifFalse:[
            self nonDefaultSort:from to:to
        ]
    ].

    "Modified: / 27.10.1997 / 18:55:48 / cg"
! !

!SequenceableCollectionSorter class methodsFor:'documentation'!

version
    ^ '$Header$'
! !


SequenceableCollectionSorter initialize!