Hello,
I hope I can discuss my approch to this problem : Given a number determine whether or not it is valid per the Luhn formula. The Luhn algorithm is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers and Canadian Social Insurance Numbers. The task is to check if a given string is valid. Validating a NumberStrings of length 1 or less are not valid. Spaces are allowed in the input, but they should be stripped before checking. All other non-digit characters are disallowed. Example 1: valid credit card number
The first step of the Luhn algorithm is to double every second digit, starting from the right. We will be doubling
If doubling the number results in a number greater than 9 then subtract 9 from the product. The results of our doubling:
Then sum all of the digits:
If the sum is evenly divisible by 10, then the number is valid. This number is valid!
my idea was to do these steps 1) reverse the input. 2) use this to double every second digit and calculate the sum
of all the numbers : checkNumber := (collection reverse selectwith index: [:item
:index | (index % 2 == 0) . IfTrue: [item *2]] ) sumNumbers 3) check if its a valid number by doing this : ^ (checkNumber % 10 == 0)
is this a good game plan or has it flaws or can I do it better ? Regards,
|
On Thu, 30 Apr 2020 at 14:33, Roelof Wobben via Pharo-users <[hidden email]> wrote:
I just want to check that before you are ask such questions, did you actually run this code? Because it would be fairly easy to see how close you are by evaluating it without #sumNumbers. i.e Please report what you see when evaluating this.... ``` collection := '8569 2478 0383 3437'. (collection reverse selectWithIndex: [:item
:index | (index % 2 == 0) ifTrue: [item *2]] ) inspect ```
That would work, but there is a more intention revealing method... #isDivisibleBy: cheers -ben |
Op 30-4-2020 om 10:31 schreef Ben Coman:
> collection := '8569 2478 0383 3437'. > (collection reverse selectWithIndex: [:item :index | (index % 2 == 0) > ifTrue: [item *2]] ) inspect I see a error and the non-even numbers are nill now. but after some figgeling this seems to work : collection := '8569247803833437'asArray. checkNumber := (collection reverse withIndexCollect: [:item :index | (index % 2 == 0) ifTrue: [item asInteger *2] ifFalse: [item asInteger]] ) sum inspect ^ checkNumber isDivisibleBy: 10 |
On Thu, 30 Apr 2020 at 16:46, Roelof Wobben via Pharo-users <[hidden email]> wrote: Op 30-4-2020 om 10:31 schreef Ben Coman: Great that you worked it out for yourself !!! "figgeling" is a very important part of programming.
That looks like it would work, but the inspect is redundant now you know how it works. An important take away from this is that when you are confused by what is happening, you need to LOOK at each spot you data is transformed, where "inspect" is your friend, particular running it from the debugger. -------------------------------- Now there is another path you might try, but first I want to stress that I didn't know this answer a minute ago - I only just discovered it !! So this answer is not based on something magic I knew
directly, but on my approach to guess, search and test within the system. I was considering how you were dealing with "every second digit" and considered a general description of this was "pairs". I _wondered_ whether Pharo had any methods dealing with "pairs" ? Spotter is a good place to ask this question, so I... Pressed <Shift-Enter> Typed... pair #i to see if there were any implementors containing the word "pair" and discovered SequenceableCollection>>#pairsCollect: 3. First time I've seen that method, I wonder how it works? The method comment gives a clue that the block takes two arguments. So lets try-the-simplest-possible-thing... ( '123456' pairsCollect: [ :a :b | a ] ) inspect ==> #($1 $3 $5) So that looks useful. Try experimenting with it yourself. cheers -ben |
Op 30-4-2020 om 10:57 schreef Ben
Coman:
I will do and till now I did not find a way to update the second one with it and keep the first one. It seems I will loose the first number and then I cannot use it to calculate the sum. Roelof |
In reply to this post by Pharo Smalltalk Users mailing list
It looks like a cool problem from where did you take it?
1 to: xx by: 2 do:
-------------------------------------------- Stéphane Ducasse 03 59 35 87 52 Assistant: Julie Jonas FAX 03 59 57 78 50 TEL 03 59 35 86 16 S. Ducasse - Inria 40, avenue Halley, Parc Scientifique de la Haute Borne, Bât.A, Park Plaza Villeneuve d'Ascq 59650 France |
Hello!. For this problem is possible to use Regular Expressions too.
https://ci.inria.fr/pharo-contribution/job/UpdatedPharoByExample/lastSuccessfulBuild/artifact/book-result/Regex/Regex.html Saludos, Pablo.
El 30 de abr. de 2020 10:11 -0300, Stéphane Ducasse <[hidden email]>, escribió:
|
In reply to this post by Stéphane Ducasse
Op 30-4-2020 om 15:10 schreef Stéphane
Ducasse:
The last problems I posted here are all from the exercism pharo track. |
In reply to this post by Stéphane Ducasse
Op 30-4-2020 om 15:10 schreef Stéphane
Ducasse:
Hmm, I looks like #do does not change the collection and I need to collection := '4539148803436467'. 1 to: (collection size + 1) by: 2 do: [:item | item asInteger * 2 ]. ^ collection |
In reply to this post by Pharo Smalltalk Users mailing list
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. But I am not at all sure that it is right for *you* at this stage. Your goal is to get practice in making code that is obviously correct and lends itself to testing. I think in your case it makes more sense to make the (A) choices. You can write some code that reverses a string (using the built-in method) and test that it does what you expect. You can write some code that checks whether a string contains only digits and spaces, and test that. You can write some code that returns a space-less copy, and test that. You can write some code that returns the even (odd) elements of a sequence, and test those. SequenceableCollection>> withIndexSelect: aBlock |index| index := 0. ^self select: [:each | aBlock value: each value: (index := index + 1)] evenElements ^self withIndexSelect: [:each :index | index even] oddElements ^self withIndexSelect: [:each :index | index odd] The easiest way to convert a string to numbers and add up the numbers is to use #detectSum, as in aString oddElements detectSum: [:char | char digitValue] How do you find #digitValue? By looking in Character. #detectSum:? Collection enumeration methods. Using the (A) approach will give you lots of little methods, which you can comment and above all TEST, so that each mistake will be in just one small method. This is a typical functional programming "lots of little functions" approach. On Thu, 30 Apr 2020 at 18:33, Roelof Wobben via Pharo-users <[hidden email]> wrote: > > Hello, > > I hope I can discuss my approch to this problem : > > Given a number determine whether or not it is valid per the Luhn formula. > > The Luhn algorithm is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers and Canadian Social Insurance Numbers. > > The task is to check if a given string is valid. > > Validating a Number > > Strings of length 1 or less are not valid. Spaces are allowed in the input, but they should be stripped before checking. All other non-digit characters are disallowed. > > Example 1: valid credit card number > > 4539 1488 0343 6467 > > The first step of the Luhn algorithm is to double every second digit, starting from the right. We will be doubling > > 4_3_ 1_8_ 0_4_ 6_6_ > > If doubling the number results in a number greater than 9 then subtract 9 from the product. The results of our doubling: > > 8569 2478 0383 3437 > > Then sum all of the digits: > > 8+5+6+9+2+4+7+8+0+3+8+3+3+4+3+7 = 80 > > If the sum is evenly divisible by 10, then the number is valid. This number is valid! > > > my idea was to do these steps > > 1) reverse the input. > > 2) use this to double every second digit and calculate the sum of all the numbers : > > checkNumber := (collection reverse selectwith index: [:item :index | (index % 2 == 0) . IfTrue: [item *2]] ) sumNumbers > > 3) check if its a valid number by doing this : > > > ^ (checkNumber % 10 == 0) > > > is this a good game plan or has it flaws or can I do it better ? > > Regards, > > > Roelof > > |
In reply to this post by pablo1n7
I'm sure you could fit regular expressions into the Luhn check,
but I am rather mystified as to what one could possibly gain by doing so. How do you do the equivalent of #(0 2 4 6 8 1 3 5 7 9) at: char digitValue + 1 in a regular expression and why would you want to? On Fri, 1 May 2020 at 01:31, Pablo Navarro <[hidden email]> wrote: > > Hello!. For this problem is possible to use Regular Expressions too. > > https://ci.inria.fr/pharo-contribution/job/UpdatedPharoByExample/lastSuccessfulBuild/artifact/book-result/Regex/Regex.html > > Saludos, Pablo. > El 30 de abr. de 2020 10:11 -0300, Stéphane Ducasse <[hidden email]>, escribió: > > It looks like a cool problem > from where did you take it? > > I hope I can discuss my approch to this problem : > > Given a number determine whether or not it is valid per the Luhn formula. > > The Luhn algorithm is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers and Canadian Social Insurance Numbers. > > The task is to check if a given string is valid. > > > > I like people that are always thinking in string as if a collection of number would not make it :) > > Validating a Number > > Strings of length 1 or less are not valid. Spaces are allowed in the input, but they should be stripped before checking. All other non-digit characters are disallowed. > > Example 1: valid credit card number > > 4539 1488 0343 6467 > > The first step of the Luhn algorithm is to double every second digit, starting from the right. We will be doubling > > 4_3_ 1_8_ 0_4_ 6_6_ > > If doubling the number results in a number greater than 9 then subtract 9 from the product. The results of our doubling: > > 8569 2478 0383 3437 > > Then sum all of the digits: > > 8+5+6+9+2+4+7+8+0+3+8+3+3+4+3+7 = 80 > > If the sum is evenly divisible by 10, then the number is valid. This number is valid! > > > my idea was to do these steps > > 1) reverse the input. > > 2) use this to double every second digit and calculate the sum of all the numbers : > > checkNumber := (collection reverse selectwith index: [:item :index | (index % 2 == 0) . IfTrue: [item *2]] ) sumNumbers > > > > you can also > 1 to: xx by: 2 do: > > 3) check if its a valid number by doing this : > > > ^ (checkNumber % 10 == 0) > > > is this a good game plan or has it flaws or can I do it better ? > > Regards, > > > Roelof > > > > > > -------------------------------------------- > Stéphane Ducasse > http://stephane.ducasse.free.fr / http://www.pharo.org > 03 59 35 87 52 > Assistant: Julie Jonas > FAX 03 59 57 78 50 > TEL 03 59 35 86 16 > S. Ducasse - Inria > 40, avenue Halley, > Parc Scientifique de la Haute Borne, Bât.A, Park Plaza > Villeneuve d'Ascq 59650 > France > |
In reply to this post by Richard O'Keefe
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 |
In reply to this post by Richard O'Keefe
Op 30-4-2020 om 16:16 schreef Roelof Wobben:
> nextIsOdd := true. > aString reverseDo: [:digit | > digit = Character space ifFalse: [ > nextIsOdd > ifTrue: [oddSum := ...] > ifFalse: [evenSum := ...]. > nextIsOdd := nextIsOdd not]]. hmm, Still no luck with this code : cardNumber := '4539 1488 0343 6467'. oddSum := 0. evenSum := 0. nextIsOdd := true. cardNumber reverseDo: [:digit | digit = Character space ifFalse: [ nextIsOdd ifTrue: [oddSum := oddSum + digit asInteger ] ifFalse: [evenSum := ((digit asInteger * 2) > 9) ifTrue: [evenSum + ((digit asInteger * 2) - 9) ] ifFalse: [ evenSum + (digit asInteger * 2) ]]. nextIsOdd := nextIsOdd not]]. ^ oddSum + evenSum the answer schould be 57 where I get 1157 So some debugging to do |
In reply to this post by Richard O'Keefe
I proposed as alternative to Luhn check. In special to validation problems like credit card number validation. El jue., 30 abr. 2020 11:11, Richard O'Keefe <[hidden email]> escribió: I'm sure you could fit regular expressions into the Luhn check, |
In reply to this post by Richard O'Keefe
and also not with this one :
cardNumber := '4539 1488 0343 6467'. oddSum := 0. evenSum := 0. nextIsOdd := false. cardNumber reverseDo: [:digit | digit = Character space ifFalse: [ nextIsOdd ifFalse: [oddSum := oddSum + (digit asString asInteger ) ] ifTrue: [(((digit asString asInteger) * 2) > 9) ifTrue: [evenSum := evenSum + ((digit asString asInteger) * 2) - 9 ] ifFalse: [ evenSum := evenSum + (digit asString asInteger) * 2 ]]. nextIsOdd := nextIsOdd not]]. ^ evenSum Op 30-4-2020 om 18:30 schreef Roelof Wobben: > Op 30-4-2020 om 16:16 schreef Roelof Wobben: >> nextIsOdd := true. >> aString reverseDo: [:digit | >> digit = Character space ifFalse: [ >> nextIsOdd >> ifTrue: [oddSum := ...] >> ifFalse: [evenSum := ...]. >> nextIsOdd := nextIsOdd not]]. > > hmm, > > Still no luck with this code : > > cardNumber := '4539 1488 0343 6467'. > oddSum := 0. > evenSum := 0. > nextIsOdd := true. > cardNumber reverseDo: [:digit | > digit = Character space ifFalse: [ > nextIsOdd > ifTrue: [oddSum := oddSum + digit asInteger ] > ifFalse: [evenSum := ((digit asInteger * 2) > 9) > ifTrue: [evenSum + ((digit asInteger * 2) - 9) ] > ifFalse: [ evenSum + (digit asInteger * 2) ]]. > nextIsOdd := nextIsOdd not]]. > ^ oddSum + evenSum > > the answer schould be 57 where I get 1157 > > So some debugging to do > |
Administrator
|
See below. On Thu, Apr 30, 2020 at 10:58 AM Roelof Wobben via Pharo-users <[hidden email]> wrote: and also not with this one : With the one change to use #digitValue instead of #asInteger (which answers the code point), the above example works. | evenSum oddSum | cardNumber := '4539 1488 0343 6467'. oddSum := evenSum := 0. nextIsOdd := true. cardNumber reverseDo: [:digit | digit = Character space ifFalse: [ nextIsOdd ifTrue: [oddSum := oddSum + digit digitValue ] ifFalse: [evenSum := ((digit digitValue * 2) > 9) ifTrue: [evenSum + ((digit digitValue * 2) - 9) ] ifFalse: [ evenSum + (digit digitValue * 2) ]]. nextIsOdd := nextIsOdd not]]. ^ oddSum + evenSum gives me 80 for the answer. A few (mostly) minor points: - I don't see why "reverse" is necessary. Of course if you use #do:, nextIsOdd needs to start with true. - repeating the "digit digitValue" or other variations adds clutter and reduces clarity -
((digit digitValue * 2) - 9) is not unreasonable, but could be simplified. What is the largest integer X such that 2xX<=9? > |
Op 30-4-2020 om 20:19 schreef Richard
Sargent:
Reverse is necessary because the * 2 needs to be all even index from the right. oke, can I then make a temp variable digit which holds the value and rename digit to character because it holding a character instead of a digit when x is 5 or larger then 2 times the value will be greater then 10. so the code will be then : |
evenSum oddSum |
cardNumber := '4539 1488 0343 6467'. oddSum := evenSum := 0. nextIsOdd := true. cardNumber reverseDo: [:digit |
character = Character space ifFalse: [
nextIsOdd
digit := digit digitValue. ifTrue: [oddSum := oddSum
+ digit ]
ifFalse: [evenSum := (digit >= 5 ) ifTrue: [evenSum + ((digit
* 2) - 9) ]
ifFalse: [ evenSum + (digit * 2) ]]. nextIsOdd := nextIsOdd not]].
^ oddSum + evenSum
isDivisibleBy: 10. |
In reply to this post by Richard O'Keefe
(oddSum + evenSum) dividedBy: 10
You previously had _ isDivisibleBy: 10 which certainly works. Squeak, Pharo, and ST/X have #isDivisibleBy: VisualWorks. Dolphin, and GNU Smalltalk do not. Here's the code from Number.st in ST/X. isDivisibleBy:aNumber "return true, if the receiver can be divided by the argument, aNumber without a remainder. Notice, that the result is only worth trusting, if the receiver is an integer." aNumber = 0 ifTrue: [^ false]. aNumber isInteger ifFalse: [^ false]. ^ (self \\ aNumber) = 0 The comment is wrong: the question makes sense for any combination of exact numbers. When, as in this case, aNumber is a literal integer, all #isDivisibleBy: really adds is overhead. (oddSum + evenSum) \\ 10 = 0 is quite clear, and completely portable. On Fri, 1 May 2020 at 02:16, Roelof Wobben <[hidden email]> 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 > |
Op 1-5-2020 om 02:51 schreef Richard O'Keefe:
> (oddSum + evenSum) dividedBy: 10 > > You previously had _ isDivisibleBy: 10 > which certainly works. > > Squeak, Pharo, and ST/X have #isDivisibleBy: > VisualWorks. Dolphin, and GNU Smalltalk do not. > > Here's the code from Number.st in ST/X. > isDivisibleBy:aNumber > "return true, if the receiver can be divided by the argument, > aNumber without a remainder. > Notice, that the result is only worth trusting, if the receiver > is an integer." > > aNumber = 0 ifTrue: [^ false]. > aNumber isInteger ifFalse: [^ false]. > ^ (self \\ aNumber) = 0 > > The comment is wrong: the question makes sense for any combination > of exact numbers. > When, as in this case, aNumber is a literal integer, all > #isDivisibleBy: really adds is overhead. > > (oddSum + evenSum) \\ 10 = 0 > > is quite clear, and completely portable. > > > > On Fri, 1 May 2020 at 02:16, Roelof Wobben <[hidden email]> 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. |
In reply to this post by Richard O'Keefe
Op 1-5-2020 om 08:35 schreef Roelof Wobben:
> Op 1-5-2020 om 02:51 schreef Richard O'Keefe: >> (oddSum + evenSum) dividedBy: 10 >> >> You previously had _ isDivisibleBy: 10 >> which certainly works. >> >> Squeak, Pharo, and ST/X have #isDivisibleBy: >> VisualWorks. Dolphin, and GNU Smalltalk do not. >> >> Here's the code from Number.st in ST/X. >> isDivisibleBy:aNumber >> "return true, if the receiver can be divided by the argument, >> aNumber without a remainder. >> Notice, that the result is only worth trusting, if the receiver >> is an integer." >> >> aNumber = 0 ifTrue: [^ false]. >> aNumber isInteger ifFalse: [^ false]. >> ^ (self \\ aNumber) = 0 >> >> The comment is wrong: the question makes sense for any combination >> of exact numbers. >> When, as in this case, aNumber is a literal integer, all >> #isDivisibleBy: really adds is overhead. >> >> (oddSum + evenSum) \\ 10 = 0 >> >> is quite clear, and completely portable. >> >> >> >> On Fri, 1 May 2020 at 02:16, Roelof Wobben <[hidden email]> 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. > > Roelof |
Free forum by Nabble | Edit this page |