--- Begin Message ---
Op 2-5-2020 om 19:33 schreef Ben Coman:


On Sat, 2 May 2020 at 15:52, Roelof Wobben via Pharo-users <pharo-users@lists.pharo.org> wrote:
Op 1-5-2020 om 08:35 schreef Roelof Wobben:
>> On Fri, 1 May 2020 at 02:16, Roelof Wobben <r.wob...@home.nl> wrote:
>>> Op 30-4-2020 om 16:06 schreef Richard O'Keefe:
>>>> This sounds very much like the Luhn test task at RosettaCode.
>>>> https://rosettacode.org/wiki/Luhn_test_of_credit_card_numbers
>>>> except that there it is described as working on the digits of an
>>>> integer.
>>>>
>>>> (1) There are two approaches to traversing a sequence in reverse.
>>>>       (A) Reverse the sequence, then traverse the copy forward.
>>>>           aString reverse do: [:each | ...]
>>>>       (B) Just traverse the sequence in reverse
>>>>           aString reverseDo: [:each | ...]
>>>>       My taste is for the second.
>>>>
>>>> (2) There are two approaches to deleting spaces.
>>>>       (A) Make a copy of the string without spaces.
>>>>           x := aString reject: [:each | each = Character space].
>>>>           x do: ...
>>>>       (B) Ignore spaces as you go:
>>>>           (i) aString do: [:each | each = Character space ifFalse:
>>>> [...]]
>>>>           (ii) aString select: [:each | each ~= Character space]
>>>> thenDo:
>>>> [:each | ...]
>>>>
>>>> Combining (1A) and (2A) you get very obvious code:
>>>>       (aString reject: [:each | each = Character space]) reverse do:
>>>> [:digit } ...]
>>>> Combining (1B) and (2Bi) you get more efficient code:
>>>>       aString reverseDo: [:digit |
>>>>           digit = Character space ifFalse: [ ...]]
>>>>
>>>> By the way, let's start by checking that the character in the
>>>> string *are*
>>>> digits or spaces:
>>>>       (aString allSatisfy: [:each | each isDigit or: [each =
>>>> Character s[ace]])
>>>>           ifFalse: [^false],
>>>>
>>>> (3) There are two approaches to doubling the even digits.
>>>>       (A) Make a new string that starts as a copy and change every
>>>> second
>>>>            digit from the right.
>>>>       (B) Simply *act* as if this has been done; keep track of
>>>> whether the
>>>>           current digit position is even or odd and multiply by 1
>>>> or 2 as
>>>>           appropriate.
>>>>       nextIsOdd := true.
>>>>       aString reverseDo: [:digit |
>>>>           digit = Character space ifFalse: [
>>>>           nextIsOdd
>>>>               ifTrue:  [oddSum := ...]
>>>>               ifFalse: [evenSum := ...].
>>>>           nextIsOdd := nextIsOdd not]].
>>>>
>>>> I *like* code that traverses a data structure exactly once and
>>>> allocates no intermediate garbage, so I'd be making (B) choices.
>>>>
>>>>
>>> For me  , I use this to practice solving problems  and doing the
>>> "right"
>>> steps.
>>> So I love it , that so many people share there way of solving it.
>>> I can learn a lot from it
>>> Expecially when they explain there thinking process so detailed.
>>>
>>> I like this code also a lot.
>>> Am  I correct for testing if it is a valid string by doing this ^
>>> (oddSum + evenSum) dividedBy: 10
>>>
>>> Roelof
>>>
>
>
> oke,
>
> so this is better
>
> cardNumber := '8273 1232 7352 0569'.
> oddSum := 0.
> evenSum := 0.
> nextIsOdd := false.
>      cardNumber reverseDo: [:character |
>           digit := character digitValue.
>          character = Character space ifFalse: [
>          nextIsOdd
>              ifFalse:  [oddSum := oddSum + digit ]
>              ifTrue: [(digit >= 5 )
>     ifTrue: [evenSum := evenSum + (digit * 2) - 9 ]
>     ifFalse: [ evenSum := evenSum + (digit * 2) ]].
>             nextIsOdd := nextIsOdd not]].
> ^ evenSum + oddSum // 10 == 0.
>
>
> where I could even make a seperate method of the ifTrue branch when
> the digit is greater then 5.
>
>
nobody who can say if this is a good solution ?

You didn't actually ask a question :)

btw, Its good you are stretching yourself to do it the most complicated way, 
but pay attention that when Richard says HE "likes code that traverses a data structure exactly once"
its because he is starting with the philosophy to OPTIMISE for EFFICIENCY.
Often in programming that is the LAST thing you should do because to do so your code often becomes more complex,
and Kernagan's law applies...

In general, you want to avoid premature optimization.

The catch-phrase I see around this forum priorities things in this order:
1. Make it work.
2. Make it right.
3. Make it fast. 
and often you can stop at 2 because it is already "fast enough".

Considering your code above, if I take TIME to go through it, I can evaluate that it seems right 
but I remain feeling that I could have missed something.

Something like Richard's (A) examples without loops  I would consider to be "more good".
since each line can be dealt with individually.

cardNumber := '4539 1488 0343 6467'.
cleanDigits := cardNumber copyWithout: Character space.
preWrapDigits := cleanDigits asArray collectWithIndex: [ :char :idx | (idx\\2 + 1) * char digitValue ].
wrappedDigits :=  preWrapDigits collect: [ :d | d > 9 ifFalse: [ d ] ifTrue: [ d-9 ] ].
^ wrappedDigits sum isDivisibleBy: 10.  

A side benefit is having full result of each transformation available to be reviewed,
rather than having only pieces of it in view when you debug a loop.

In the end though, yours is a good solution first and foremost because tried a more detailed way and you got it working!!
Remember there is more than one "good way" to do it depending on what you are optimising for - efficiency or readability.  

cheers -ben
 

I think I find readability more important then efficieny.

But I think I have to alter your code. At first look it does not look like that every second value from the right is multiplied by 2.

When the index is for example 7 .

7 // 2 gives  3.
Maybe it needs to be %.

Roelof

--- End Message ---

Reply via email to