Raindrops puzzle on Exercism

Craig Maloney - Tue, 08/15/2017 - 11:28

One of the puzzles on Exercism is the Raindrops Puzzle. In this puzzle you find the numbers that divide into the original number (aka: factors). An example of this is the number 28 where 28 can be divided by the following numbers: 1, 2, 4, 7, 14, 28. (Remember that 1 divides into all numbers, and all numbers are divisible by themselves).

The challenge is to output one of the following:

  • If 3 is one of the numbers that are divisible by the original number then display "Pling"
  • If 5 is one of the numbers that are divisible by the original number then display "Plang"
  • If 7 is one of the numbers that are divisible by the original number then display "Plong"
  • If the number is not divisible by any of the above display the original number.

So in the case of 4 we factor that (1, 2, 4) and see that 3, 5, or 7 are not in those factors and display 4. For a number like 5 we note that (1, 5) are its factors and since 5 is in that set of factors we display "Plang". In the 28 example above we note that 7 is one of the numbers, and display "Plong".

Where it gets interesting is a number like 15. 15's factors are (1, 3, 5, 15). In this case we first check if the number 3 is one of the factors. 3 is present in the factors so we display "Pling". But note too that 5 is also one of the factors, so we also need to display "Plang". The correct output is "PlingPlang", and we display "PlingPlang" instead of the number 15.

With that in mind I started to attack the problem. This problem was part of the Scheme track so I started thinking how I'd approach this.

The first part was getting the factors out. That suggested "recursion" but my recursion is rusty. I knew that I could easily blow the stack with larger numbers so it would need to be tail-recursive. And that's about where my brain said "do you know how to write a tail-recursive routine because I sure don't?". So I checked Google and found a few examples (one of which lead me down figuring out how to use let to create a loop, so that was interesting).

Just for grins I wrote an iterative version in Python:

def factor(n): factors = [] for i in range(1, n+1): if (n % i == 0): factors.append(i) return factors def main(): print(factor(28)) print(factor(34)) if __name__ == "__main__": main()

Hmm, maybe I don't need recursion after all. This seems to be quick and does the trick. But is that Scheme? Scheme tends to favor recursion over looping (in my experience) so I needed a different approach, and with code that I could ultimately understand.

I found a Prime Decomposition in Scheme on Rosetta Code:

(define (factor number) (define (*factor divisor number) (if (> divisor number) (list number) (if (= (modulo number divisor) 0) (cons divisor (*factor divisor (/ number divisor))) (*factor (+ divisor 1) number)))) (*factor 2 number)) (display (factor 28)) (newline)

Ah, that sort of does what I want. I played with it a bit and came up with my own factorization algorithm / program:

(define (factor number) (define (*factor divisor number) (if (>= divisor number) (list number) (if (= (remainder number divisor) 0) (cons divisor (*factor (+ 1 divisor) number )) (*factor (+ divisor 1) number)))) (*factor 1 number)) (display (factor 28)) (newline)

They may look similar but the key difference is in the (cons divisor (*factor (+ 1 divisor) number )) line (and the (*factor 1 number) instead of 2 line). The original algorithm cut the space for searching in half (which is great if you're looking for primes). But in the case of 28 I wanted 2 * 14 = 28. I wanted 4 * 7 = 28 (7 being one of the factors that causes "Plong" to occur). Instead I got '(2 2 7 1) which sort-of-works, but isn't what I wanted. With the re-worked algorithm I got what I wanted:'(1 2 4 7 14 28).

Next came learning how to append strings in Scheme. That wasn't nearly as difficult as I thought it would be ((set! outstring (string-append outstring "Foo")), but what was slightly non-obvious to me was determining if an element is in the list.

Scheme has a function called memq which will find an element and return the rest of the list. So if I have a list '(1 2 3 4 5) and I want to see if 4 is in that list I can use (memq 4 '(1 2 3 4 5)) and get '(4 5) as the result. If I do the same for 6 ((memq 6 '(1 2 3 4 5))) I get back #f. In Scheme the presence of a list can be tested using if, so checking if we have a list or don't becomes (if (memq 3 factors) ...).

Appending a string in

Here's the completed code (and a link to comment on Exercism):

