Discussion:
[Pharo-users] how to write this without a if then
Roelof Wobben
2018-11-27 16:40:56 UTC
Permalink
Hello,

Yesterday I had a talk with luc frabresse about using if then.
He said if I understand it right, Its the best to not using a if then or
a ifTrue/ifFalse.

Can anyone help me figure out how to rewrite this project so I will not
use the ifTrue in the basement function.

my code so far can be found here : https://github.com/RoelofWobben/AOC2015

Roelof
Dale Henrichs
2018-11-27 17:33:54 UTC
Permalink
Roelof,

One technique to eliminating the use of #ifTrue:ifFalse: is to use
double dispatching. There are some good examples of using double
dispatching in Ralph Johnson's paper "ARITHMETIC AND DOUBLE DISPATCHING
IN SMALLTALK-80"[1].  you should be able to get the basic idea by
skimming the smalltalk code examples.

I used double dispatching fairly extensively in Metacello ...

In practice you may not eliminate all use of #ifTrue:ifFalse:, but
double dispatching works quite well in places where you are tempted to
do type checking...

Dale

[1]
https://www.researchgate.net/publication/239578755_Arithmetic_and_double-_dispatching_in_smalltalk-80
Post by Roelof Wobben
Hello,
Yesterday I had a talk with luc frabresse about using if then.
He said if I understand it right, Its the best to not using a if then
or a ifTrue/ifFalse.
Can anyone help me figure out how to rewrite this project so I will
not use the ifTrue in the basement function.
https://github.com/RoelofWobben/AOC2015
Roelof
Richard O'Keefe
2018-11-28 05:15:33 UTC
Permalink
The idea of a flat ban on #ifTrue:ifFalse: is ridiculous.
Number>>abs
^self negative ifTrue: [self negated] ifFalse: [self]

Is there any better way to do this? Not really, you can
only move the #ifTrue:ifFalse: somewhere else.

The main argument against #ifTrue:ifFalse: is NOT TO USE
IT FOR TYPE TESTS. If you want to do different things
depending on the class of x, ask x to do it. (The irony
is that in Smalltalk, #ifTrue:ifFalse: *is* in principle
an object-oriented dispatch.) If you want to do
different things depending on the state of x, and this
requires revealing internal details that would not
otherwise be revealed, ask x to do it.

Two key ideas in software engineering are coupling
(lots of interdependencies makes things hard to re-use)
and cohesion (modules/types/classes should be "about"
one thing). You have to balance the "use OO dispatch"
idea against this: you have a method in class A that
depends on an object of class B, and you don't want
A to have to know too much about B, but on the other
hand, you don't want B to have to depend too much on
what A is up to. If it makes sense to have a B without
any A around, then changes to A should not really
require changes to B.

So eliminating #ifTrue:ifFalse: from your code can make
it WORSE. You have to THINK about each task and decide
where it REALLY belongs. Is this bit of code entirely
about B? Then it belongs in B. Is that bit of code
about already public information concerning B and also
tied to A's needs and wants? Then it belongs in A. And
if that means using some sort of "if", go ahead!

