Cons.st
author Claus Gittinger <cg@exept.de>
Sat, 02 May 2020 21:40:13 +0200
changeset 5476 7355a4b11cb6
parent 4932 571ac375933a
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) 2002 by eXept Software AG
              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 }"

SequenceableCollection subclass:#Cons
	instanceVariableNames:'car cdr'
	classVariableNames:''
	poolDictionaries:''
	category:'Collections-Linked'
!

!Cons class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2002 by eXept Software AG
              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 pair as in lisp.
        Cons car:a cdr:b

    Conses are not heavily used by Smalltalk (actually: not at all).
    Consider this a demo class.

    [author:]
        Claus Gittinger (Jun 2002)

    [see also:]

"
!

examples
"
                                                                        [exBegin]
    |p1 p2 p3|

    p3 := Cons car:3 cdr:nil.
    p2 := Cons car:2 cdr:p3.
    p1 := Cons car:1 cdr:p2.
    p1 head.
    p1 tail.
    p1 size.
    p1 do:[:each | Transcript showCR:each].
    p1 at:2
                                                                        [exEnd]
"
! !

!Cons class methodsFor:'instance creation'!

car:carArg cdr:cdrArg
    ^ self basicNew car:carArg cdr:cdrArg
!

fromArray:anArray
    |p last first|

    anArray do:[:el |
        p := self car:el cdr:nil.
        first isNil ifTrue:[
            first := p.
        ] ifFalse:[
            last cdr:p.
        ].
        last := p.
    ].
    ^ first.

    "
     Cons fromArray:#(1 2 3 4)   
     Cons fromArray:#()    
     Cons fromArray:#(1)    
     Cons fromArray:(1 to:10000)    
    "
!

makeList:size
    |first prev this|

    size == 0 ifTrue:[^ nil].

    first := prev := self car:nil cdr:nil.
    2 to:size do:[:n |
        this := self car:nil cdr:nil.
        prev cdr:this.
        prev := this
    ].
    ^ first

    "
     (self makeList:0) size    
     (self makeList:1) size    
     (self makeList:100) size
     (self makeList:1000) size 
     (self makeList:10000) size 
    "

    "Created: / 28-04-2011 / 00:37:11 / cg"
    "Modified: / 29-04-2011 / 10:38:11 / cg"
! !

!Cons class methodsFor:'sExpressions'!

readLispAtomFrom:aStream
    |chars n|

    aStream skipSeparators.
    aStream atEnd ifTrue:[^ nil].

    chars := aStream upToMatching:[:ch | ch isSeparator or:[ch = $( or:[ch = $)]]].
    (n := Number readFromString:chars onError:nil) notNil ifTrue:[
        "/ smalltalk allows +n; scheme does not
        (chars startsWith:'+') ifFalse:[
            ^ n
        ].
    ].
    ^ chars asSymbol

    "
     self readLispFrom:('(cons 1 2)' readStream).
     self readLispFrom:('(cons 1+ 2)' readStream).  
     self readLispFrom:('(cons +1 2)' readStream).
    "

    "Created: / 08-08-2010 / 17:15:18 / cg"
!

readLispFrom:aStream
    [
        aStream skipSeparators.
        aStream atEnd ifTrue:[^ nil].

        aStream peek ==$; ifFalse:[
            aStream peek ==$( ifTrue:[
                ^ self readLispListFrom:aStream
            ].
            ^ self readLispAtomFrom:aStream
        ].
        "/ EOL comment
        aStream skipLine.
    ] loop
    
    "
     self readLispFrom:('(cons 1 2)' readStream).
    "

    "Created: / 08-08-2010 / 17:07:49 / cg"
!

readLispListFrom:aStream
    |first this prev element|

    aStream next.   "/ the leading '('
    [
        aStream skipSeparators.
        aStream peek ~= $)
    ] whileTrue:[
        element := self readLispFrom:aStream.
        this := self car:element cdr:nil.
        prev isNil ifTrue:[
            first := this
        ] ifFalse:[
            prev cdr:this.
        ].
        prev := this.
    ].

    aStream next.   "/ the trailing ')'
    ^ first.

    "
     self readLispFrom:('(cons 1 2)' readStream).
    "

    "Modified: / 08-08-2010 / 17:15:54 / cg"
