GNU Smalltalk: Difference between revisions
Content added Content deleted
(from wikipedia) |
(→Examples: -source) |
||
Line 6: | Line 6: | ||
These examples only work on GNU Smalltalk 3.0 and later versions. Classic [[Hello world]] example: |
These examples only work on GNU Smalltalk 3.0 and later versions. Classic [[Hello world]] example: |
||
'Hello World!' displayNl |
|||
<source lang="smalltalk"> |
|||
'Hello World!' displayNl |
|||
</source> |
|||
Some basic Smalltalk code: |
Some basic Smalltalk code: |
||
"Everything, including a literal, is an object, so this works:" |
|||
<source lang="smalltalk"> |
|||
-199 abs "199" |
|||
"Everything, including a literal, is an object, so this works:" |
|||
'gst is cool' size "11" |
|||
' |
'Slick' indexOf: $c "4" |
||
'Nice Day Isn''t It?' asLowercase asSet asSortedCollection asString "' '?acdeinsty'" |
|||
'Slick' indexOf: $c "4" |
|||
'Nice Day Isn''t It?' asLowercase asSet asSortedCollection asString "' '?acdeinsty'" |
|||
</source> |
|||
===Collections=== |
===Collections=== |
||
Constructing and using an array: |
Constructing and using an array: |
||
a := #(1 'hi' 3.14 1 2 #(4 5)) |
|||
<source lang="smalltalk"> |
|||
a := #(1 'hi' 3.14 1 2 #(4 5)) |
|||
a at: 3 "3.14" |
|||
a |
a reverse "#((4 5) 2 1 3.14 'hi' 1)" |
||
a |
a asSet "Set(1 'hi' 3.14 2 #(4 5))" |
||
a asSet "Set(1 'hi' 3.14 2 #(4 5))" |
|||
</source> |
|||
Constructing and using a hash table: |
Constructing and using a hash table: |
||
hash := Dictionary from: { 'water' -> 'wet'. 'fire' -> 'hot' }. |
|||
<source lang="smalltalk"> |
|||
hash : |
hash at: 'fire' "Prints: hot" |
||
hash at: 'fire' "Prints: hot" |
|||
hash keysAndValuesDo: [ :k :v | |
|||
('%1 is %2' % { k. v }) displayNl ] |
|||
hash keysAndValuesDo: [ :k :v | |
|||
('%1 is %2' % { k. v }) displayNl ] |
|||
"Prints: water is wet |
|||
fire is hot" |
|||
"Prints: water is wet |
|||
fire is hot" |
|||
hash removeKey: 'water' "Deletes 'water' -> 'wet'" |
|||
hash removeKey: 'water' "Deletes 'water' -> 'wet'" |
|||
</source> |
|||
===Blocks and iterators=== |
===Blocks and iterators=== |
||
Parameter-passing a block to be a closure: |
Parameter-passing a block to be a closure: |
||
"remember a block." |
|||
<source lang="smalltalk"> |
|||
remember := [ :name | ("Hello, %1!" % { name }) displayNl ]. |
|||
"remember a block." |
|||
remember := [ :name | ("Hello, %1!" % { name }) displayNl ]. |
|||
"When the time is right -- call the closure!" |
|||
remember value: 'world' |
|||
"When the time is right -- call the closure!" |
|||
"=> "Hello, world!"" |
|||
"=> "Hello, world!"" |
|||
</source> |
|||
Returning closures from a method: |
Returning closures from a method: |
||
Integer extend [ |
|||
<source lang="smalltalk"> |
|||
asClosure [ |
|||
Integer extend [ |
|||
| value | |
|||
asClosure [ |
|||
value := self. |
|||
value := self. |
|||
^{ [ :x | value := x ]. [ value ] } |
^{ [ :x | value := x ]. [ value ] } |
||
] |
] |
||
] |
] |
||
blocks := 10 asClosure. |
blocks := 10 asClosure. |
||
setter := blocks first. |
setter := blocks first. |
||
getter := blocks second. |
getter := blocks second. |
||
getter value "=> 10" |
getter value "=> 10" |
||
setter value: 21 "=> 21" |
setter value: 21 "=> 21" |
||
getter value "=> 21" |
getter value "=> 21" |
||
</source> |
|||
Using block to send info back to the caller: |
Using block to send info back to the caller: |
||
Integer extend [ |
|||
<source lang="smalltalk"> |
|||
ifEven: evenBlock ifOdd: oddBlock [ |
|||
Integer extend [ |
|||
^self even |
|||
ifEven: evenBlock ifOdd: oddBlock [ |
|||
ifTrue: [ evenBlock value: self ] |
|||
ifFalse: [ oddBlock value: self ] |
|||
] |
|||
ifFalse: [ oddBlock value: self ] |
|||
] |
|||
] |
|||
] |
|||
"Invoke the above method, passing it a block." |
|||
10 ifEven: [ :n | n / 2 ] ifOdd: [ :n | n * 3 + 1 ] "=> 5" |
|||
"Invoke the above method, passing it a block." |
|||
10 ifEven: [ :n | n / 2 ] ifOdd: [ :n | n * 3 + 1 ] "=> 5" |
|||
</source> |
|||
Iterating over enumerations and arrays using blocks: |
Iterating over enumerations and arrays using blocks: |
||
array := #(1 'hi' 3.14) |
|||
<source lang="smalltalk"> |
|||
array : |
array do: [ :item | item displayNl ] |
||
"=> 1" |
|||
array do: [ :item | item displayNl ] |
|||
"=> |
"=> hi" |
||
"=> |
"=> 3.14" |
||
"=> 3.14" |
|||
(3 to: 6) do: [ :item | item displayNl ] |
|||
"=> 3" |
|||
(3 to: 6) do: [ :item | item displayNl ] |
|||
"=> |
"=> 4" |
||
"=> |
"=> 5" |
||
"=> |
"=> 6" |
||
"=> 6" |
|||
</source> |
|||
A method such as ''inject:into:'' can accept both a parameter and a block. It iterates over each member of a list, performing some function on while retaining an aggregate. This is analogous to the [[foldl]] function in [[Haskell]]. For example: |
A method such as ''inject:into:'' can accept both a parameter and a block. It iterates over each member of a list, performing some function on while retaining an aggregate. This is analogous to the [[foldl]] function in [[Haskell]]. For example: |
||
#(1 3 5) inject: 10 into: [ :sum :element | sum + element ] "=> 19" |
|||
<source lang="smalltalk"> |
|||
#(1 3 5) inject: 10 into: [ :sum :element | sum + element ] "=> 19" |
|||
</source> |
|||
On the first pass, the block receives 10 (the argument to inject) as sum, and 1 (the first element of the array) as element, This returns 11. 11 then becomes sum on the next pass, which is added to 3 to get 14. 14 is then added to 5, to finally return 19. |
On the first pass, the block receives 10 (the argument to inject) as sum, and 1 (the first element of the array) as element, This returns 11. 11 then becomes sum on the next pass, which is added to 3 to get 14. 14 is then added to 5, to finally return 19. |
||
Line 118: | Line 100: | ||
Blocks work with many built-in methods: |
Blocks work with many built-in methods: |
||
(File name: 'file.txt') withWriteStreamDo: [ :file | |
|||
<source lang="smalltalk"> |
|||
file nextPutAll: 'Wrote some text.'; nl ] |
|||
(File name: 'file.txt') withWriteStreamDo: [ :file | |
|||
"File is automatically closed here" |
|||
file nextPutAll: 'Wrote some text.'; nl ] |
|||
"File is automatically closed here" |
|||
(File name: 'file.txt') linesDo: [ :each | |
|||
each displayNl ] |
|||
"=> Wrote some text." |
|||
(File name: 'file.txt') linesDo: [ :each | |
|||
each displayNl ] |
|||
"=> Wrote some text." |
|||
</source> |
|||
Using an enumeration and a block to square the numbers 1 to 10: |
Using an enumeration and a block to square the numbers 1 to 10: |
||
(1 to: 10) collect: [ :x | x squared ] "=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]" |
|||
<source lang="smalltalk"> |
|||
(1 to: 10) collect: [ :x | x squared ] "=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]" |
|||
</source> |
|||
===Classes=== |
===Classes=== |
||
Line 138: | Line 117: | ||
The following code defines a class named Person. By deriving from Magnitude, it automatically defines all comparison methods except one (<code><</code>). With the addition of that one, <code>asSortedCollection</code> can sort by age. Note that we can override the way the object is printed/displayed (the default is to share the programmer-print and user-display representation) by overriding <code>printOn:</code>. |
The following code defines a class named Person. By deriving from Magnitude, it automatically defines all comparison methods except one (<code><</code>). With the addition of that one, <code>asSortedCollection</code> can sort by age. Note that we can override the way the object is printed/displayed (the default is to share the programmer-print and user-display representation) by overriding <code>printOn:</code>. |
||
Magnitude subclass: Person [ |
|||
<source lang="smalltalk"> |
|||
| name age | |
|||
Magnitude subclass: Person [ |
|||
Person class >> name: name age: age [ |
|||
^self new name: name; age: age; yourself |
|||
] |
|||
^self new name: name; age: age; yourself |
|||
] |
|||
< aPerson [ ^self age < aPerson age ] |
|||
name [ ^name ] |
|||
name: value [ name := value ] |
|||
age [ ^age ] |
|||
age: value [ age := value ] |
|||
printOn: aStream [ aStream nextPutAll: ('%1 (%2)' % { name. age }) ] |
|||
] |
|||
group := { |
|||
Person name: 'Dan' age: 23. |
|||
Person name: 'Mark' age: 63. |
|||
Person name: 'Cod' age: 16. |
|||
}. |
|||
group asSortedCollection reverse |
|||
The above prints three names in reverse age order: |
|||
< aPerson [ ^self age < aPerson age ] |
|||
name [ ^name ] |
|||
name: value [ name := value ] |
|||
age [ ^age ] |
|||
age: value [ age := value ] |
|||
printOn: aStream [ aStream nextPutAll: ('%1 (%2)' % { name. age }) ] |
|||
] |
|||
OrderedCollection (Mark (63) Dan (23) Cod (16) ) |
|||
group := { |
|||
Person name: 'Dan' age: 23. |
|||
Person name: 'Mark' age: 63. |
|||
Person name: 'Cod' age: 16. |
|||
}. |
|||
group asSortedCollection reverse |
|||
</source> |
|||
The above prints three names in reverse age order: |
|||
<source lang="text"> |
|||
OrderedCollection (Mark (63) Dan (23) Cod (16) ) |
|||
</source> |
|||
===Exceptions=== |
===Exceptions=== |
||
An exception is raised with a <code>halt</code> call: |
An exception is raised with a <code>halt</code> call: |
||
<source lang="smalltalk"> |
|||
self halt |
self halt |
||
</source> |
|||
An optional message can be added to the exception; there's also <code>error:</code> which raises a different kind of exception: |
An optional message can be added to the exception; there's also <code>error:</code> which raises a different kind of exception: |
||
<source lang="smalltalk"> |
|||
self halt: 'This is a message' |
self halt: 'This is a message' |
||
self error: 'This is a message' |
self error: 'This is a message' |
||
</source> |
|||
These are actually wrappers for the actual exception raising method, <code>signal>: |
These are actually wrappers for the actual exception raising method, <code>signal>: |
||
<source lang="smalltalk"> |
|||
Error signal |
Error signal |
||
Error signal: 'Illegal arguments!' |
Error signal: 'Illegal arguments!' |
||
</source> |
|||
Exceptions are handled by <code>on:do:</code> blocks. |
Exceptions are handled by <code>on:do:</code> blocks. |
||
[ something to do ] |
|||
<source lang="smalltalk"> |
|||
on: Exception |
|||
[ something to do ] |
|||
do: [ :ex | handle exception in ex ] |
|||
on: Exception |
|||
do: [ :ex | handle exception in ex ] |
|||
</source> |
|||
Of course you can catch only particular exceptions (and their subclasses): |
Of course you can catch only particular exceptions (and their subclasses): |
||
[ something to do ] |
|||
<source lang="smalltalk"> |
|||
on: Warning |
|||
[ something to do ] |
|||
do: [ :ex | handle exception in ex ] |
|||
on: Warning |
|||
do: [ :ex | handle exception in ex ] |
|||
</source> |
|||
It is possible to use the exception object, which is made available to the handler clause, to exit or resume the first block: |
It is possible to use the exception object, which is made available to the handler clause, to exit or resume the first block: |
||
[ Error signal: 'foo' ] |
|||
<source lang="smalltalk"> |
|||
on: Error |
|||
[ Error signal: 'foo' ] |
|||
do: [ :ex | ex return: 5 ] |
|||
on: Error |
|||
do: [ :ex | ex return: 5 ] |
|||
(Warning signal: 'now what?') printNl "=> nil" |
|||
(Warning signal: 'now what?') |
[ (Warning signal: 'now what?') |
||
printNl ] on: Warning do: [ :ex | ex resume: 5 ] "=> 5" |
|||
[ (Warning signal: 'now what?') |
|||
printNl ] on: Warning do: [ :ex | ex resume: 5 ] "=> 5" |
|||
</source> |
|||
== External links == |
== External links == |