[prev] [up] [next]

10 One Liners to Impress Your Friends

Introduction

Inspired by corresponding papers for the Ruby and Scala programming language, here is a Smalltalk version of "10 One Liners to Impress Your Friends". These demonstrate that Smalltalk's elegant syntax also makes it very easy to write highly readable, concise and powerful code. And all of this has been around for more than 30 years now!

Multiply Each Item in a List by 2

The collect: method, which is implemented by all collections, returns a new collection of the same type as the receiver, containing the collected results from applying a block (lambda, anonymous function) to each element. It directly corresponds to the map function of lisp/scheme, which also expects a lambda (closure) as processing argument.
Notice the "(1 to:10)" expression, which creates an interval object (1, 2, 3,..., 9, 10) without a special syntactic construct for list comprehension.
    (1 to:10) collect:[:e | e * 2]

By the way: there is also a "to:collect:" method found in the Number class, which allows for the above to be also written as:

    1 to:10 collect:[:e | e * 2]
In contrast to the above, this version does not create a temporary collection object, but enumerates the values directly (that is like a collecting for-loop).

Sum a List of Numbers

Similar to ReduceLeft from functional languages, the Smalltalk inject: method can be used to process each element of a collection, where each result is passed on to the next iteration as input parameter. The very first injected value is passed in as argument:
    (1 to:1000) inject:0 into:[:sumSoFar :e | sumSoFar + e]
of course, every collection also implements the sum method, so you can make it even more compact:
    (1 to:1000) sum

Verify if Exists in a String

The following returns true, if a list contains any from a list of words:
    wordList := #('scala' 'akka' 'play framework' 'sbt' 'typesafe').
    tweet := 'This is an example tweet talking about scala and sbt.'.

    wordList contains:[:w | tweet includesString:w]
the contains: method expects a block argument and returns true, if this block ever returns true, when applied to each element of the collection. It does not proceed through the remaining elements, if any delivers a false.
But, as this is a relatively common task, the Smalltalk library also contains a method called includesAny:, which checks a collection's elements to contain any from another collection. This gives us:
    wordList := #('scala' 'akka' 'play framework' 'sbt' 'typesafe').
    tweet := 'This is an example tweet talking about scala and sbt.'.

    tweet asCollectionOfWords includesAny:wordList

Read in a File

The following gives you a list of text lines:
    fileText := 'data.txt' asFilename contents
or as one big string:
    fileText := 'data.txt' asFilename contentsAsString

Happy Birthday to You!

A common one-liner which prints out the "Happy Birthday" song.
    1 to:4 do:[:i | ('Happy Birthday ',((i == 3) ifTrue:['dear Tony'] ifFalse:['to You'])) printCR ]

Filter List of Numbers

Smalltalk does not directly support returning multiple values; however, we can pass a block as argument, which takes two values. Programmers familiar with continuations will like:
    |passed failed|

    #(49 58 76 82 88 90) partition:[:e | e > 60] into:[:a :b| passed := a. failed := b].
    passed printCR. failed printCR.

Find Minimum (or Maximum) in a List

    #(14 35 -7 46 98) reduceLeft:[:min :el | min min: el]
some Smalltalk dialects do not have reduceLeft:; then, you can write:
    #(14 35 -7 46 98) inject:nil into:[:minSoFar :el | (minSoFar ? el) min: el]
(the ?-operator returns the left side if it is non-nil, the right side if it is nil.
Finally, there is the super-concise min:
    #(14 35 -7 46 98) min
and:
    #(14 35 -7 46 98) minMax
to find both minimum and maximum as a tuple.

Count, how often a Word is found in a File

    'data.text' asFilename contentsAsString asCollectionOfWords occurrencesOf:'fred'
if the file is large, and you cannot hold its contents in memory, process it line-wise:
    bag := Bag new.
    'data.text' asFilename readingLinesDo:[:l | bag addAll:l asCollectionOfWords].
    bag occurrencesOf:'fred'
(not a 1-liner, but we could have written in one longer line).

Generate Strings with Random Contents

use atRandom to pick a random element from a collection:
    (1 to:30) collect:[:i | ($A to:$Z) atRandom] as:String

Splitting Strings

the splitOn: method can split by characters,
    'abc_def_ghi_jkl' splitOn:$_
by substrings:
    'abc==def==ghi==jkl' splitOn:'=='
or by a 'rule' block:
    'abc1def2ghi3jkl' splitOn:[:ch | ch isDigit]


Copyright © 2002 Claus Gittinger, all rights reserved

<info@exept.de>

Doc $Revision: 1.8 $