The Pharo 6 sources contain about 5800 classes and
over 40000 ifTrue:/ifFalse:/ifNil:/ifNotNil: uses.
The Dolphin core contains about 2100 clases and
over 12600 ifs.
My Smalltalk has about 13 ifs per class.
Smalltalk/X (JV) has about 6500 classes and
over 127000 ifs, nearly 20 per class.
Post by Roelof Wobben
Hello,
Yesterday I had a talk with luc frabresse about using if then.
He said if I understand it right, Its the best to not using a if then or
a ifTrue/ifFalse.
Can anyone help me figure out how to rewrite this project so I will not
use the ifTrue in the basement function.
my code so far can be found here : https://github.com/RoelofWobben/AOC2015
Roelof
Sven Van Caekenberghe
2018-11-28 07:07:11 UTC
Permalink
Post by Richard O'Keefe
The idea of a flat ban on #ifTrue:ifFalse: is ridiculous.
Number>>abs
^self negative ifTrue: [self negated] ifFalse: [self]
Is there any better way to do this? Not really, you can
only move the #ifTrue:ifFalse: somewhere else.
The main argument against #ifTrue:ifFalse: is NOT TO USE
IT FOR TYPE TESTS. If you want to do different things
depending on the class of x, ask x to do it. (The irony
is that in Smalltalk, #ifTrue:ifFalse: *is* in principle
an object-oriented dispatch.) If you want to do
different things depending on the state of x, and this
requires revealing internal details that would not
otherwise be revealed, ask x to do it.
Two key ideas in software engineering are coupling
(lots of interdependencies makes things hard to re-use)
and cohesion (modules/types/classes should be "about"
one thing). You have to balance the "use OO dispatch"
idea against this: you have a method in class A that
depends on an object of class B, and you don't want
A to have to know too much about B, but on the other
hand, you don't want B to have to depend too much on
what A is up to. If it makes sense to have a B without
any A around, then changes to A should not really
require changes to B.
So eliminating #ifTrue:ifFalse: from your code can make
it WORSE. You have to THINK about each task and decide
where it REALLY belongs. Is this bit of code entirely
about B? Then it belongs in B. Is that bit of code
about already public information concerning B and also
tied to A's needs and wants? Then it belongs in A. And
if that means using some sort of "if", go ahead!
Very well written, thanks.
Post by Richard O'Keefe
The Pharo 6 sources contain about 5800 classes and
over 40000 ifTrue:/ifFalse:/ifNil:/ifNotNil: uses.
The Dolphin core contains about 2100 clases and
over 12600 ifs.
My Smalltalk has about 13 ifs per class.
Smalltalk/X (JV) has about 6500 classes and
over 127000 ifs, nearly 20 per class.
Hello,
Yesterday I had a talk with luc frabresse about using if then.
He said if I understand it right, Its the best to not using a if then or
a ifTrue/ifFalse.
Can anyone help me figure out how to rewrite this project so I will not
use the ifTrue in the basement function.
my code so far can be found here : https://github.com/RoelofWobben/AOC2015
Roelof
Tim Mackinnon
2018-11-28 07:27:16 UTC
Permalink
Post by Richard O'Keefe
The idea of a flat ban on #ifTrue:ifFalse: is ridiculous.
Number>>abs
^self negative ifTrue: [self negated] ifFalse: [self]
Is there any better way to do this? Not really, you can
only move the #ifTrue:ifFalse: somewhere else.
But still it’s always a good thought experiment to think about how to do it without the if, as many more interesting objects and approaches can come out of it.

From above - it might be interesting if positive numbers were different from negative ones ... I’m not saying you would do it (there are often trade offs) but it’s too easy to stick in an if and miss out on a richer domain.

Tim
Richard O'Keefe
2018-11-28 07:54:24 UTC
Permalink
In Advent-of-Code 2015, the first problem is
really quite simple. There are at least two
ways to think about it.
"CS101":
set a counter to 0
for each character of the string
if it is '(' increment the counter
if it is ')' decrement the counter
report the counter

"Higher level":
report the difference between
(the number of '(' characters in the string) and
(the number of ')' characters in the string).

Expressed in Smalltalk this looks something like
Transcript print:
(s occurrencesOf: $() - (s occurrencesOf: $)); cr.

Make no mistake: you *cannot* tell the difference
between $( and $) using class-based dispatch because
they belong to the same class. There has to be an
"if" somewhere, the question is not whether but where.
In this case, counting the number of occurrences of an
object in a collection is has been a standard Collection
method for nearly 40 years; it's one of the basic
operations you need to learn.

The "higher level" approach can be less efficient that
the "CS101" approach, but in a case like this we really
do not care. We want the code to be *clear*.

What about the second part of the problem?
Not having submitted any answers, I can't actually
see the second part on the AOC site, but luckily you
have included it in your program.

We want to
[find the first place] where
[the cumulative sum] of (c=$()
[minus]
[the cumulative sum] of (c=$))
equals -1.

The "CS101" approach is
n := i := 0.
while n >= 0 and i < size(s) do
i +:= 1
if s[i] = $( then n := n + 1
if s[i] = $) then n := n - 1
report n

