Perform block for each satisfied condition, otherwise perform other block - iteration

Imagine that you have to select some values and for each of them you have to evaluate a block. On the other hand if there is no value that satisfies the condition another block has to be evaluated.
Example:
Consider the next method signature:
forPositivesOf: aCollection do: aBlock otherwise: defaultBlock
This method should evaluate a block aBlock with every positive element of aCollection, but if there are no elements like that, evaluate defaultBlock. Please note that in reality the method may calculate something more complex than just positive numbers, and instead of aCollection there can be a much complex object.

A more compact version of the first alternative is the following that doesn't instantiate a new closure, and just uses the ones received as arguments.
forPositivesOf: aCollection do: aBlock otherwise: defaultBlock
^(aCollection select: [:each | each positive ])
ifEmpty: defaultBlock
ifNotEmpty: [ :collection | collection do: aBlock ]

Taking Uko's solution a bit further:
forPositivesOf: aCollection do: aBlock otherwise: defaultBlock
^ (aCollection
select: #positive
thenCollect: aBlock
) ifEmpty: defaultBlock

At the moment I see two solutions:
1)
forPositivesOf: aCollection do: aBlock otherwise: defaultBlock
(aCollection select: #positive)
ifEmpty: [ defaultBlock value ]
ifNotEmpty: [ :collection |
collection do: [ :el | aBlock cull: el ] ]
but in case calculation of positive is expensive it would be good to evaluate aBlock for the first encountered element, as then the one who passed aBlock will be able to react in any desired way.
2)
forPositivesOf: aCollection do: aBlock otherwise: defaultBlock
| encountered |
encountered := false.
aCollection do: [ :el |
el positive ifTrue: [
encountered := true.
aBlock cull: el ] ].
encountered ifFalse: [
defaultBlock value ]
But I don't like the extra encountered variable, it makes code less functional.

One really nice functional way that works for SequenceableCollections:
forPositivesOf: aCollection do: aBlock otherwise: defaultBlock
(aCollection
select: #positive
thenCollect: [ :el | aBlock cull: el ]) ifEmpty: [ defaultBlock value ]

Related

How do we iterate and select an element of a set in pharo?

