Factors to string
Any integer can be written as a product of prime numbers. This is called the number's prime factorization. For instance, 24's prime factorization is 24 = 2 x 2 x 2 x 3
or even better 24 = 2 ^ 3 x 3
. For this exercise, we are given the prime factorization. We have to construct the string representation.
Examples:
(factors->string [2 2 2 3]) ;=> "24 = 2^3 x 3"
(factors->string [7]) ;=> "7 = 7"
(factors->string [2 2 7]) ;=> "28 = 2^2 x 7"
Just for clarity, here are the rules:
- If the prime factor appears only once, just use the number by itself (i.e., "2")
- If the prime factor appears more than once, use the exponent notation (i.e., "2^3")
- If there is more than one prime factor, separate them with an "x" for multiplication (i.e., "3 x 7")
- Start the string with "n =" where n is the number that has been factorized. You can calculate this by multiplying all the factors together.
Thanks to this site for the challenge idea where it is considered Hard level in Ruby.
Email submissions to eric@purelyfunctional.tv before August 29, 2020. You can discuss the submissions in the comments below.
Some thoughts:
In this case I like to bind
(frequencies factors)
to the namefactor+frequency
.(frequencies factors)
has multiple meanings: a function of factor to frequency and also a collection of[factor frequency]
tuples. In this case only the collection of tuple aspect is relevant, which I signal with the namefactor+frequency
. I allow the reader to forget about the function aspect of(frequencies factors)
, freeing up his mind. In the same sense, the reader can choose to keep his mind as empty as possible by not even reading the right hand side of the let binding at all. Small optimisations perhaps not worth the overhead (although the more I look at it, the more I like it), but definitely nice to contemplate as an exercise.Lately I've been playing around some with Uncle Bob's (Robert Martin's) rule of thumb to keep the implementation of a function exactly one level of abstraction lower than the name of that function. If something is not exactly one level lower, it should ("should"; rule of thumb) be extracted into a function that is one level lower. The goal of this is to make the function 'polite'; the function provides only minimal information needed to understand what this function is doing. If the reader wants to know more, he can look up the implementations of the functions called as part of the implementation of this function.
From what I've seen, Martin talks only about extracting to a function. An additional benefit of extraction to a named function is that that named function can now be tested. (However, as Martin also points out, not all functions need to be explicitly tested: if the function results from a refactoring step, then it will already be tested, provided the pre-refactor code was tested.) The cost of extracting to named functions is extra overhead (more defn forms) and a worse overview (related code becomes less spatially local). I find instead extracting functions and calculations to let binding a nice way to obtain the benefits of 'politeness' while maintaining overview (locality) at a small cost of extra code. Instead of naming a function, I can just name the data resulting from a function call or thread.
In that sense, you could see the form
(str factorized " = " factors-as-str)
as the true body of the function, with the let binding providing the 'politeness' menu in case the reader wants more detail. That made me think: the bindings are not so polite, becausefactor+frequency
and->str
are not directly related to the 'true body' of the function! This inspired me to do something I have never done before: put a let binding inside a let binding:Now there is a 'polite' layering. To understand the final
(str ...
form, the reader can look upfactorized
andfactors-as-str
in the top level let binding. To try and understandfactors-as-str
, the reader can readIf that is not sufficient still, the reader can look at the bindings of the inner let. Perhaps much overdone, but interesting to contemplate as an exercise.