On Sun., 3 May 2020, 3:48 pm Roelof Wobben, <r.wob...@home.nl> wrote:
> Op 3-5-2020 om 04:24 schreef Ben Coman: > > > > On Sun, 3 May 2020 at 03:09, Roelof Wobben <r.wob...@home.nl> wrote: > >> 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... >> https://talixa.com/blog/why-i-write-simple-code/ >> >> In general, you want to avoid premature optimization. >> https://effectiviology.com/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 %. >> > > But why are you testing // when my code uses \\ ? :) > small details can have a big impact. > > cheers -ben > > > Chips, but then it does exactly the other way around that I wanted. > > 7 is a non-even index 7 \\ 2 gives 1 + 1 gives 2 so the number gets > multiplied by 2 where its must be multiplied by 1. > 8 is a even index 8 \\ 2 gives 0 + 1 gives 1 so the number gets > muliplied by 1 where it must be multiplied by 2. > Sorry, I worked from the example of your original post. >>> 4539 1488 0343 6467 >>> 4_3_ 1_8_ 0_4_ 6_6_ >>> 8569 2478 0383 3437 >>> 8+5+6+9+2+4+7+8+0+3+8+3+3+4+3+7 = 80 where it looked like the odd indexes needed doubling and I was getting a checksum of 80 so that seemed correct. If you want to double the even indexes try (2 - (idx\\2)). cheers -ben