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