My collection is a Set that contains a number of dictionaries. How can iterate over each dictionary in the Set to select a specific key.
a Set(a Dictionary('age'->'25' 'code'->2512) a Dictionary('age'->'40' 'code'->'1243') a Dictionary('age'->'35' 'code'->'7854'))
set := {
{ 'age'->'25'. 'code'->'2512' } asDictionary .
{ 'age'->'40'. 'code'->'1243' } asDictionary.
{ 'age'->'35'. 'code'->'7854' } asDictionary.
} asSet.
If you are interested in retrieving just a single item, then detect: is the way to go. It will return the first item matching the predicate (the block). Note that Set has no defined order, so if you have multiple items matching, it may return different ones at different time.
d := set detect: [ :each | (each at: 'code') = '1243' ].
d. "a Dictionary('age'->'40' 'code'->'1243' )"
If you want to retrieve multiple items that all match the predicate, then use select:
multi := set select: [ :each | (each at: 'age') asNumber >= 35 ].
multi. "a Set(a Dictionary('age'->'40' 'code'->'1243' ) a Dictionary('age'->'35' 'code'->'7854' ))"
Update from comment for commenting:
As Carlos already stated, collect: will do what you need. It applies the transformation block to every item in the collection and then returns a collection of results.
codes := set collect: [ :each | each at: 'code' ].
Works for any collection
#(2 3 4) collect: [ :each | each squared ] "#(4 9 16)"
For further I recommend going through the Collections chapter in Pharo By Example book https://ci.inria.fr/pharo-contribution/job/UpdatedPharoByExample/lastSuccessfulBuild/artifact/book-result/Collections/Collections.html
mySet do: [:each | each do: [ :i | i doStuff ]]
or use detect (I`m not sure if detect works like this, I never used it so far):
mySet do: [:i | i detect: [ :each| (each at: 'key') doStuff ]].
or use keysDo:
mySet do: [:each | each keysDo: [ :k | k doStuff ]]
Check out: http://pharo.gforge.inria.fr/PBE1/PBE1ch10.html

Visibilily in #inject:into: block

This code:
((1 to: 10)
inject: (WriteStream on: String new)
into: [ :strm :each |
((each rem: 3) = 0)
ifTrue: [
strm
nextPutAll: each printString;
space;
yourself ]]) contents
fails because strm is undefined where it is used in the ifTrue: block. Why is it not visible there?
Edit: I tried it out in VASt and Pharo.
The problem is that the implied ifFalse: branch returns nil. To fix this, try the following:
((1 to: 10)
inject: (WriteStream on: String new)
into: [ :strm :each |
((each rem: 3) = 0)
ifFalse: [strm] "This is needed to avoid nil being returned"
ifTrue: [
strm
nextPutAll: each printString;
space;
yourself ]]) contents
Depending on the dialect (methods available), you can take much shorter approach
((1 to: 10) select: [ :each | (each rem: 3) = 0 ]) joinUsing: ' '
As a rule of thumb¹, any collection do: [ :each | something ifTrue: [] ] can be turned into much more straight-forward and readable collection select: [] or collection reject: []
Doing so will spread out the complexity over several independent steps (1. filtering, 2. adding to stream), instead of shoving it all together.
Or if you want to stick to your original
(((1 to: 10) select: [ :each | (each rem: 3) = 0 ])
inject: (WriteStream on: String new)
into: [ :stream :each |
stream
nextPutAll: each printString;
space;
yourself ]) contents
or
String streamContents: [ :stream |
(1 to: 10)
select: [ :each | (each rem: 3) = 0 ]
thenDo: [ :each |
stream
nextPutAll: each printString;
space
]
]
¹So not always, but always good to keep in mind when you encounter such situation.

How to find all methods available in Smalltalk and search by name?

In Smalltalk, is there a way to search for all methods available (of any object), say, that contain the word convert (case insensitive search), and also contain the word string? (the method name, not the source code)
In Smalltalk you have direct access to all classes, their methods and their source code, so you can go through them.
Pharo
Go over all the classes and then from each class select all methods that match your needs (or use the Finder tool).
Object withAllSubclasses flatCollect: [ :cls |
cls methods select: [ :method |
(method selector includesSubstring: 'convert' caseSensitive: false) and: [
(method selector includesSubstring: 'string' caseSensitive: false) ]
]
].
GNU Smalltalk
GST doesn't have as nice API, but it can be done also.
(Object withAllSubclasses collect: [ :cls |
cls methodDictionary ifNotNil: [ :dict |
dict values select: [ :method |
(method selector asLowercase indexOfSubCollection: 'convert' asLowercase) > 0 and: [
(method selector asLowercase indexOfSubCollection: 'string' asLowercase) > 0 ]
]
]
]) join
VisualWorks
(also Pharo and Squeak, and with ifNotNil: also GNU Smalltalk)
VW doesn't have #flatten, so it's implemented explicitly. For case-insensitive search #findSameAs:startingAt:wildcard: can also be used.
(Object withAllSubclasses collect: [ :cls |
cls methodDictionary values select: [ :method |
(method selector asLowercase findString: 'convert' asLowercase startingAt: 1) > 0 and: [
(method selector asLowercase findString: 'string' asLowercase startingAt: 1) > 0 ]
]
]) inject: #() into: [ :arr :each | arr, each ]
Dolphin
Dolphin seems to have different object model, see Leandro's answer below.
This may not work on all smalltalk dialects, but it works at least
with squeak and pharo (other smalltalks may have similar tools/classes)
SystemNavigation default browseAllSelect:[:e |
(e selector includesSubstring:'convert' caseSensitive:false)
and:[e selector includesSubstring:'string' caseSensitive:false]]
This is more a complement to the answer given by #Peter.
Be aware that in some dialects (e.g., Dolphin) the message #withAllSubclasses will only collect classes, and not metaclasses. Because of that the enumerations in #Peter's answer should add all metaclasses in an explicit way.
For instance,
selectors := OrderedCollection new.
Object withAllSubclasses do: [:class | | matching |
matching := class selectors select: [:s |
(s includesString: 'onvert') and: [s includesString: 'tring']].
selectors addAll: matching.
matching := class class selectors select: [:s |
(s includesString: 'onvert') and: [s includesString: 'tring']].
selectors addAll: matching].
^selectors
Note BTW that I've removed the first letter from both 'convert' and 'string' to cheaply prevent case mismatches.
Another difference with my code is that it iterates over the selectors of a class rather than over its methods.
UPDATE
Note that I've used two enumerations because we cannot do this:
class selectors , class class selectors select: [:s |
etc. The reason is that the selectors of a class come in a Set and these do not understand #,.
We could have done instead:
all := class selectors addAll: class class selectors; yourself.
all selectors select: [:s |
etc. (note the use of #yourself)
This is to add different flavor to the list above.
#Smalltalk/X
Object withAllSubclasses flatCollect: [ :cls |
cls methodDictionary values select: [ :method |
(method selector includesSubstring: 'convert' caseSensitive: false) and: [
(method selector includesSubstring: 'string' caseSensitive: false) ]
]
].

How to retrieve a function from a series of functions and call it

I'm trying to create a dispatcher of functions in Rebol 3, so that for each string the program receives there's an associated function to be called.
For example:
handlers: make map! [
"foo" foo-func
"bar" bar-func
]
where foo-func and bar-func are functions:
foo-func: func [ a b ] [ print "foo" ]
bar-func: func [ a b ] [ print "bar" ]
The idea is to select the function starting from the string, so:
f: select handlers "foo"
so that executing f is the same as executing foo-func and then call f with some arguments:
f param1 param2
I tried quoting the words in the map!, or using get-words but without success.
Using a get-word! at the console, without passing through a map! it works:
>> a: func [] [ print "Hello world!" ]
>> a
Hello world!
>> b: :a
>> b
Hello world!
Any help appreciated.
select handlers "foo" only get the word foo-func:
f: select handlers "foo"
probe f ;will get: foo-func
You need to get its content:
f: get f
f 1 2 ;will print "foo"
Or more compact:
f: get select handlers "foo"
It's better to actually have the reference to the function in the map, rather than a word that refers to the function. If you store a word then you have to make sure the word is bound to an object which has a reference to that function, like this:
handlers: object [
foo-func: func [ a b ] [ print "foo" ]
bar-func: func [ a b ] [ print "bar" ]
]
handler-names: map [
"foo" foo-func
"bar" bar-func
]
apply get in handlers select handler-names name args
But if you just have a reference to the function in your map, you don't have to do the double indirect, and your code looks like this:
handlers: map reduce [
"foo" func [ a b ] [ print "foo" ]
"bar" func [ a b ] [ print "bar" ]
]
apply select handlers name args
Cleaner code, and more efficient too. Or if you're careful enough, like this:
handlers/(name) a b
The path method above will also work if you want the code to do nothing if there is no handler - common in cases where you have optional handlers, such as in GUIs.
You can even have more than one reference to the same function with different key names. You don't have to assign functions to words, they're just values. You can also use the path method to collect the handlers in the first place, saving a reduce.
handlers: make map! 10 ; preallocate as many entries as you expect
handlers/("foo"): func [ a b ] [ print "foo" ]
handlers/("bar"): func [ a b ] [ print "bar" ]
handlers/("baz"): select handlers "bar" ; multiple references
That path syntax is just another way to call poke, but some prefer it. We have to put the string values in parens because of a (hopefully temporary) syntax conflict, but within those parens the string keys work. It's a faster alternative to do select or poke.
foo-func in your map is just an unevaluated word
>> type? select handlers "foo"
== word!
You should first create your functions and then reduce the block, you use for creating your handler map so
handlers: make map! reduce [
"foo" :foo-func
"bar" :bar-func
]
then you have functions inside your map
>> type? select handlers "foo"
== function!
Try:
....
f: do select handlers "foo"
....

How to get all items in from of Text from Ordered collection

simple question i got
|list string|
list:= #('ab' 'efghij' 'lmnopqrst'). "Ordered collection"
list do:[:each| "string with:each" i know this is not right how do i add items ].
I tried streams too it returned me this "an ordered collection('ab' 'efghij' 'lmnopqrst')"
All i need is a single Text that has
'abc efghij lmnopqrst '
In Pharo you can do
Character space join: list
If join: is not available and it should perform well then you can use a stream variant
String streamContents: [:stream|
list
do [:each| stream nextPutAll: each ]
separatedBy: [ stream nextPut: Character space ]
Object class has defined a #asString message that reads:
"Answer a string that represents the receiver."
So, you can do:
| aList aStringsList |
aList := #('ab' 'efghij' 'lmnopqrst'). "Array"
aStringsList := aList collect: [ :each | each asString ]
And aStringsList will be an Array of the Strings returned by the invocation of #asString in each of aList's members.
If you want to concatenate all of them in a single String, you can use the #inject:into: method of the collections instead of #collect::
aList inject: '' into: [ :text :each | text , each asString , ' ' ]
If you print that you'll get the 'ab efghij lmnopqrst ' you want :)