The "higher level" approach looks something like
n := (s cumCount: [:each | each = $(]) -
(s cumCount: [:each | each = $)])
indexOf: -1.
-- although it gives 0 instead of s size + 1 when
-1 is never reached.

Here #indexOf: is standard, #- is defined on sequences
in Squeak and Pharo, but #cumCount: does not
exist. So we need something like

cumCount: aBlock
|c a|
a := Array new: self size.
c := 0.
self withIndexDo: [:each :i |
(aBlock value: each) ifTrue: [c := c + 1].
a at: i put: c].
^a
This is *not* coupled to the particular use we have
in mind for it; it is in no way tied to characters
or strings. It's quite general.

Note: we do not need any new classes, except maybe
a place to put one problem-specific method.

While this answer, with no loop and no if in the
problem-specific code, is quite pretty, it has a
problem. Suppose the string to have M characters
and the desired step to be number K. The CS101
approach takes O(K) time and no allocations, but
the higher level approach takes O(M) time and
allocates three M-element Arrays. (In a non-strict
functional language like Haskell, the higher level
version *also* takes O(K) time, and with a good
enough "deforesting" compiler should allocate no
data structures.)

For a problem like this, I really don't care about
the efficiency aspect. If I *do* care about that,
then starting from a higher level version gives me
something to test a lower level version against.

To get an efficient answer to the second part,
we still don't need a new semantic class, just
some place to put the code.

Day1
class methods:
indexOfFirstBasementTime: steps
|floor|
floor := 0.
steps keysAndValuesDo: [:i :each |
each = $( ifTrue: [floor := floor + 1].
each = $) ifTrue: [floor := floor - 1].
floor = -1 ifTrue: [^i]].
^0 "same convention as #indexOf:"

Does this contain "if"? Why yes, it does.
Is there any problem with that? Why no, there isn't.
You need to treat members of the same class (left
parenthesis, right parenthesis, others) differently.
You need to treat members of the same class (minus
one, all other integers) differently.
Would there be any gain in clarity or maintainability
if these ifs were somehow eliminated? Certainly NOT.

Quite the reverse, in fact.
input2 withIndexDo: [ :element :index | |action|
action:= SantaAction getActionFor: element. floor := action doMovementFor:
floor .
self hasReachedBasement
ifTrue: [^ index]].
^ '-2'.

There is only one word for this: obfuscated.
I was initially puzzled by your returning -2
instead of the conventional 0 if the basement
is not reached, and then *deeply confused* by
the fact that you are returning a *string* in
this case.

Looking at your code, I was further confused
by variables called 'aSymbol' whose value is
always and only a Character, never a Symbol.
And if I am wrong that
IllegalMoveSanta class>> canHandleInput:
in Smalltalk is to return 0 when something is

approach



We want to find the first place where something
becomes true. There are again at least to approaches.
Post by Roelof Wobben
Hello,
Yesterday I had a talk with luc frabresse about using if then.
He said if I understand it right, Its the best to not using a if then or
a ifTrue/ifFalse.
Can anyone help me figure out how to rewrite this project so I will not
use the ifTrue in the basement function.
my code so far can be found here : https://github.com/RoelofWobben/AOC2015
Roelof
Richard O'Keefe
2018-11-28 08:01:15 UTC
Permalink
DRAT! What (genius negated) designed gmail's interface?

If I am wrong that the canHandleInput: class method of
IllegalMoveSanta should return false, not true (because
the order of the elements of #subclasses is not defined,
so that IllegalMoveSanta might *always* be selected),
then that is further evidence that avoiding "if" made
the code LESS readable.

