Loops and testing - beginner's question

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Loops and testing - beginner's question

Peter Kenny-2
Hi all

I have been using Dolphin for over a year, but I still find myself stuck on
quite simple points. Could some kind person give me a steer on my current
hold-up?

I am trying to tidy up the coding of a part of my app (which may be my first
mistake - it works, so perhaps I should leave it alone!). I have modified
the coding of the method View>>#onMouseWheeled:, and my code currently
reads:

test := self topView allSubViews select:[:view|{condition}].
selectedView := test notEmpty ifTrue: [test at: 1] ifFalse: [nil].

The logic of the selection {condition} specifies that the number of
successful selects will be zero or one (usually one), so it seems
inefficient to test all the remaining views once I have found the one that
satisfies the condition. At first sight it looks as though
SequenceableCollection>>#findFirst: should do what I want, but this returns
the index of the matching view, not the view itself. I can get round this by
writing something like:

gotIt := (allViews := self topView allSubViews)
findFirst:[:view|{condition}].
selectedView := gotIt > 0 ifTrue: [allViews at: gotIt] ifFalse:[nil].

but somehow this looks kludgy. What I would like is an equivalent of
#findFirst: which returns the matching element, not its index, and
presumably nil if no match. Since I can't find such a method, I tried to
construct the equivalent using #do: or #keysAndValuesDo:, but I find I have
no idea how to break out of the loop when I have found a matching element.
If I could, I would write something like:

selectedView := nil.
self topView allSubViews do: [:view| {condition} ifTrue: [selectedView :=
view. break]].

which looks the neatest of all.

Is there any way of breaking out of a loop like this? Is it worth worrying
about making the code more efficient anyway? - I started programming when we
had to think about efficiency, but maybe it doesn't matter now.

Sorry to ask such trivial questions. Thanks in advance for any help.

Peter Kenny


Reply | Threaded
Open this post in threaded view
|

Re: Loops and testing - beginner's question

Ian Bartholomew-21
Peter,

> test := self topView allSubViews select:[:view|{condition}].
> selectedView := test notEmpty ifTrue: [test at: 1] ifFalse: [nil].

It looks like you want #detect:ifNone: which will go through a
collection and answer the first one that passes the test, or nil if none do.

selectedView := self topView allSubViews
                        detect: [:each | {condition}]
                        ifNone: []

NB an empty block answers nil


> Is there any way of breaking out of a loop like this? Is it worth worrying
> about making the code more efficient anyway? - I started programming when we
> had to think about efficiency, but maybe it doesn't matter now.

The normal way (other than using #detect) would be to put the test in
another method and return the found value or nil from there.  Something like

whateverOrNil := self findItFrom: aCollection

and

MyClass>>findItFrom: aCollection
        aCollection do: [:each |
                {condition} ifTrue: [^each]].
        ^nil


--
Ian

Use the Reply-To address to contact me.
Mail sent to the From address is ignored.


Reply | Threaded
Open this post in threaded view
|

Re: Loops and testing - beginner's question

Peter Kenny-2
"Ian Bartholomew" <[hidden email]> wrote in message
news:[hidden email]...
> Peter,
>
> > test := self topView allSubViews select:[:view|{condition}].
> > selectedView := test notEmpty ifTrue: [test at: 1] ifFalse: [nil].
>
> It looks like you want #detect:ifNone: which will go through a
> collection and answer the first one that passes the test, or nil if none
do.


Ian

Thanks a lot. What I was looking for is exactly what #detect:ifNone: does,
so this solves my problem. I was considering another method to Collection by
adapting the code of #findFirst:, but it already exists - however the
difference of terminology between 'find' to 'detect' managed to hide that
from me!

Thanks again

Peter


Reply | Threaded
Open this post in threaded view
|

Re: Loops and testing - beginner's question

Schwab,Wilhelm K
In reply to this post by Ian Bartholomew-21
Peter,

>> test := self topView allSubViews select:[:view|{condition}].
>> selectedView := test notEmpty ifTrue: [test at: 1] ifFalse: [nil].
>
>
> It looks like you want #detect:ifNone: which will go through a
> collection and answer the first one that passes the test, or nil if none
> do.
>
> selectedView := self topView allSubViews
>             detect: [:each | {condition}]
>             ifNone: []
>
> NB an empty block answers nil

I think Ian pegged it.  To his reply I will add that you might look at
#detect:ifNone: compared to #detect:, which signals an error if no item
is found.  In GUI code (handling mouse events qualifies), you probably
do not want errors being generated, but in many other situations, it is
nice to have the code complain at the site of the problem (unable to
find a suitable element) vs. fumbling along until a less obvious DNU
error occurs later.

Note that error handlers ( #on:do: etc.) have costs to establish and
greater costs to trap an error.  However, there are times when you might
know what happened but not what to do about it - signalling an error to
be trapped at higher levels is the cleanest (and most idiot proof<g>)
way to handle it.

Have a good one,

Bill


--
Wilhelm K. Schwab, Ph.D.
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Loops and testing - beginner's question

Peter Kenny-2
"Bill Schwab" <[hidden email]> wrote in message
news:de2n1n$12he$[hidden email]...

> Peter,
>
> I think Ian pegged it.  To his reply I will add that you might look at
> #detect:ifNone: compared to #detect:, which signals an error if no item
> is found.  In GUI code (handling mouse events qualifies), you probably
> do not want errors being generated, but in many other situations, it is
> nice to have the code complain at the site of the problem (unable to
> find a suitable element) vs. fumbling along until a less obvious DNU
> error occurs later.
>
> Note that error handlers ( #on:do: etc.) have costs to establish and
> greater costs to trap an error.  However, there are times when you might
> know what happened but not what to do about it - signalling an error to
> be trapped at higher levels is the cleanest (and most idiot proof<g>)
> way to handle it.
>
> Have a good one,
>
> Bill
>
>
> --
> Wilhelm K. Schwab, Ph.D.
> [hidden email]

Bill

Thanks for the advice. I shall bear it in mind for future use. In the
present context the ifNone: case is not an error, it just means 'do
nothing'. What I am doing is modifying the handling of mouse wheel events so
that the pane that scrolls is the one containing the cursor, not what
Windows calls the 'active pane' (i.e. I want Dolphin to do what Squeak does
in this respect). The ifNone: case arises if the cursor is pointing at a
non-scrollable part of the window (or outside the window).

Thanks again

Peter