(define-module (raindrops) #:export (convert)) (define (factor number) (define (*factor divisor number) (if (>= divisor number) (list number) (if (= (remainder number divisor) 0) (cons divisor (*factor (+ 1 divisor) number )) (*factor (+ divisor 1) number)))) (*factor 1 number)) (define (convert number) (let ((outstring "") (factors (factor number))) (if (memq 3 factors) (set! outstring (string-append outstring "Pling"))) (if (memq 5 factors) (set! outstring (string-append outstring "Plang"))) (if (memq 7 factors) (set! outstring (string-append outstring "Plong"))) (if (string=? outstring "") (number->string number) outstring ) ) )
Categories: LugNut Blogs

100 day challenge: Final results

Craig Maloney - Sat, 08/12/2017 - 08:48

In the spirit of the 100 day challenge I wrote a quick program to parse the tags of the 100day challenge and note which tags were most often used.

#!/usr/bin/env python from collections import Counter def main(): tag_counter = Counter() with open('100day_tags', 'rt') as infile: for line in infile.readlines(): line = line.strip().lower() (filename, spacer, tag_string) = line.split(':') tags = [x.strip() for x in tag_string.split(',')] for tag in tags: tag_counter[tag] += 1 del tag_counter['a day in the life'] del tag_counter['100day'] for i in tag_counter.most_common(): print("{tag}: {counter}".format( tag=i[0], counter=i[1])) if __name__ == '__main__': main()

The tags are from a simple grep 100day *.md > ~/100day_tags command in my Pelican directory.

programming: 39 scheme: 29 racket: 16 javascript: 10 python: 5 godot: 3 guile: 3 pyohio: 2 css: 2 c: 1 html: 1 html5: 1

It looks like Scheme / Racket were the ones that got the most attention. After that is JavaScript, then some Python and a few mentions of Godot.

Not sure if this means anything in particular but it's interesting to me.

Categories: LugNut Blogs

Day 70ish/100: Fin?

Craig Maloney - Thu, 08/10/2017 - 22:30

Apparently this is the second time that I can't count, and have double-counted days.

It's also another day where "life has become rather chaotic and programming was the last thing on my mind".

So I'm thinking about calling this done for now. I'm not planning on abandoning daily programming for now. But putting it as part of the daily challenge? That's becoming a bit much.

That and I'm feeling guilty about not blogging about other things that are happening.

So, it's done for now. Won't say this is the last time this will happen, but frankly I think it's run its course.

Categories: LugNut Blogs

Day 69/100: More reading / more Exercism / Hacker Rank

Craig Maloney - Wed, 08/09/2017 - 22:28

Worked a little on Exercism today. One of the problems I was working on required the use of factoring a number to find all of the divisors (eg: 4 can be divided by 4, 2, and 1). It's a recursive solution but I didn't know offhand how to write it in Scheme (shameface) and looked one up online. That got me down a rabbithole of trying to figure out what the solution I found was doing. That lead to trying to figure out how the debugger in Guile worked, which lead me to try the debugger in DrRacket instead (pro-tip: The IDE debugger in Racket is quite good.).

I also played around a bit with HackerRank. Expect a blog post on why I think HackerRank has its heart in the right place but the implementation needs a lot of work.

Categories: LugNut Blogs

Day 68/100: Reading / Exercism

Craig Maloney - Tue, 08/08/2017 - 22:23

Did a quick play around with Exercism and some reading.

Here's my solution for RNA Transcription in Python:

def to_rna(dna): dna_rna_trans = {'G': 'C', 'C': 'G', 'T': 'A', 'A': 'U'} rna_strand = [] dna_list = list(dna) for dna_nucleotide in dna_list: if dna_nucleotide not in dna_rna_trans: return '' else: rna_strand.append(dna_rna_trans[dna_nucleotide]) return ''.join(rna_strand)
Categories: LugNut Blogs

Day 68/100: Off my game / Reading

Craig Maloney - Mon, 08/07/2017 - 23:23

Today I was pretty well off my game. Did a lot of administrative work for MUG and what-not, but didn't get to doing much programming. Hoping to rectify this tomorrow (before the MUG meeting. :) )

Did a little reading here and there. Did a little reading of the Guile Manual. Still love this manual.

Categories: LugNut Blogs

Day 67/100: ...

Craig Maloney - Sun, 08/06/2017 - 23:34


(Yes, not a lot happened today).

Categories: LugNut Blogs

Day 66/100: Just some reading

Craig Maloney - Sat, 08/05/2017 - 23:03

Did some more reading, but not a whole lot else.

Categories: LugNut Blogs

Day 65/100: More Godot

Craig Maloney - Fri, 08/04/2017 - 22:31

Today I watched a few introductory videos to learn more about Godot. The more I look at this engine the more I'm falling in love with it. I can't wait to put together and release a game with it. Thinking about doing a Kaboom-style game with it.

Categories: LugNut Blogs

Day 64/100: Pangram

Craig Maloney - Thu, 08/03/2017 - 22:12

Today I worked on the Pangram solution for Exercism. Didn't quite get an hour in programming today but close enough.

Categories: LugNut Blogs

Day 63/100: Off-by-one error?

Craig Maloney - Wed, 08/02/2017 - 23:26

I think I managed to have two day 59s. Not sure if this means something is screwed up but whatever. I'm willing to call it good if you are. ;)

Today I worked on the Exercism problem called "Bob". This was a hard problem for me but I think I managed it well.

Here's a link to my solution to the Bob problem.

Categories: LugNut Blogs

Day 62/100: Working on other things

Craig Maloney - Tue, 08/01/2017 - 23:06

Today I had a few other things to work on, so I didn't get to programming. More to come.

Categories: LugNut Blogs

Day 61/100: Getting back into the swing

Craig Maloney - Tue, 08/01/2017 - 09:27

Day 61 was dedicated to catching up from the weekend. Now I need to get back into the swing of coding again. PyOhio is a great motivator to get the programming juices flowing again.

Categories: LugNut Blogs

Day 60/100: Decompression

Craig Maloney - Tue, 08/01/2017 - 09:25

Day 60 was traveling and decompression.

Categories: LugNut Blogs

PyOhio: Introduction to Debugging in Python Presentation

Craig Maloney - Mon, 07/31/2017 - 16:43

It looks like the video for my PyOhio presentation "Introduction to Debugging on Python" is up.

You can follow along with the slides and the sample code.

Hope you enjoy!

Categories: LugNut Blogs

Day 59/100: PyOhio Presenting

Craig Maloney - Sun, 07/30/2017 - 21:48

I gave a presentation at PyOhio today about the Python Debugger (here is a link to the slides). Had a great time, and am looking forward to the next PyOhio already.

Not a lot of "proper" programming today, but looking forward to getting back to it tomorrow.

Categories: LugNut Blogs

Day 59/100: PyOhio fun

Craig Maloney - Sun, 07/30/2017 - 11:54

Spent the day at PyOhio. Lots of interesting talks to digest. More later when I have some time to decompress.

Categories: LugNut Blogs

Day 58/100: Nothing to report

Craig Maloney - Fri, 07/28/2017 - 23:01

Most of today was spent on the road, so no progress to report.

Categories: LugNut Blogs

Day 57/100: More Exercism, but not much else

Craig Maloney - Thu, 07/27/2017 - 23:09

Worked a little more on Exercism problems, but for the most part today was taken up with other stuff. Not sure if tomorrow will do much better but we'll see.

Categories: LugNut Blogs

Day 56/100: Leap years in scheme, Guile code, and The Guile Manual

Craig Maloney - Wed, 07/26/2017 - 23:09

Today I did the Exercism exercise for Leap Years in Scheme. It was essentially a port of the Python code I wrote, but in Scheme. If you want to see it LMK.

Today I got curious about how Guile works. Chris Webber was asking questions in the #guile channel about some networking implementation details in Guile. I was curious what he was asking and wondered if I could play along at home. Sadly I am pretty much out of my depth when it comes to C code, so I quickly bounced off of his issue. But it got me curious enough to wonder what goes on under the covers of Guile.

One thing I have found incredibly pleasant with Guile is the manual. Good manuals explain how to use the software. Great manuals encourage you to understand the software and the implementation details. What the Guile manual does is not only explain the REPL (the interface you get when you type guile) and the API (how you can incorporate Guile into your C programs), but it also explains how things are implemented underneath. It encourages a deeper understanding of what is going on at the C code level. And it doesn't just cover what is there but also what you could try and why that might not do what you'd expect. In brief it is like having an expert programmer guide you through design decisions as though you are being mentored. It makes this manual a pleasure to read, and is easily one of my favorite manuals to date.

Definitely check out The Guile manual; it'll help make you a better programmer.

Categories: LugNut Blogs