You are a beginner at this, doing all the right things
for a beginner to do. One of the things you have to do
is to develop a sense of "smell" for code. Each class
ought to "pull its weight". Four classes -- which it
really does not make sense to instantiate, just put the
instance methods on the class side and drop the #new --
just to avoid a couple of very clear "ifs"? I don't
think so. IF THESE CLASSES NEEDED TO EXIST FOR SOME
OTHER REASON, if they had real work to do, sure. But
they don't. They only bloat and obscure the code.
Post by Richard O'Keefe
In Advent-of-Code 2015, the first problem is
really quite simple. There are at least two
ways to think about it.
set a counter to 0
for each character of the string
if it is '(' increment the counter
if it is ')' decrement the counter
report the counter
report the difference between
(the number of '(' characters in the string) and
(the number of ')' characters in the string).
Expressed in Smalltalk this looks something like
(s occurrencesOf: $() - (s occurrencesOf: $)); cr.
Make no mistake: you *cannot* tell the difference
between $( and $) using class-based dispatch because
they belong to the same class. There has to be an
"if" somewhere, the question is not whether but where.
In this case, counting the number of occurrences of an
object in a collection is has been a standard Collection
method for nearly 40 years; it's one of the basic
operations you need to learn.
The "higher level" approach can be less efficient that
the "CS101" approach, but in a case like this we really
do not care. We want the code to be *clear*.
What about the second part of the problem?
Not having submitted any answers, I can't actually
see the second part on the AOC site, but luckily you
have included it in your program.
We want to
[find the first place] where
[the cumulative sum] of (c=$()
[minus]
[the cumulative sum] of (c=$))
equals -1.
The "CS101" approach is
n := i := 0.
while n >= 0 and i < size(s) do
i +:= 1
if s[i] = $( then n := n + 1
if s[i] = $) then n := n - 1
report n
The "higher level" approach looks something like
n := (s cumCount: [:each | each = $(]) -
(s cumCount: [:each | each = $)])
indexOf: -1.
-- although it gives 0 instead of s size + 1 when
-1 is never reached.
Here #indexOf: is standard, #- is defined on sequences
in Squeak and Pharo, but #cumCount: does not
exist. So we need something like
cumCount: aBlock
|c a|
a := Array new: self size.
c := 0.
self withIndexDo: [:each :i |
(aBlock value: each) ifTrue: [c := c + 1].
a at: i put: c].
^a
This is *not* coupled to the particular use we have
in mind for it; it is in no way tied to characters
or strings. It's quite general.
Note: we do not need any new classes, except maybe
a place to put one problem-specific method.
While this answer, with no loop and no if in the
problem-specific code, is quite pretty, it has a
problem. Suppose the string to have M characters
and the desired step to be number K. The CS101
approach takes O(K) time and no allocations, but
the higher level approach takes O(M) time and
allocates three M-element Arrays. (In a non-strict
functional language like Haskell, the higher level
version *also* takes O(K) time, and with a good
enough "deforesting" compiler should allocate no
data structures.)
For a problem like this, I really don't care about
the efficiency aspect. If I *do* care about that,
then starting from a higher level version gives me
something to test a lower level version against.
To get an efficient answer to the second part,
we still don't need a new semantic class, just
some place to put the code.
Day1
indexOfFirstBasementTime: steps
|floor|
floor := 0.
steps keysAndValuesDo: [:i :each |
each = $( ifTrue: [floor := floor + 1].
each = $) ifTrue: [floor := floor - 1].
floor = -1 ifTrue: [^i]].
^0 "same convention as #indexOf:"
Does this contain "if"? Why yes, it does.
Is there any problem with that? Why no, there isn't.
You need to treat members of the same class (left
parenthesis, right parenthesis, others) differently.
You need to treat members of the same class (minus
one, all other integers) differently.
Would there be any gain in clarity or maintainability
if these ifs were somehow eliminated? Certainly NOT.
Quite the reverse, in fact.
input2 withIndexDo: [ :element :index | |action|
action:= SantaAction getActionFor: element. floor := action
doMovementFor: floor .
self hasReachedBasement
ifTrue: [^ index]].
^ '-2'.
There is only one word for this: obfuscated.
I was initially puzzled by your returning -2
instead of the conventional 0 if the basement
is not reached, and then *deeply confused* by
the fact that you are returning a *string* in
this case.
Looking at your code, I was further confused
by variables called 'aSymbol' whose value is
always and only a Character, never a Symbol.
And if I am wrong that
in Smalltalk is to return 0 when something is
approach
We want to find the first place where something
becomes true. There are again at least to approaches.
Post by Roelof Wobben
Hello,
Yesterday I had a talk with luc frabresse about using if then.
He said if I understand it right, Its the best to not using a if then or
a ifTrue/ifFalse.
Can anyone help me figure out how to rewrite this project so I will not
use the ifTrue in the basement function.
https://github.com/RoelofWobben/AOC2015
Roelof
Roelof Wobben
2018-11-28 08:05:45 UTC
Permalink
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<div class="moz-cite-prefix">Thanks for all the explanations. <br>
<br>
and yes, I have to get a feel when I need a new class and when
not. <br>
and when it's "allowed" to use a if then and when not. <br>
<br>
and if I understand the explanations I did not ask a class
something so a if then is <br>
allowed. <br>
<br>
Roelof<br>
<br>
<br>
<br>
Op 28-11-2018 om 09:01 schreef Richard O'Keefe:<br>
</div>
<blockquote type="cite"
cite="mid:CABcYAd+5dYkzfNiEYMJ_QqXMsWYVk9DNjD9pxd3R-***@mail.gmail.com">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<div dir="ltr">
<div class="gmail_default"
style="font-family:monospace,monospace">DRAT!  What (genius
negated) designed gmail's interface?</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">If I am wrong that the
canHandleInput: class method of</div>
<div class="gmail_default"
style="font-family:monospace,monospace">IllegalMoveSanta
should return false, not true (because</div>
<div class="gmail_default"
style="font-family:monospace,monospace">the order of the
elements of #subclasses is not defined,</div>
<div class="gmail_default"
style="font-family:monospace,monospace">so that
IllegalMoveSanta might *always* be selected),</div>
<div class="gmail_default"
style="font-family:monospace,monospace">then that is further
evidence that avoiding "if" made</div>
<div class="gmail_default"
style="font-family:monospace,monospace">the code LESS
readable.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">You are a beginner at
this, doing all the right things</div>
<div class="gmail_default"
style="font-family:monospace,monospace">for a beginner to do. 
One of the things you have to do</div>
<div class="gmail_default"
style="font-family:monospace,monospace">is to develop a sense
of "smell" for code.  Each class</div>
<div class="gmail_default"
style="font-family:monospace,monospace">ought to "pull its
weight".  Four classes -- which it</div>
<div class="gmail_default"
style="font-family:monospace,monospace">really does not make
sense to instantiate, just put the</div>
<div class="gmail_default"
style="font-family:monospace,monospace">instance methods on
the class side and drop the #new --</div>
<div class="gmail_default"
style="font-family:monospace,monospace">just to avoid a couple
of very clear "ifs"?  I don't</div>
<div class="gmail_default"
style="font-family:monospace,monospace">think so.  IF THESE
CLASSES NEEDED TO EXIST FOR SOME</div>
<div class="gmail_default"
style="font-family:monospace,monospace">OTHER REASON, if they
had real work to do, sure.  But</div>
<div class="gmail_default"
style="font-family:monospace,monospace">they don't.  They only
bloat and obscure the code.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr">On Wed, 28 Nov 2018 at 20:54, Richard O'Keefe
&lt;<a href="mailto:***@gmail.com" moz-do-not-send="true">***@gmail.com</a>&gt;
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;padding-left:1ex">
<div dir="ltr">
<div dir="ltr">
<div dir="ltr">
<div dir="ltr">
<div dir="ltr">
<div class="gmail_default"
style="font-family:monospace,monospace">In
Advent-of-Code 2015, the first problem is</div>
<div class="gmail_default"
style="font-family:monospace,monospace">really
quite simple.  There are at least two</div>
<div class="gmail_default"
style="font-family:monospace,monospace">ways to
think about it.</div>
<div class="gmail_default"
style="font-family:monospace,monospace">"CS101":</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   set a
counter to 0</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   for
each character of the string</div>
<div class="gmail_default"
style="font-family:monospace,monospace">     if it
is '(' increment the counter</div>
<div class="gmail_default"
style="font-family:monospace,monospace">     if it
is ')' decrement the counter</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   report
the counter</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">"Higher
level":</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   report
the difference between</div>
<div class="gmail_default"
style="font-family:monospace,monospace">     (the
number of '(' characters in the string) and</div>
<div class="gmail_default"
style="font-family:monospace,monospace">     (the
number of ')' characters in the string).</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">Expressed
in Smalltalk this looks something like</div>
<div class="gmail_default"
style="font-family:monospace,monospace"> 
Transcript print:</div>
<div class="gmail_default">
<div class="gmail_default"
style="font-family:monospace,monospace">    (s
occurrencesOf: $() - (s occurrencesOf: $)); cr.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">Make no
mistake: you *cannot* tell the difference</div>
<div class="gmail_default"
style="font-family:monospace,monospace">between
$( and $) using class-based dispatch because</div>
<div class="gmail_default"
style="font-family:monospace,monospace">they
belong to the same class.  There has to be an</div>
<div class="gmail_default"
style="font-family:monospace,monospace">"if"
somewhere, the question is not whether but
where.</div>
<div class="gmail_default"
style="font-family:monospace,monospace">In this
case, counting the number of occurrences of an</div>
<div class="gmail_default"
style="font-family:monospace,monospace">object
in a collection is has been a standard
Collection</div>
<div class="gmail_default"
style="font-family:monospace,monospace">method
for nearly 40 years; it's one of the basic</div>
<div class="gmail_default"
style="font-family:monospace,monospace">operations
you need to learn.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">The
"higher level" approach can be less efficient
that</div>
<div class="gmail_default"
style="font-family:monospace,monospace">the
"CS101" approach, but in a case like this we
really</div>
<div class="gmail_default"
style="font-family:monospace,monospace">do not
care.  We want the code to be *clear*.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">What
about the second part of the problem?</div>
<div class="gmail_default"
style="font-family:monospace,monospace">Not
having submitted any answers, I can't actually</div>
<div class="gmail_default"
style="font-family:monospace,monospace">see the
second part on the AOC site, but luckily you</div>
<div class="gmail_default"
style="font-family:monospace,monospace">have
included it in your program.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">We want
to</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   [find
the first place] where</div>
<div class="gmail_default"
style="font-family:monospace,monospace">     
[the cumulative sum] of (c=$()</div>
<div class="gmail_default"
style="font-family:monospace,monospace">     
[minus]</div>
<div class="gmail_default"
style="font-family:monospace,monospace">     
[the cumulative sum] of (c=$))</div>
<div class="gmail_default"
style="font-family:monospace,monospace">     
equals -1.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">The
"CS101" approach is</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   n :=
i := 0.</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   while
n &gt;= 0 and i &lt; size(s) do</div>
<div class="gmail_default"
style="font-family:monospace,monospace">     
i +:= 1</div>
<div class="gmail_default"
style="font-family:monospace,monospace">      if
s[i] = $( then n := n + 1</div>
<div class="gmail_default"
style="font-family:monospace,monospace">      if
s[i] = $) then n := n - 1</div>
<div class="gmail_default"
style="font-family:monospace,monospace"> 
 report n</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">The