! !

!Cons methodsFor:'accessing'!

at:n
    "for collection compatibility:
     a slow indexed accessor"

    ^ (self nth:n)

    "
     (Cons fromArray:#(1))       at:1     
     (Cons fromArray:#(1 2 3 4)) at:1 
     (Cons fromArray:#(1 2 3 4)) at:3  
     (Cons fromArray:#(1 2 3 4)) at:4  
     (Cons fromArray:#(1 2 3 4)) at:5  
    "
!

at:n put:newValue
    "destructive: 
     for collection compatibility: a slow indexed accessor"

    (self nthPair:n) car:newValue.
    ^ newValue.

    "
     |l|

     l := Cons fromArray:#(1 2 3 4).
     l at:1 put:'one'.
     l at:3 put:'three'.
     l       
    "
!

first
    "return the head, first or car - whatever you wonna call it"

    ^ car

    "Modified: / 08-08-2010 / 17:04:23 / cg"
!

head
    "return the head, first or car - whatever you wonna call it"

    ^ car

    "Modified: / 08-08-2010 / 17:04:20 / cg"
!

last
    "for lispers:
     return the last element of a list"

    |p rest|

    p := self.
    [(rest := p cdr) notNil] whileTrue:[
        p := rest
    ].
    ^ p car

    "
     (Cons fromArray:#(1))       last     
     (Cons fromArray:#(1 2 3 4)) last    
    "
!

nth:n
    "for lispers:
     return the nth element of a list"

    ^ (self nthPair:n) car

    "
     (Cons fromArray:#(1))       nth:1     
     (Cons fromArray:#(1 2 3 4)) nth:1 
     (Cons fromArray:#(1 2 3 4)) nth:3  
     (Cons fromArray:#(1 2 3 4)) nth:4  
     (Cons fromArray:#(1 2 3 4)) nth:5  
     (Cons fromArray:#( ))       nth:1  -> error    
    "
!

rest
    "return the tail, rest or cdr - whatever you wonna call it"

    ^ cdr

    "Modified: / 08-08-2010 / 17:04:48 / cg"
!

reversed
    "for lispers:
     return a new list with the cars in reverse order"

"/ for now, tail recursion is not yet optimized by the st/x jitter...
"/
"/    |rev|
"/
"/    rev := [:lst :acc |
"/                lst isNil ifTrue:[
"/                    acc
"/                ] ifFalse:[
"/                    rev value:(lst tail)
"/                        value:(Cons car:(lst head) cdr:acc)
"/                ]
"/           ].
"/    ^ rev value:self value:nil

    | lst acc|

    lst := self.
    acc := nil.

    [
        |nLst nAcc|

        lst isNil ifTrue:[ ^ acc].

        nLst := lst tail.
        nAcc := Cons car:(lst head) cdr:acc.
        lst := nLst.
        acc := nAcc.
   ] loop

    "
     (Cons fromArray:#(1))       reversed     
     (Cons fromArray:#(1 2))     reversed     
     (Cons fromArray:#(1 2 3 4)) reversed    
     (Cons fromArray:(1 to:10000)) reversed    
    "
!

tail
    "return the tail, rest or cdr - whatever you wonna call it"

    ^ cdr

    "Modified: / 08-08-2010 / 17:04:59 / cg"
! !

!Cons methodsFor:'accessing-basic'!

cadddr
    "return the fourth element"

    ^ cdr cdr cdr car

    "Created: / 08-08-2010 / 17:29:40 / cg"
!

caddr
    "return the third element"

    ^ cdr cdr car

    "Created: / 08-08-2010 / 17:29:32 / cg"
!

cadr
    "return the second element"

    ^ cdr car

    "Created: / 08-08-2010 / 17:29:25 / cg"
!

car
    "return the head, first or car - whatever you wonna call it"

    ^ car
!

car:something
    "set the head, first or car - whatever you wonna call it"

    car := something.
!

car:carArg cdr:cdrArg 
    "set both car and cdr"

    car := carArg.
    cdr := cdrArg.
!

cddr
    "return the rest after the second element"

    ^ cdr cdr

    "Created: / 08-08-2010 / 17:47:11 / cg"
!

cdr
    "return the tail, second or cdr - whatever you wonna call it"

    ^ cdr
!

cdr:something
    "set the tail, second or cdr - whatever you wonna call it"

    cdr := something.
!

first:carArg rest:cdrArg 
    "set both car and cdr"

    car := carArg.
    cdr := cdrArg.
!

head:carArg tail:cdrArg 
    "set both car and cdr"

    car := carArg.
    cdr := cdrArg.
!

nthPair:n
    "a helper:
     return the nth pair of a list"

    |cnt p|

    cnt := n.
    p := self.
    [
        cnt := cnt - 1.
        cnt == 0 ifTrue:[^ p].

        p := p cdr.
        p isNil ifTrue:[
            self error:'no such element' mayProceed:true.
            ^ nil
        ].
    ] loop.
! !

!Cons methodsFor:'comparing'!

= aCons
    ^ aCons class == self class
    and:[ car = aCons car
    and:[ cdr = aCons cdr ]]
!

hash
    ^ car hash bitXor: cdr hash
! !

!Cons methodsFor:'enumerating'!

do:aBlock
    "evaluate aBlock for each car"

    |ptr|

    aBlock value:car.
    ptr := cdr.
    [ ptr notNil ] whileTrue:[
        aBlock value:ptr car.
        ptr := ptr cdr.
    ].

    "Modified: / 08-08-2010 / 17:33:44 / cg"
! !

!Cons methodsFor:'list processing'!

append:aCons
    "for lispers:
     append the arg. Return a new list, where the 2nd part is shared.
     Destructive: the receiver's last cdr is modified."

    |p rest|

    p := self.
    [(rest := p cdr) notNil] whileTrue:[
        p := rest
    ].
    p cdr:aCons.
    ^ self

    "
     (Cons fromArray:#(1 2 3 4)) 
        append:(Cons fromArray:#(5 6 7 8)) 
    "

    "sharing demonstrated:

     |a b ab|

     a := Cons fromArray:#(1 2 3 4).
     b := Cons fromArray:#(5 6 7 8).
     ab := a append:b.
     b car:'five'.
     ab      
    "

    "destruction demonstrated:

     |a b ab|

     a := Cons fromArray:#(1 2 3 4).
     b := Cons fromArray:#(5 6 7 8).
     ab := a append:b.
     a  
    "
!

take:nTaken
    "for lispers:
     take n elements from the list; return a new list"

    |nRemain l rslt lastCons cons|

    nTaken > 0 ifTrue:[
        "/ avoiding recursion here...
        "/ instead of:
        "/        ^ Cons car:(self car) cdr:(self cdr take:nTaken-1)
        "/ we do:
        nRemain := nTaken.
        l := self.
        rslt := lastCons := Cons car:(l car) cdr:nil.
        [nRemain > 1] whileTrue:[
            l := l cdr.
            cons := Cons car:(l car) cdr:nil.    
            lastCons cdr:cons.
            lastCons := cons.
            nRemain := nRemain - 1.
        ].
        ^ rslt.
    ].
    ^ nil

    "
     (Cons fromArray:#(1 2 3 4)) take:3  
     (Cons fromArray:#(1)) take:0  
     (Cons fromArray:#()) take:3  
     (Cons fromArray:(1 to: 1000)) take:999  

     |gen allNumbers|
     gen := [:n | LazyCons car:n cdr:[ gen value:n+1 ]].
     allNumbers := gen value:1. 
     allNumbers take:10
    "

    "Modified (comment): / 27-09-2011 / 11:31:38 / cg"
! !

!Cons methodsFor:'printing'!

displayString
    ^ self printString
!

printOn:aStream
    thisContext isRecursive ifTrue:[ 
        'Cons [error]: printOn: of self referencing collection.' errorPrintCR.
        aStream nextPutAll:'#("recursive")'.
        ^ self.
    ].

    aStream nextPutAll:'('.
    self printRestOn:aStream.

    "Modified: / 18-05-2010 / 10:25:49 / cg"
    "Modified: / 02-04-2019 / 23:59:15 / Claus Gittinger"
!

printRestOn:aStream
    thisContext isRecursive ifTrue:[ 
        'Cons [error]: printOn: of self referencing collection.' errorPrintCR.
        aStream nextPutAll:'#("recursive")'.
        ^ self.
    ].

    car printOn:aStream.
    cdr isNil ifTrue:[
        aStream nextPutAll:')'.
        ^ self.
    ].
    (cdr isLazyValue not and:[ cdr isCons ]) ifTrue:[
        aStream nextPutAll:' '.
        cdr printRestOn:aStream.
        ^ self.
    ].
    
    aStream nextPutAll:' . '.
    cdr printOn:aStream.
    aStream nextPutAll:')'.

    "Created: / 02-04-2019 / 23:55:05 / Claus Gittinger"
! !

!Cons methodsFor:'queries'!

beginAndSizeOfCycle
    "return the begin and size of a cycle, if the list contains one.
     Nil otherwise.
     Floyd's cycle-finding algorithm"

    |t h i loopStartIndex loopSize|

    t := self cdr. t isNil ifTrue:[^ nil].
    h := t cdr. h isNil ifTrue:[^ nil].

    [t ~~ h] whileTrue:[
        t := t cdr.
        h := h cdr. h isNil ifTrue:[^ nil].
        h := h cdr. h isNil ifTrue:[^ nil].
    ].

    "/ both h and t are now inside the cycle,
    "/ equidistant from the start of the cycle
    t := self.
    i := 1.
    [t ~~ h] whileTrue:[
        t := t cdr.
        h := h cdr.
        i := i + 1.
    ].
    loopStartIndex := i.

    loopSize := 1.
    h := t cdr.
    [t ~~ h] whileTrue:[
        h := h cdr.
        i := i + 1.
        loopSize := loopSize + 1.
    ].
    
    ^ { loopStartIndex. loopSize }

    "
     |n1 n2 n3 n4 n5|

     n1 := Cons new car:1.
     n2 := Cons new car:2.
     n3 := Cons new car:3.
     n4 := Cons new car:4.
     n5 := Cons new car:5.
     n1 cdr: n2.
     n2 cdr: n3.
     n3 cdr: n4.
     n4 cdr: n5.
     n1 beginAndSizeOfCycle.      
     n5 cdr: n3.    
     n1 beginAndSizeOfCycle.             
    "

    "Created: / 27-07-2012 / 00:00:36 / cg"
!

isCyclic
    "true if the list contains a cycle"

    ^ self beginAndSizeOfCycle notNil

    "
     |n1 n2 n3 n4 n5|

     n1 := Cons new car:1.
     n2 := Cons new car:2.
     n3 := Cons new car:3.
     n4 := Cons new car:4.
     n5 := Cons new car:5.
     n1 cdr: n2.
     n2 cdr: n3.
     n3 cdr: n4.
     n4 cdr: n5.
     n1 isCyclic.
     n5 cdr: n3.    
     n1 isCyclic.   
    "

    "Created: / 26-07-2012 / 23:32:52 / cg"
!

size
    "the list's length"

    |len p rest|

    len := 1.
    p := self.

    [
        p isLazyValue not
        and:[ (rest := p cdr) isCons ]
    ] whileTrue:[
        len := len + 1.
        p := rest
    ].
    ^ len

    "
     (Cons fromArray:#(1)) size       
     (Cons fromArray:#(1 2 3 4)) size    
     (Cons car:1 cdr:2) size          --> error (degenerated list)
    "

    "Modified: / 25-10-2017 / 12:43:38 / cg"
! !

!Cons methodsFor:'streaming'!

readStream
    ^ ConsStream on:self.
! !

!Cons methodsFor:'testing'!

isCons
    ^ true
!

isLazy
    ^ false
! !

!Cons class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !