Hello,
Im trying to solve a challenge from exercism where I have to calculate the points somehow gets on a very simple darts board. I solved it like this : scoreX: anInteger y: anInteger2 | distance | distance := (anInteger squared + anInteger2 squared) sqrt. distance > 10 ifTrue: [ ^ 0 ]. distance > 5 ifTrue: [ ^ 1 ]. distance > 1 ifTrue: [ ^ 5 ]. ^ 10 but now I use three if then and I think it's ugly code. Is there a way I can make it more the smalltalk way ? Regards, Roelof |
You might try Magnitude >> between: min and: max
-- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html |
Thanks,
But it looks to me I still need the ifTrue because the between gives also a true or false. Roelof Op 27-12-2019 om 20:38 schreef tbrunz: > You might try Magnitude >> between: min and: max > > > > > -- > Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html > > |
In reply to this post by Pharo Smalltalk Users mailing list
Hi First, I think your code is readable and easy to understand. I think the problem does not lend itself too much towards having “an object oriented solution”, as such. If you are looking for a one-liner using smalltalk libraries, you could do: ({10 -> 0. 5 -> 1. 1 -> 5. 0 -> 10} detect: [ :dist | dist key < distance ]) value but I really think your code is easier to read, which is what I appreciate the most. Kasper
On 27 December 2019 at 20.18.38, Roelof Wobben via Pharo-users ([hidden email]) wrote:
|
Hello Kasper
Thanks for the feedback. I also like readable and easy to understand code but I get the feeling that it;s good practice to use as minimal as possible if then's So I wonder if and how I can achieve that here. Roelof Op 27-12-2019 om 20:47 schreef Kasper Østerbye:
|
In reply to this post by Pharo Smalltalk Users mailing list
I would probably set up an array of associations whose keys are the boundary
limits and whose values are the target values (ordered, in this example, from outer to inner). Then loop over the array's associations using inject:into:, testing for the point where (each < key), at which point you return the previous association's value. -t -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html |
Doing it the way I describe above eliminates repetitive if-then statements
('switch' or 'case' statements in other languages, which make for a maintenance mess), automatically extends for additional rings, doesn't embed "magic numbers" for defined rings within the decision logic, and takes advantage of the naturally recursive nature of this kind of graduated comparison test. -t -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html |
You could also accomplish this with a simple 'do:' construct by creating the
associations with the the ring limits & points "staggered" (by one level), so that when (each < key) you can return the 'current' association value... Which, now that I'm looking closely at your solution, Kasper, seems to be what you already proposed... Either should work. -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html |
In reply to this post by Pharo Smalltalk Users mailing list
Myself, I see only one issue in your code, and that is the pointless
use of sqrt. scoreX: anInteger y: anInteger2 | radiusSquared | radiusSquared := anInteger squared + anInteger2 squared. radiusSquared > 100 ifTrue: [ ^ 0 ]. radiusSquared > 25 ifTrue: [ ^ 1 ]. radiusSquared > 1 ifTrue: [ ^ 5 ]. ^ 10 Make that two issues. The names anInteger and anInteger2. How about scoreHorizontal: horizontal vertical: vertical ... The OO dogma about "if' that you refer to is about not using "if" to make decisions based on TYPE. Making decisions based on NUMERIC RANGES is perfectly OK. You could introduce a 'HalfOpenRangeDictionary" class [K1,K2) -> V1 [K2,K3) -> V2 ... [Kn,infinity) -> Vn But "You Ain't Gonna Need It", so don't. On Sat, 28 Dec 2019 at 08:18, Roelof Wobben via Pharo-users <[hidden email]> wrote: > > Hello, > > Im trying to solve a challenge from exercism where I have to calculate the points somehow gets on a very simple darts board. > > I solved it like this : > > > scoreX: anInteger y: anInteger2 > | distance | > distance := (anInteger squared + anInteger2 squared) sqrt. > distance > 10 > ifTrue: [ ^ 0 ]. > distance > 5 > ifTrue: [ ^ 1 ]. > distance > 1 > ifTrue: [ ^ 5 ]. > ^ 10 > > > but now I use three if then and I think it's ugly code. > > Is there a way I can make it more the smalltalk way ? > > > Regards, > > Roelof > |
Hello Richard,
Thanks for the feedback. I was using it because the challenge was talking about it, that yu have the use the formula a ^2 + b ^2 = c ^2 and the challenge was talking about the distance and not the distance squared. I think a better name schould be then x or xCoordinate and y or Y_Coordinate. Roelof Op 27-12-2019 om 22:48 schreef Richard O'Keefe: > Myself, I see only one issue in your code, and that is the pointless > use of sqrt. > scoreX: anInteger y: anInteger2 > | radiusSquared | > radiusSquared := anInteger squared + anInteger2 squared. > radiusSquared > 100 > ifTrue: [ ^ 0 ]. > radiusSquared > 25 > ifTrue: [ ^ 1 ]. > radiusSquared > 1 > ifTrue: [ ^ 5 ]. > ^ 10 > > Make that two issues. The names anInteger and anInteger2. > How about > scoreHorizontal: horizontal vertical: vertical > ... > > The OO dogma about "if' that you refer to is about not using > "if" to make decisions based on TYPE. Making decisions based > on NUMERIC RANGES is perfectly OK. > > You could introduce a 'HalfOpenRangeDictionary" class > [K1,K2) -> V1 > [K2,K3) -> V2 > ... > [Kn,infinity) -> Vn > But "You Ain't Gonna Need It", so don't. > > On Sat, 28 Dec 2019 at 08:18, Roelof Wobben via Pharo-users > <[hidden email]> wrote: >> Hello, >> >> Im trying to solve a challenge from exercism where I have to calculate the points somehow gets on a very simple darts board. >> >> I solved it like this : >> >> >> scoreX: anInteger y: anInteger2 >> | distance | >> distance := (anInteger squared + anInteger2 squared) sqrt. >> distance > 10 >> ifTrue: [ ^ 0 ]. >> distance > 5 >> ifTrue: [ ^ 1 ]. >> distance > 1 >> ifTrue: [ ^ 5 ]. >> ^ 10 >> >> >> but now I use three if then and I think it's ugly code. >> >> Is there a way I can make it more the smalltalk way ? >> >> >> Regards, >> >> Roelof >> |
In reply to this post by Pharo Smalltalk Users mailing list
It would be an overkill to do it for this particular case, but Smalltalk makes it possible to implement a case-like construction: [ expression ] when: [ :value | condition1 ] do: [-0do :value | ... ]; when: [ :value | condition2 ] do: [ :value | ... ]; otherwiseDo: [ :value | ... ]; evaluate I am sure, something like this has been implemented already somewhere (maybe in Squeak?). Still not sure it is practical as compared to simple if-s, and for sure not widely used :) ...On the other hand, sometimes the case-like construction can be considered a more intension-revealing style. пт, 27 дек. 2019 г., 22:18 Roelof Wobben via Pharo-users <[hidden email]>:
|
The “Kent Beck approved solutions” here are two:
1) To use a dictionary (or ordered dictionary). case := OrderedDictionary newFromPairs: { [ cond ]. [ do ]. [ cond ]. [ do ]. [ true ]. [ otherwise do ] } case keysAndValuesDo: [ :cond :do | (cond value: expression) ifTrue: [ “you need to break here" ^ do value ]. 2) To use a “case method” caseMethod: expression expression = cond1 ifTrue: [ ^ do1 ]. expression = cond2 ifTrue: [ ^ do2 ]. ^ otherwise Personally, I prefer the second one because is more concise, performant and easy to understand. Esteban
|
Administrator
|
In reply to this post by Pharo Smalltalk Users mailing list
Pharo Smalltalk Users mailing list wrote
> Hello, > > Im trying to solve a challenge from exercism where I have to > calculate the points somehow gets on a very simple darts board. > > I solved it like this : > > > scoreX: anInteger y: anInteger2 > | distance | > distance := (anInteger squared + anInteger2 squared) sqrt. > distance > 10 > ifTrue: [ ^ 0 ]. > distance > 5 > ifTrue: [ ^ 1 ]. > distance > 1 > ifTrue: [ ^ 5 ]. > ^ 10 > > > but now I use three if then and I think it's ugly code. > > Is there a way I can make it more the smalltalk way ? Roelof, this example is too simple to "really do it the Smalltalk way". In a more realistic scenario, you would have classes for each "rule" with corresponding differences in behaviour. In the above example, you could get away with a single class with two instance variables and configure three instances of it appropriately. In more complex scenarios, your start up code instantiates the various rules, with precedence determined by the order of the rule instances. Then when you need to determine which rule to apply, you use #detect:ifNone: to find the rule that applies with the #ifNone: block providing the fallback result. Sometimes, you work with classes themselves to determine which class to use, but I have described working with instances for the general concept. (After all, classes are instances.) Using rules with a #select: allows you to handle a scenario where multiple rules apply. A good example is rules to provide the default coverages for an auto insurance policy, where each possible coverage can have one or more rules to assess the many characteristics of the applicants and so determine the one rule that applies for each possible coverage. (And yes, it is reasonable that a possible coverage has no rule applicable, in which case the coverage is not included in the default offering.) As mentioned, you /could/ apply this to your scenario, as an exercise. You might be tempted to utilize an existing class with two instance variables. That would be a bad design practice, but reasonable for a /disposable/ experiment to quickly study how it works. Always (create and) use classes which truly reflect their purpose and have named properties which truly reflect the objects they hold. In your scenario, #distance and #score seem appropriate. > > Regards, > > Roelof -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html |
In reply to this post by Pharo Smalltalk Users mailing list
What Smalltalk has for full-sized pattern matching?
Is it possible to make in message-only syntax? -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html |
In reply to this post by Dennis Schetinin
Start with
scoreX: anInteger y: anInteger2 | distance | distance := (anInteger squared + anInteger2 squared) sqrt. distance > 10 ifTrue: [ ^ 0 ]. distance > 5 ifTrue: [ ^ 1 ]. distance > 1 ifTrue: [ ^ 5 ]. ^ 10 (1) Use better argument names. (2) Forget about the square root. (3) Note the repetitive pattern. So scoreX: x y: y | distanceSquared | distanceSquared := x squared + y squared. #((100 0) (25 1) (1 5)) do: [:each | distanceSquared > each first ifTrue: [^each last]]. ^10 But I'd say that apart from the argument names and pointless square root, your original code is about as clear as it gets. The "ifs stink" dogma is about *type* testing, not *range* testing, and this is range testing. It is also a tiny piece of code which can only be made *less* readable by trying to avoid the ifs. Remember the slogan YAGNI? You Ain't Gonna Need it? The time to go for a more elaborate solution is when you HAVE to. Do The Simplest Thing That Could Possibly Work. On Sat, 28 Dec 2019 at 23:59, Dennis Schetinin <[hidden email]> wrote: > > It would be an overkill to do it for this particular case, but Smalltalk makes it possible to implement a case-like construction: > > [ expression ] > when: [ :value | condition1 ] do: [-0do :value | ... ]; > when: [ :value | condition2 ] do: [ :value | ... ]; > otherwiseDo: [ :value | ... ]; > evaluate > > > I am sure, something like this has been implemented already somewhere (maybe in Squeak?). Still not sure it is practical as compared to simple if-s, and for sure not widely used :) > ...On the other hand, sometimes the case-like construction can be considered a more intension-revealing style. > > пт, 27 дек. 2019 г., 22:18 Roelof Wobben via Pharo-users <[hidden email]>: >> >> Hello, >> >> Im trying to solve a challenge from exercism where I have to calculate the points somehow gets on a very simple darts board. >> >> I solved it like this : >> >> >> scoreX: anInteger y: anInteger2 >> | distance | >> distance := (anInteger squared + anInteger2 squared) sqrt. >> distance > 10 >> ifTrue: [ ^ 0 ]. >> distance > 5 >> ifTrue: [ ^ 1 ]. >> distance > 1 >> ifTrue: [ ^ 5 ]. >> ^ 10 >> >> >> but now I use three if then and I think it's ugly code. >> >> Is there a way I can make it more the smalltalk way ? >> >> >> Regards, >> >> Roelof >> |
Free forum by Nabble | Edit this page |