"higher level" approach looks something like</div>
<div class="gmail_default">
<div class="gmail_default"
style="font-family:monospace,monospace">  n :=
(s cumCount: [:each | each = $(]) -</div>
<div class="gmail_default"
style="font-family:monospace,monospace">     
 (s cumCount: [:each | each = $)])</div>
<div class="gmail_default"
style="font-family:monospace,monospace">     
 indexOf: -1.</div>
<div class="gmail_default"
style="font-family:monospace,monospace">--
although it gives 0 instead of s size + 1 when</div>
<div class="gmail_default"
style="font-family:monospace,monospace">-1 is
never reached.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">Here
#indexOf: is standard, #- is defined on
sequences</div>
<div class="gmail_default"
style="font-family:monospace,monospace">in
Squeak and Pharo, but #cumCount: does not</div>
<div class="gmail_default"
style="font-family:monospace,monospace">exist. 
So we need something like</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default">
<div class="gmail_default"><font
face="monospace, monospace">    cumCount:
aBlock       </font></div>
<div class="gmail_default"><font
face="monospace, monospace">      |c a|   
   </font></div>
<div class="gmail_default"><font
face="monospace, monospace">      a :=
Array new: self size.</font></div>
<div class="gmail_default"><font
face="monospace, monospace">      c := 0.</font></div>
<div class="gmail_default"><font
face="monospace, monospace">      self
withIndexDo: [:each :i |</font></div>
<div class="gmail_default"><font
face="monospace, monospace">       
(aBlock value: each) ifTrue: [c := c + 1].</font></div>
<div class="gmail_default"><font
face="monospace, monospace">        a at:
i put: c].</font></div>
<div class="gmail_default"><font
face="monospace, monospace">      ^a</font></div>
<div class="gmail_default"
style="font-family:monospace,monospace">This
is *not* coupled to the particular use we
have</div>
<div class="gmail_default"
style="font-family:monospace,monospace">in
mind for it; it is in no way tied to
characters</div>
<div class="gmail_default"
style="font-family:monospace,monospace">or
strings.  It's quite general.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">Note:
we do not need any new classes, except maybe</div>
<div class="gmail_default"
style="font-family:monospace,monospace">a
place to put one problem-specific method.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">While
this answer, with no loop and no if in the</div>
<div class="gmail_default"
style="font-family:monospace,monospace">problem-specific
code, is quite pretty, it has a</div>
<div class="gmail_default"
style="font-family:monospace,monospace">problem. 
Suppose the string to have M characters</div>
<div class="gmail_default"
style="font-family:monospace,monospace">and
the desired step to be number K.  The CS101</div>
<div class="gmail_default"
style="font-family:monospace,monospace">approach
takes O(K) time and no allocations, but</div>
<div class="gmail_default"
style="font-family:monospace,monospace">the
higher level approach takes O(M) time and</div>
<div class="gmail_default"
style="font-family:monospace,monospace">allocates
three M-element Arrays.  (In a non-strict</div>
<div class="gmail_default"
style="font-family:monospace,monospace">functional
language like Haskell, the higher level</div>
<div class="gmail_default"
style="font-family:monospace,monospace">version
*also* takes O(K) time, and with a good</div>
<div class="gmail_default"
style="font-family:monospace,monospace">enough
"deforesting" compiler should allocate no</div>
<div class="gmail_default"
style="font-family:monospace,monospace">data
structures.)</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">For
a problem like this, I really don't care
about</div>
<div class="gmail_default"
style="font-family:monospace,monospace">the
efficiency aspect.  If I *do* care about
that,</div>
<div class="gmail_default"
style="font-family:monospace,monospace">then
starting from a higher level version gives
me</div>
<div class="gmail_default"
style="font-family:monospace,monospace">something
to test a lower level version against.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">To
get an efficient answer to the second part,</div>
<div class="gmail_default"
style="font-family:monospace,monospace">we
still don't need a new semantic class, just</div>
<div class="gmail_default"
style="font-family:monospace,monospace">some
place to put the code.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">Day1</div>
<div class="gmail_default"
style="font-family:monospace,monospace"> 
class methods:</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   
indexOfFirstBasementTime: steps</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   
  |floor|</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   
  floor := 0.</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   
  steps keysAndValuesDo: [:i :each |</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   
    each = $( ifTrue: [floor := floor + 1].</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   
    each = $) ifTrue: [floor := floor - 1].</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   
    floor = -1 ifTrue: [^i]].</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   
  ^0 "same convention as #indexOf:"</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">Does
