Floating point formatted to base 16 - format

I am writing a Lisp program to approximate pi using a Spigot algorithm, one that finds a single digit at a time without needing any digits previously calculated. This one:
Supposedly goes one hexadecimal digit at a time, however testing it out we have consecutive calls (iterating over i) yielding Lisp's well-known fractions:
2F/F
6A/333
33D/4CB3
13C/3B79
And so forth. This is achieved using the format function with the ~x parameter:
(format t "~x" [some number])
Alternatively,
(format t "~d" [some number])
Yields the base 10 decimals:
3.1333334
0.12942614
0.042220525
0.020755336
However I want decimals in hexadecimal, for example if one round yielded 0.5 then I want 0.8; if the base 10 is 0.75 then I want 0.C. That way I can calculate see each hexadecimal digit of pi separately, which was my original reason for choosing this Spigot algorithm. I could also use a fraction of the form (n / 16^k), because this could easily be converted into a hexadecimal decimal.
Is this possible?

~x and ~d are formatting routines for integers, not floating point numbers (emphasis added):
22.3.2.2 Tilde D: Decimal
An arg, which should be an integer, is printed in decimal radix. ~D
will never put a decimal point after the number. … If arg is not an
integer, it is printed in ~A format and decimal base.
22.3.2.5 Tilde X: Hexadecimal
This is just like ~D but prints in hexadecimal radix (radix 16)
instead of decimal.
(As an aside, I think there's actually some ambiguity about what base ~x should print its argument in if it's not an integer. It comes down to whether "just like ~D but prints in hexadecimal" overrides just the integer arguments, or the all arguments.)
But what are these format directives actually doing? They're binding the values of *print-base* and *print-radix* and the documentation on those says that they only affect the printing of rational numbers:
Variable *PRINT-BASE*, *PRINT-RADIX*
*print-base* and *print-radix* control the printing of rationals. The value of *print-base* is called the current output base.
The value of *print-base* is the radix in which the printer will print
rationals. For radices above 10, letters of the alphabet are used to
represent digits above 9.
If the value of *print-radix* is true, the printer will print a radix
specifier to indicate the radix in which it is printing a rational
number. The radix specifier is always printed using lowercase letters.
If *print-base* is 2, 8, or 16, then the radix specifier used is #b,
#o, or #x, respectively. For integers, base ten is indicated by a trailing decimal point instead of a leading radix specifier; for
ratios, #10r is used.
Now, there are some floating point directives for format, and they're listed in 22.3.3 FORMAT Floating-Point Printers. Unfortunately, none of those do anything with different bases or radices, so you won't be able to print hexadecimals with them.
It looks like you'll end up having to write your own output routine, or find one in a library. Of course, you should probably implement it as a function that can be used with ~slash, so that you can still write things like:
(format t "~/hexadecimal/" 1/16)
It's actually not too hard to do this, so here's one way (but I won't guarantee that there are no bugs in this). It wouldn't be too hard to extend this to work with a user-provided base, and to make it a bit more like the other floating point printers (e.g., to add a + for positive numbers if # is provided, and to add support for widths, etc.).
(in-package #:common-lisp-user)
(defun hexadecimal (stream number colonp atp &rest args)
(declare (ignore colonp atp args))
(when (< number 0)
(write-char #\- stream)
(setq number (- number)))
(multiple-value-bind (quotient remainder) (floor number 1.0)
(format stream "~x." quotient)
(do () ((zerop remainder))
(multiple-value-setq (quotient remainder)
(floor (* 16 remainder)))
(format stream "~x" quotient))))
With this, you get the expected results:
(LOOP :FOR NUM :FROM -1/2 :TO 1/2 :BY 1/256
:COLLECT (FORMAT NIL "~/hexadecimal/" NUM))
;=>
("-0.8" "-0.7F" "-0.7E" "-0.7D" "-0.7C" "-0.7B" "-0.7A" "-0.79" "-0.78"
"-0.77" "-0.76" "-0.75" "-0.74" "-0.73" "-0.72" "-0.71" "-0.7" "-0.6F"
...
"-0.1D" "-0.1C" "-0.1B" "-0.1A" "-0.19" "-0.18" "-0.17" "-0.16" "-0.15"
"-0.14" "-0.13" "-0.12" "-0.11" "-0.1" "-0.0F" "-0.0E" "-0.0D" "-0.0C"
"-0.0B" "-0.0A" "-0.09" "-0.08" "-0.07" "-0.06" "-0.05" "-0.04" "-0.03"
"-0.02" "-0.01" "0." "0.01" "0.02" "0.03" "0.04" "0.05" "0.06" "0.07"
"0.08" "0.09" "0.0A" "0.0B" "0.0C" "0.0D" "0.0E" "0.0F" "0.1" "0.11"
...
"0.6C" "0.6D" "0.6E" "0.6F" "0.7" "0.71" "0.72" "0.73" "0.74" "0.75"
"0.76" "0.77" "0.78" "0.79" "0.7A" "0.7B" "0.7C" "0.7D" "0.7E" "0.7F"
"0.8")
In fact, it's really not hard to make this a bit more generic. Since we can pass arguments to format directives, we can take the printing base as an argument, and we can bind *print-base* to it at the same time, so we can just write in the body of the function, and we can do our numeric work with *print-base*:
(in-package #:common-lisp-user)
(defun floating (stream number colonp atp
&optional (*print-base* 10) (num-digits 10)
&rest args)
(declare (ignore colonp args))
;; If the number is negative, print the #\- and invert the number.
;; Otherwise, the number is non-negative, and if an # was provided
;; we print a leading #\+.
(cond
((minusp number)
(write-char #\- stream)
(setq number (- number)))
(atp
(write-char #\+ stream)))
;; Print number, which is now guaranteed to be positive. Begin by
;; taking its integer part and printing it, followed by a point.
;; Then, pull individual places and write them. This continues,
;; updating quotient and remainder by multiplying the remainder by
;; the base and taking the floor again until either the remainder
;; becomes zero, or we've reached the maximum number of digits.
(multiple-value-bind (quotient remainder) (floor number 1.0)
(write quotient :stream stream)
(write-char #\. stream)
(do ((num-digits num-digits (1- num-digits)))
((or (zerop remainder) (zerop num-digits)))
(multiple-value-setq (quotient remainder)
(floor (* *print-base* remainder)))
(write quotient :stream stream))))
;; 1/2 base 10 is a repeating decimal in base 3.
CL-USER> (format t "~3/floating/" 1/2)
0.1111111111
;; so is -1/2, and asking for more numbers gives them to us
CL-USER> (format t "~3,15/floating/" -1/2)
-0.111111111111111
;; but 1/3 base 10 is non repeating in base 3
CL-USER> (format t "~3,15/floating/" 1/3)
0.1
;; it's non-repeating in base 6, as well
CL-USER> (format t "~6,15/floating/" 1/3)
0.2
;; base 16 still works
CL-USER> (format t "~16/floating/" 189/256)
0.BD
;; and the # will give us a leading +
CL-USER> (format t "~16#/floating/" 189/256)
+0.BD

I don't think it is possible with the built in features of format1 However I'm sure someone with some fancy math could implement a function to do this.
1. I tested with SBCL v1.1.14. I tried (format t "~x" (coerce 2/32 'float)) which was the same as (format t "~d" (coerce 2/32 'float))

Related

Scheme Function (DrRacket)

So, i'm trying to write the following function in scheme, and to be able to run it on DrRacket. The problem is as follows,
make5 - takes two integers, and returns a 5-digit integer constructed of the rightmost 3 digits of the first input, and the leftmost 2 digits of the second input. For example, (make5 561432 254) would return 43225.
Negative signs on either input number should be ignored - that is, (make5 561432 -254) would also return 43225.
If the first number has less than three digits or the last three digits start with zeros, and/or the second number has less two digits, your
function should return -2. Note: you may want to define some auxiliary functions.
So far this is the function I've been able to write.
(define (make5 x y)
(cond ((< (length x) 3) -2)
((< (length y) 2) -2)
(((modulo (abs(x)) 1000) 0) -2)
(((modulo (abs(y)) 1000) 0) -2)
(else (append (list-tail x 3) (cons (first(y)second(y)))))))
I'm getting the error...
application: not a procedure;
expected a procedure that can be applied to arguments
Any advice would be appreciated. I'm new to scheme and still trying to grasp everything.
Don't wrap your arguments in parentheses - (abs(x)) means "call the procedure x and pass the result to abs.
(cons (first(y)second(y)) means "cons these four things: the value of first; the result of calling the procedure y; the value of second; and the result of calling the procedure y".
(You've called procedures correctly in some places. Stick to the same pattern.)
You're also missing a comparison in a couple of conditions; (= (modulo (abs x) 1000) 0).
The inputs are not lists, they're integers, so you can't apply length, first, or any such things to them.
The result should be an integer, not a list, so you can't construct it using append and cons, you should only use arithmetic.
These facts about integers should get you started:
A number has fewer than five digits if it is smaller than 10000.
The last four digits of a non-negative number n is (modulo n 10000).
If x is 12 and y is 34, x * 100 + y is 1234.
To get the three leftmost digit in an integer, you can divide by 10 repeatedly until you have a number less than 1000.
Also note that the second number only has one condition on its digits while the first has two, and that the note about defining auxiliary functions was not left there as a challenge for you to do without them.
For instance, if you had the auxiliary functions
(left-digits n x), which produces the leftmost n digits of x, and
(right-digits n x), which produces the rightmost n digits of x
you could write (it's also probably not a coincidence that the description uses the words "if" and "or"):
(define (make5 x y)
(if (or ( ... ))
-2
(+ (* 100 (right-digits 3 x)) (left-digits 2 y))))
Since you want to ignore the sign of the numbers, it's convenient to take care of abs once at the start, using let:
(define (make5 signed-x signed-y)
(let ((x (abs signed-x))
(y (abs signed-y)))
(if (or ( ... ))
-2
(+ (* 100 (right-digits 3 x)) (left-digits 2 y)))))
"All" that's left now is filling in the conditions and writing the two digit-extracting functions.

output n spaces in Common Lisp [duplicate]

I am looking for a way to output a character
a number of times using format. Is this possible? Can someone fill
in the _?_'s, so that the example works?
(let ((n 3))
(format nil "_?_" _?_ #\* _?_ ))
should return
=> "***"
It's nice to see so many solutions: ~A, ~<, and ~{ so far.
The ~#{ iteration construct provides a concise solution:
(format nil "~v#{~A~:*~}" 3 #\*)
(format nil "~a~:*~a~:*~a~:*" #\*)
"***"
Or elaborating a bit on Joshua Taylor's answer:
(format nil "~v{~a~:*~}" 3 '(#\*))
Would be one way of doing this. Don't be confused by the asterisks in the format directive, they are control characters, not characters being printed.
In terms of efficiency, however, this:
(make-string 3 :initial-element #\*)
would be a preferred way to achieve the same effect.
If you use the ~A directive, you can get this in exactly the form that you suggested, i.e.,
(let ((n 3))
(format nil "_?_" _?_ #\* _?_ ))
with three format arguments. However, if you use ~<, you can actually do this with just two format arguments. If you don't need this string inside of some other string that's already being generated by format, you could also just make the string using make-string.
Using Tilde A (~A)
You could print the character and specify a minimum width and the same character as the padding character. E.g., using ~v,,,vA and two arguments, you can ensure that some number of characters is printed, and what the padding character is.
CL-USER> (let ((n 3))
(format nil "~v,,,vA"
n ; number of characters that must be printed
#\* ; character to use as padding
#\*)) ; character to print with ~A
"***"
CL-USER> (let ((n 3))
(format nil "~v,,,vA" n #\* #\*))
"***"
CL-USER> (let ((n 10))
(format nil "~v,,,vA" n #\* #\*))
"**********"
This uses the full form of ~A:
~mincol,colinc,minpad,padcharA is the full form of ~A, which allows
control of the padding. The string is padded on the right (or on the
left if the # modifier is used) with at least minpad copies of
padchar; padding characters are then inserted colinc characters at a
time until the total width is at least mincol. The defaults are 0 for
mincol and minpad, 1 for colinc, and the space character for padchar.
as well as v:
In place of a prefix parameter to a directive, V (or v) can be used.
In this case, format takes an argument from args as a parameter to the
directive. The argument should be an integer or character. If the arg
used by a V parameter is nil, the effect is as if the parameter had
been omitted. # can be used in place of a prefix parameter; it
represents the number of args remaining to be processed. When used
within a recursive format, in the context of ~? or ~{, the # prefix
parameter represents the number of format arguments remaining within
the recursive call.
Using Tilde Less Than (~<)
There's also a less commonly used format directive, tilde less than, that's used for justification. it takes a format string and makes s
~mincol,colinc,minpad,padchar<str~>
This justifies the text produced by processing str within a field at
least mincol columns wide. str may be divided up into segments with
~;, in which case the spacing is evenly divided between the text
segments.
We can (ab)use this by passing an empty format string and just specifying the width and the padding character:
CL-USER> (let ((n 3))
(format nil "~v,,,v<~>"
n ; width
#\*)) ; padding character
"***"
CL-USER> (let ((n 5))
(format nil "~v,,,v<~>" n #\*))
"*****"
Just make a string
Of course, unless you need this special string inside of some other string that you're already formatting, you should do what wvxvw suggested, and just use make-string:
(make-string 3 :initial-element #\*)
Other alternatives
format is very flexible, and as this and other answers are pointing out, there are lots of ways to do this. I've tried to stick to ones that should do this in one pass and not do explicit iterations, but this can be done with format iterations, too, as Lars Brinkhoff and wvxvw have pointed out.
Like the answer of Lars, but we write the character wih ~C, instead of using the printer with ~A:
(format nil "~v#{~C~:*~}" 3 #\*)
Writing a character with something like write-char is a simpler operation than printing a Lisp object. The printer has a lot of context to observe and has to find the right way to print the object. OTOH, something like WRITE-CHAR just writes a single character to a stream.

Racket - creating a water density function with certain restrictions

I am attempting to solve the following problem:
Lately, Finn has been very curious about buckets of ice water and their properties. He has been reviewing the density of water and ice. It turns out the density of water in both states depends on many factors, including the temperature, atmospheric pressure, and the purity of the water.
As an approximation, Finn has written the following function to determine the density of the water (or ice) in kg/m3 as a function of temperature t in Celsius (−273.15 ≤ t ≤ 100):
water-density(t) = ( 999.97 if t ≥ 0 ;
916.7 if t < 0 )
Write a function water-density that consumes an integer temperature t and produces either 999.97 or 916.7, depending on the value of t. However, you may only use the features of Racket given up to the end of Module 1.
You may use define and mathematical functions, but not cond, if, lists, recursion, Booleans, or other things we’ll get to later in the course. Specifically, you may use any of the functions in section 1.5 of this page: http://docs.racket-lang.org/htdp-langs/beginner.html except for the following functions, which are not allowed: sgn, floor, ceiling, round.
This is what I have so far:
(define (water-density t)
(+ (* (/ (min t 0) (min t -0.000001)) -83.27) 999.97))
This code does definitely work as long as the given temperature is not between -0.000001 and 0, but it will not work for temperatures between that range. What can I do to avoid this problem? Dividing by zero is the biggest problem I have here.
This is a somewhat.... interesting way of going about teaching programming, and I have a feeling this class is going to cause more StackOverflow questions to appear in the future, but you can do it by combining max and min to make a function that returns either 1 or 0 depending on whether its input is negative:
(define (negative->boolint n))
(- 0
(min 0
(max (inexact->exact (floor n))
-1))))
This function takes a number, rounds it down with (inexact->exact (floor n)), then the combination of max and min "bounds" the number to be between -1 and 0, then subtracts that result from 1. Since after conversion to an integer the number can never be between -1 and 0, the bounding just results in 0 for positives and zero and -1 negatives. The subtraction part means the function returns (- 0 0) for all positive numbers and zero and returns (- 1 -1) for all negative numbers. By combining the result of this function with some arithmetic, you can get the behavior you want:
(define (water-density t)
(- 999.97
(* 83.27
(negative->boolint t))))
If t is positive or zero, then the result of (* 83.27 (negative->boolint t)) will just be zero. Otherwise, the difference of the two densities will be subtracted, giving you the correct result.
This works because it's just taking advantage of max and min's built-in conditional functionality to do conditional arithmetic. You could probably achieve the same with some level of hackery for round or abs or other statements that have conditional logic.
EDIT
My apologies, I missed the part of your question about not being able to use the rounding functions. Want you want is still doable however, by using two base functions for simulating conditionals: abs and expt. Getting conditionals from abs is fairly straightforward, you can divide a number by its absolute value to get it's sign. The reason you need expt is because it lets you get around the division by zero issue with abs, because (expt 0 x) is 0 for all positive numbers, 1 for zero, and undefined for negative numbers. We can use this to make a zero->boolint function:
(define (zero->boolint x)
(expt 0 (abs x)))
With this, we can add its result to the numerator and denominator to get around division by zero in (/ x (abs x)). Since this causes the division by zero case to return 1, we now have a nonnegative->boolint function:
(define (nonnegative->boolint x)
(/ (+ 1
(/ (+ (zero->boolint x) x)
(+ (zero->boolint x) (abs x))))
2))
The inner division takes care of dividing a number by its absolute value to return -1 for negatives and 1 for positives and zero. The outer addition by 1 and then division by 2 turns this into 0 for negatives and 1 for positives and zero. In order to get a negative->boolint function, we just need some sort of not operation - which in the case of 1 for true and 0 for false is just subtracting the value from 1. So we can define negative->boolint based on only the conditional logic of abs and expt as:
(define (negative->boolint x)
(- 1 (nonnegative->boolint x))
This works as expected with the definition of water-density. Also, please don't ever do this in real world code. No matter how "clever" it may seem at the time.

Lisp format a character a number of times

I am looking for a way to output a character
a number of times using format. Is this possible? Can someone fill
in the _?_'s, so that the example works?
(let ((n 3))
(format nil "_?_" _?_ #\* _?_ ))
should return
=> "***"
It's nice to see so many solutions: ~A, ~<, and ~{ so far.
The ~#{ iteration construct provides a concise solution:
(format nil "~v#{~A~:*~}" 3 #\*)
(format nil "~a~:*~a~:*~a~:*" #\*)
"***"
Or elaborating a bit on Joshua Taylor's answer:
(format nil "~v{~a~:*~}" 3 '(#\*))
Would be one way of doing this. Don't be confused by the asterisks in the format directive, they are control characters, not characters being printed.
In terms of efficiency, however, this:
(make-string 3 :initial-element #\*)
would be a preferred way to achieve the same effect.
If you use the ~A directive, you can get this in exactly the form that you suggested, i.e.,
(let ((n 3))
(format nil "_?_" _?_ #\* _?_ ))
with three format arguments. However, if you use ~<, you can actually do this with just two format arguments. If you don't need this string inside of some other string that's already being generated by format, you could also just make the string using make-string.
Using Tilde A (~A)
You could print the character and specify a minimum width and the same character as the padding character. E.g., using ~v,,,vA and two arguments, you can ensure that some number of characters is printed, and what the padding character is.
CL-USER> (let ((n 3))
(format nil "~v,,,vA"
n ; number of characters that must be printed
#\* ; character to use as padding
#\*)) ; character to print with ~A
"***"
CL-USER> (let ((n 3))
(format nil "~v,,,vA" n #\* #\*))
"***"
CL-USER> (let ((n 10))
(format nil "~v,,,vA" n #\* #\*))
"**********"
This uses the full form of ~A:
~mincol,colinc,minpad,padcharA is the full form of ~A, which allows
control of the padding. The string is padded on the right (or on the
left if the # modifier is used) with at least minpad copies of
padchar; padding characters are then inserted colinc characters at a
time until the total width is at least mincol. The defaults are 0 for
mincol and minpad, 1 for colinc, and the space character for padchar.
as well as v:
In place of a prefix parameter to a directive, V (or v) can be used.
In this case, format takes an argument from args as a parameter to the
directive. The argument should be an integer or character. If the arg
used by a V parameter is nil, the effect is as if the parameter had
been omitted. # can be used in place of a prefix parameter; it
represents the number of args remaining to be processed. When used
within a recursive format, in the context of ~? or ~{, the # prefix
parameter represents the number of format arguments remaining within
the recursive call.
Using Tilde Less Than (~<)
There's also a less commonly used format directive, tilde less than, that's used for justification. it takes a format string and makes s
~mincol,colinc,minpad,padchar<str~>
This justifies the text produced by processing str within a field at
least mincol columns wide. str may be divided up into segments with
~;, in which case the spacing is evenly divided between the text
segments.
We can (ab)use this by passing an empty format string and just specifying the width and the padding character:
CL-USER> (let ((n 3))
(format nil "~v,,,v<~>"
n ; width
#\*)) ; padding character
"***"
CL-USER> (let ((n 5))
(format nil "~v,,,v<~>" n #\*))
"*****"
Just make a string
Of course, unless you need this special string inside of some other string that you're already formatting, you should do what wvxvw suggested, and just use make-string:
(make-string 3 :initial-element #\*)
Other alternatives
format is very flexible, and as this and other answers are pointing out, there are lots of ways to do this. I've tried to stick to ones that should do this in one pass and not do explicit iterations, but this can be done with format iterations, too, as Lars Brinkhoff and wvxvw have pointed out.
Like the answer of Lars, but we write the character wih ~C, instead of using the printer with ~A:
(format nil "~v#{~C~:*~}" 3 #\*)
Writing a character with something like write-char is a simpler operation than printing a Lisp object. The printer has a lot of context to observe and has to find the right way to print the object. OTOH, something like WRITE-CHAR just writes a single character to a stream.

Scheme recursion (decimal to octal)

So our class was given an assignment to convert decimal numbers to their octal representations. I managed to just tinker with it till it worked, but I'm having some problems comprehending why it works.
Any possibility at explaining the recursion in a more simplistic manner? Thanks.
(define octify
(lambda (n)
(cond
((zero? n) 0)
((zero? (quotient n 8)) n)
(else (+ (* 10 (octify (quotient n 8))) (remainder n 8))))))
First of all, a "number" is not decimal or octal. A "number" is a mathematical concept, which is stored in the computer in some sort of format with a bunch of bits. Decimal and octal refer to different string representations of a number. That is, "decimal" and "octal", etc. only make sense when talking about strings, and a particular number can be converted into a string as decimal or octal or whatever.
Producing the octal (or some other base) string representation of an integer is a common basic task in programming. The algorithm you have basically figured out: take the remainder of the number by the base to get the last digit, then recurse on the quotient of the number by the base to get the rest (all but last digit) of the number.
What is strange about what you are doing is that you are not producing a string, as one would normally do for this task. Instead, you are trying to pack it back into a number, in such a way that the resulting number's decimal representation would look like what the octal representation of the original number would look like. (This happens to be possible since any octal representation is also a valid decimal representation of some number. This wouldn't be possible with hex, for example.) In other words, you are converting a number to its octal string representation, then parsing that string into a number as if it were a decimal representation. For example, take the number 42, whose decimal representation is the string "42" and octal representation is the string "52". Your program returns the number 52 (whose octal representation is the string "64").
You may be confused because you are typing this into an interpreter, and when you compute or print a number, it outputs the decimal representation. But it is important to understand that a number is completely different from a string. (If you computed a string in your interpreter, perhaps it would surround it with quotes or something.) It would make most sense if your program outputted the string of the octal representation, rather than a number which when printed, looks like it.
The mainstream representation of number, the one that took the world by storm, is positional notation. It's a representation that's tied intimately with the concept of quotient and remainder operations, which you're seeing somewhat from your recursive function defintion. Why is that?
Let's take a quick aside: positional notation is not the only viable representation for number. One way that comes up every so often is a tallying approach, where a number is either zero or one more than a number. We can use sticks. Since we're talking about programs, let's use a data-type.
Number :== Zero
| Successor(n) where n is a number
Read this as "A number is either Zero, or the successor of another number". Or, to code it up in a Scheme that supports structured representations (like Racket), we can write this:
(define-struct Zero ())
(define-struct Successor (n))
For example, representing three with this notation would be (Successor (Successor (Successor (Zero))). (This representation is called Peano, if I'm remembering right.)
Functions that deal with this kind of structured datatype often have the same shape as that of the datatype itself. That is, a function that works on a representation in Peano will look something like this:
;; a peano-eating-function-template: peano-number -> ???
(define (a-peano-eating-function-template a-num)
(cond [(Zero? a-num)
...]
[(Successor? a-num)
...
(a-peano-eating-function-template (Successor-n a-num))
...]
where the ... will be something specific to the particular problem you're trying to solve on Peano numbers. It's a matter of functions following the structure of the data that they're working on. As an concrete example of a Peano-eating function, here's one that turns a piano into a bunch of stars:
;; peano->stars: peano-number -> string
;; Turn a peano in a string of stars. We are all made of stars.
(define (peano->stars a-num)
(cond [(Zero? a-num)
""]
[(Successor? a-num)
(string-append "*"
(peano->stars (Successor-n a-num)))]))
Anyway, so datatypes lead naturally to functions with particular shapes. This leads us to going back to positional notation. Can we capture positional notation as a datatype?
It turns out that we can! Positional notation, such as decimal notation, can be described in a way similar to how the Peano number description worked. Let's call this representation Base10, where it looks like this:
Base10 :== Zero
| NonZero(q, r) where q is a Base10, and r is a digit.
Digit :== ZeroD | OneD | TwoD | ... | NineD
And if we want to get concrete in terms of programming in a language with structures,
(define-struct Zero ())
(define-struct NonZero(q r))
(define-struct ZeroD ())
(define-struct OneD ())
(define-struct TwoD ())
(define-struct ThreeD ())
(define-struct FourD ())
;; ...
For example, the number forty-two can be represented in Base10 as:
(NonZero (NonZero (Zero) (FourD)) (TwoD))
Yikes. That looks a bit... insane. But let's lean on this a little more. As before, functions that deal with Base10 will often have a shape that matches Base10's structure:
;; a-number-eating-function-template: Base10 -> ???
(define (a-number-eating-function-template a-num)
(cond
[(Zero? a-num)
...]
[(NonZero? a-num)
... (a-number-eating-function-template (NonZero-q a-num))
... (NonZero-r a-num)]))
That is, we can get the shape of a recursive function that works on Base10 pretty much for free, just by following the structure of Base10 itself.
... But this is a crazy way to deal with numbers, right? Well... remember that wacky representation for forty-two:
(NonZero (NonZero (Zero) (FourD)) (TwoD))
Here's another way to represent the same number.
((0 * 10 + 4) * 10 + 2)
Pretty much the same idea. Here, let's get rid of a few more parentheses. We can represent forty-two with the following notation:
42
Our programming languages are hardcoded to know how to deal with this notation for numbers.
What's our equivalent for checking Zero? We know that one.
(= n 0) ;; or (zero? n)
What's our equivalent for checking NonZero? Easy!
(> n 0)
What are our equivalents for NonZero-q and NonZero-r?
(quotient n 10)
(remainder n 10)
Then, we can pretty much plug-and-play to get the shape of recursive functions that deal positionally with their numeric inputs.
(define (a-decimal-eating-function-template n)
(cond [(= n 0)
...]
[(> n 0)
... (a-decimal-eating-function-template (quotient n 10))
... (remainder n 10)]))
Look familiar? :)
For more of this, see a textbook like How to Design Programs.
Obviously, (octify 0) should be 0 and (octify n) for n such that 0 < n < 8 is n. The next condition is the complex one. The first question is "what does (octify (quotient n 8)) return?". With base-10 numbers, dividing by 10 removes the right-most digit - 145/10=14 (assuming integer division). With base-8 numbers, dividing by 8 works similarly. Therefore, (octify (quotient n 8)) returns a number with all but the last digit of n, assuming that octify is defined correctly (which we have to assume). Now, we need to take this number and "push" it a digit to the left. Multiplying it by 10 does this, since the printer will be printing it in base-10. (remainder n 8) gets the final digit of n. Now we have the first however many digits (with a zero for the final digit to go) and the final digit, so we can combine them by adding them. At this point, the function is done and the correct value is returned.

Resources