1

I am using Smalltalk to type in Transcript window a 1-9 multiplication table .

Here is my code:

1 to: 9 do: [:i|
    1 to: i do: [:j|
        Transcript show: j.
        Transcript show: ' * '.
        Transcript show: i.
        Transcript show: ' = '.
        Transcript show: j * i.
        Transcript show: '  '.
        ].
    Transcript show: ' '; cr.
    ].

As can be seen, the above code, although working fine, looks far from beautiful and concise.

I had hoped to write something like :

Transcript show: j '*' i '=' j * i.

Unfortunately, they are wrong. I remember C has a very nice way to handle that issue of mine.

Like, printf("%d * %d = %d ", j, i, j * i);

Is there a more elegant way to make Smalltalk codes elegant in this situation ?


Peter solves this problem. Thanks.

More to ask:

How to display "Escape character" in Smalltalk.

I know in C, printf("%d * %d = %d\n", j, i, j * i); seems fine.

But in Smalltalk, Transcript show: ('{1} * {2} = {3}cr.' format: {i. j. i * j}). is not OKAY.

How can I solve that?

Community
  • 1
  • 1

3 Answers3

3

I would suggest two changes:

  1. Have the code work with a generic Stream and use it with the Transcript

    dumpTableOn: aStream
       <your code here>
    

    And then evaluate

    self dumpTableOn: Transcript
    
  2. Split your code in two methods

    dumpTableOn: aStream
      1 to: 9 do: [:i | 
        1 to: i do: [:j |
            self dump: i times: j on: aStream.
            aStream space].
        aStream cr]
    
    
    dump: i times: j on: aStream
      aStream
        nextPutAll: j asString;
        nextPutAll: ' * ';
        nextPutAll: i asString;
        nextPutAll: ' = ';
        nextPutAll: (j * i) asString
    

Here is a less verbose version of the method above

    dump: i times: j on: aStream
      aStream
        print: j;
        print: ' * ';
        print: i;
        print: ' = ';
        print: j * i

Note that #print: doesn't require us to convert the argument to a String first.

Note also that the cascade doesn't create intermediate Strings, while the expression

j asString, '*', i asString, '=', (j*i) asString

creates 4 intermediate strings (one for every #,). Not a big deal, except that we wouldn't be taking full advantage of the Stream protocol, whose whole point is to free the client from the need of concatenating things on its side.

Leandro Caniglia
  • 14,495
  • 4
  • 29
  • 51
  • Note also that this suggests an important change: from a "loose" script in a workspace, to methods in a class. That's a relevant step in Smalltalk, because creating a class to attack a problem is the first step in the exploration of a domain and capture of knowledge – Carlos E. Ferro Nov 06 '17 at 12:35
3

String expansion

Like, printf("%d * %d = %d ", j, i, j * i);

There are some options for formatting, but nothing comparable to printf (afaik)

'<1p> * <2p> = <3p>' expandMacrosWith: 2 with: 3 with: 2 * 3.
"same as the following:"
'<1p> * <2p> = <3p>' expandMacrosWithArguments: {2. 3. 2 * 3}.

'{1} * {2} = {3}' format: {2. 3. 2 * 3}.

So your code could be rewritten as the following

1 to: 9 do: [ :i |
    1 to: i do: [ :j |
        Transcript
            show: ('{1} * {2} = {3}' format: {j. i. j * i});
            show: ' '
    ].
    Transcript show: ' '; cr.
].

Update re: escape characters

There are no escape characters in Strings with the only exception being single quote ('), which you "escape" by doubling it ('It''s doubled!')

To write new line, or tabulator, you can

  1. type it into the string

    a := 'two lines'. (note that StackOverflow replaced the tab with spaces :/)

  2. Join strings

    b := 'two', String cr, String tab, 'lines'.

  3. Tell write stream to add it

    c := String streamContents: [ :stream | stream << 'two'; cr; tab; << 'lines' ].

  4. use expandMacros

    d := 'two<n><t>lines' expandMacros.

a = b = c = d

Peter Uhnak
  • 9,617
  • 5
  • 38
  • 51
  • Thanks. That solves my problem. Forgot to ask. How to display "Escape character" in Smalltalk. I know in C, printf("%d * %d = %d\n", j, i, j * i); seems fine. But in Smalltalk, Transcript show: ('{1} * {2} = {3}cr.' format: {i. j. i * j}). is not OKAY. How can I solve that? – Sleeping On a Giant's Shoulder Nov 10 '17 at 04:58
2

If you'd prefer to use something similar to printf, Squeak has the expandMacrosWith: family of methods (browse class String).

Your example could be written as:

1 to: 9 do: [:i|
    1 to: i do: [:j|
        Transcript show: ('<1p> * <2p> = <3p>   ' expandMacrosWith: j with: i with: j*i)
        ].
    Transcript show: ' '; cr.
    ].

The receiver of expandMacros... contains placeholders between angled brackets. I can't find the relevant documentation in Squeak, the following is taken from the comment to the equivalent implementation in Dolphin Smalltalk:

Expand the receiver with replacable arguments.
e.g.
    'Hello <D><N><1P> <D>' expandMacrosWithArguments: #('World' 1).
    'Hello <2?Good:Bad> <1S>' expandMacrosWithArguments: #('World' false)

<nD> expands the nth parameter using it's #displayString
<nP> expands the nth parameter using it's #printString
<nS> expands the nth parameter treating it as a <String>
<N> expands as a newline combination
<T> expands as a TAB
<n?xxxx:yyyy> if the nth parameter is true expands to 'xxxx' else 'expands to yyyy'