this contain "if"?  Why yes, it does.</div>
<div class="gmail_default"
style="font-family:monospace,monospace">Is
there any problem with that?  Why no, there
isn't.</div>
<div class="gmail_default"
style="font-family:monospace,monospace">You
need to treat members of the same class
(left</div>
<div class="gmail_default"
style="font-family:monospace,monospace">parenthesis,
right parenthesis, others) differently.</div>
<div class="gmail_default"
style="font-family:monospace,monospace">You
need to treat members of the same class
(minus</div>
<div class="gmail_default"
style="font-family:monospace,monospace">one,
all other integers) differently.</div>
<div class="gmail_default"
style="font-family:monospace,monospace">Would
there be any gain in clarity or
maintainability</div>
<div class="gmail_default"
style="font-family:monospace,monospace">if
these ifs were somehow eliminated? 
Certainly NOT.</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">Quite
the reverse, in fact.</div>
<div class="gmail_default"
style="font-family:monospace,monospace">
<div class="gmail_default">input2
withIndexDo: [ :element :index | |action| </div>
<div class="gmail_default"><span style="white-space:pre-wrap"> </span>action:=
SantaAction getActionFor: element. floor
:= action  doMovementFor: floor .</div>
<div class="gmail_default"><span style="white-space:pre-wrap"> </span>self
hasReachedBasement </div>
<div class="gmail_default"><span style="white-space:pre-wrap"> </span>ifTrue:
[^ index]].</div>
<div class="gmail_default">    ^ '-2'.</div>
<div class="gmail_default"><br>
</div>
<div class="gmail_default">There is only one
word for this: obfuscated.</div>
<div class="gmail_default">I was initially
puzzled by your returning -2</div>
<div class="gmail_default">instead of the
conventional 0 if the basement</div>
<div class="gmail_default">is not reached,
and then *deeply confused* by</div>
<div class="gmail_default">the fact that you
are returning a *string* in</div>
<div class="gmail_default">this case.</div>
<div class="gmail_default"><br>
</div>
<div class="gmail_default">Looking at your
code, I was further confused</div>
<div class="gmail_default">by variables
called 'aSymbol' whose value is</div>
<div class="gmail_default">always and only a
Character, never a Symbol.</div>
<div class="gmail_default">And if I am wrong
that</div>
<div class="gmail_default">
<table
class="m_7790805574745581477gmail-highlight
m_7790805574745581477gmail-tab-size
m_7790805574745581477gmail-js-file-line-container"
style="box-sizing:border-box;border-collapse:collapse;border-spacing:0px;color:rgb(36,41,46);font-family:-apple-system,BlinkMacSystemFont,&quot;Segoe
UI&quot;,Helvetica,Arial,sans-serif,&quot;Apple Color
Emoji&quot;,&quot;Segoe UI
Emoji&quot;,&quot;Segoe UI
Symbol&quot;;font-size:14px"> <tbody style="box-sizing:border-box"> <tr style="box-sizing:border-box"> <td id="m_7790805574745581477gmail-LC7" class="m_7790805574745581477gmail-blob-code m_7790805574745581477gmail-blob-code-inner m_7790805574745581477gmail-js-file-line" style="box-sizing:border-box;padding:0px 10px;line-height:20px;vertical-align:top;font-family:SFMono-Regular,Consolas,&quot;Liberation Mono&quot;,Menlo,Courier,monospace;font-size:12px;overflow:visible;white-space:pre-wrap;word-wrap:normal">IllegalMoveSanta class&gt;&gt; canHandleInput:
</td>
</tr>
</tbody>
</table>
</div>
<div class="gmail_default">in Smalltalk is
to return 0 when something is</div>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">   
  </div>
