1

I am trying to create a class with string trimming functions:

Object subclass: Trimmer [
    trimleading: str [ |ch ret|
        ch := (str first: 1).           "get first character"
        ret := str.                     "make a copy of sent string"
        [ch = ' '] whileTrue: [         "while first char is space"
            ret := (ret copyFrom: 2).   "copy from 2nd char"
            ch := ret first: 1.         "again test first char"
            ].
        ^ret                            "return value is modified string"
        ].
    trim: str [ |ret|
        ret := str. 
        ret := (trimleading value: ret).           "trim leading spaces"
        ret := (trimleading value: (ret reverse)). "reverse string and repeat trim leading"
        ^(ret reverse)                             "return reverse string"
        ]
].

oristr := '        this is a test  '
('ORI..>>',oristr,'<<') displayNl.
('FINAL>>',((Trimmer new) trim: oristr),'<<') displayNl.

However, it is not running and giving following error:

$ gst trimstring_class.st

trimstring_class.st:10: invalid class body element
trimstring_class.st:17: expected expression

Where is the problem and how can this be solved?

If I remove the . after trimleading method block, as in following code:

Object subclass: Trimmer [
    trimleading: str [ |ch ret flag|
        ret := str.                     "make a copy of sent string"
        flag := true.
        [flag] whileTrue: [         "while first char is space"
            ch := ret first: 1.         "again test first char"
            ch = ' '
            ifTrue: [ ret := (ret copyFrom: 2 to: ret size)]    "copy from 2nd char"
            ifFalse: [flag := false] 
            ].
        ^ret                                "value is modified string"
        ]     "<<<<<<< PERIOD/DOT REMOVED FROM HERE."
    trim: str [ |ret|
        ret := str. 
        ret := (trimleading value: ret).           "trim leading spaces"
        ret := (trimleading value: (ret reverse)). "reverse string and repeat trim leading"
        ^(ret reverse)                      "return reverse string"
        ]
].

Then the code starts to run but stops with following error:

$ gst trimstring_class.st 
trimstring_class.st:15: undefined variable trimleading referenced
ORI..>>        this is a test  <<
Object: Trimmer new "<0x7f1c787b4750>" error: did not understand #trim:
MessageNotUnderstood(Exception)>>signal (ExcHandling.st:254)
Trimmer(Object)>>doesNotUnderstand: #trim: (SysExcept.st:1448)
UndefinedObject>>executeStatements (trimstring_class.st:23)

Why trimleading method is undefined now and why gnu-smalltalk did not understand #trim:?

rnso
  • 23,686
  • 25
  • 112
  • 234

1 Answers1

2

Usually it is wise for a such common use case to check if such functionallity was already implemented. You can take an inspiration from it for your code (you will improve as Smalltalk programmer too). Take a look at trimBlanksFrom: from sports.st:

 SpStringUtilities class >> trimBlanksFrom: aString [
    "^a String
     I return a copy of aString with all leading and trailing blanks removed."

    <category: 'services'>
    | first last |
    first := 1.
    last := aString size.
    [last > 0 and: [(aString at: last) isSeparator]] 
        whileTrue: [last := last - 1].
    ^last == 0 
        ifTrue: [String new]
        ifFalse: [
            [first < last and: [(aString at: first) isSeparator]] 
                whileTrue: [first := first + 1].
            aString copyFrom: first to: last
       ]
]

If you want to trim only leading spaces you can just take the second part, where it is trimming the leading spaces.

EDIT The OP own code a fixes applied:

Object subclass: Trimmer [
    trimleading: str [ |ch ret flag|
        ret := str.                     "make a copy of sent string"
        flag := true.
        [flag] whileTrue: [         "while first char is space"
            ch := ret first: 1.         "again test first char"
            ch = ' '
            ifTrue: [ ret := (ret copyFrom: 2 to: ret size) ]    "copy from 2nd char"
            ifFalse:  [flag := false ] 
            ].
        ^ret                                "value is modified string"
        ]     "<<<<<<< PERIOD/DOT REMOVED FROM HERE."
    trim: str [ |ret|
        ret := str. 
        ret := self trimleading: (ret copy).           "trim leading spaces"
        ret := self trimleading: (ret copy reverse). "reverse string and repeat trim leading"
        ^ (ret reverse)                      "return reverse string"
        ]
].

oristr := '        this is a test  '
('ORI..>>',oristr,'<<') displayNl.
('FINAL>>',((Trimmer new) trim: oristr),'<<') displayNl.

The were some mistakes that needed to be fixed. If you want to address a selector #trimleading: you have to use self keyword which searches the local class (for own classes or inherited). Next you should not change a variable that you are assigning to you should use a #copy otherwise strange result can be expected.

tukan
  • 17,050
  • 1
  • 20
  • 48
  • I am interested in finding error in my code. What is the error in my code and how can it be corrected? – rnso May 14 '19 at 08:58
  • Yes, it works. `self trimleading` needed to be used. I have already copied `str` to `ret`. Code works without using `(ret copy)` everytime. Is it really needed here? – rnso May 14 '19 at 11:17
  • @rnso For your specific code it is not needed as you copied it yourself, but you should get acquainted, that was the reason why I used it, to `copy` (usually a shallow copy) and `deepCopy`. It is easier to read and understand when you use it. – tukan May 14 '19 at 13:54