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