<div class="gmail_default"
style="font-family:monospace,monospace">approach</div>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div style="font-family:monospace,monospace"><br>
</div>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace"><br>
</div>
<div class="gmail_default"
style="font-family:monospace,monospace">We want
to find the first place where something</div>
<div class="gmail_default"
style="font-family:monospace,monospace">becomes
true.  There are again at least to approaches. </div>
</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr">On Wed, 28 Nov 2018 at 05:41, Roelof
Wobben &lt;<a href="mailto:***@home.nl"
target="_blank" moz-do-not-send="true">***@home.nl</a>&gt;
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px
0px 0px 0.8ex;border-left:1px solid
rgb(204,204,204);padding-left:1ex">Hello,<br>
<br>
Yesterday I had a talk with luc frabresse about
using if then.<br>
He said if I understand it right, Its the best to
not using a if then or <br>
a ifTrue/ifFalse.<br>
<br>
Can anyone help me figure out how to rewrite this
project so I will not <br>
use the ifTrue in the basement function.<br>
<br>
my code so far can be found here : <a
href="https://github.com/RoelofWobben/AOC2015"
rel="noreferrer" target="_blank"
moz-do-not-send="true"><span
class="gmail_default"
style="font-family:monospace,monospace"></span>https://github.com/RoelofWobben/AOC2015</a><br>
<br>
Roelof<br>
<br>
<br>
</blockquote>
</div>
</div>
</div>
</div>
</div>
</blockquote>
</div>
</blockquote>
<br>
</body>
</html>

Loading...