Friday 24 July 2020

(practical-python->racket 1.5 Lists)

The format on the section 1.5 Lists in Practical Python Programming is similar to the section on Strings. Again, I have created a single file, lists.rkt, that covers both the examples and exercises. 

Before starting on the noticeable differences (to me), a quick reminder to say that I'm in the process of learning Racket and my translations are highly likely not to be the best. If you can suggest better ones, please leave a comment.

The one big difference that I noticed is that lists are mutable in Python but immutable in Racket. (Though Racket does have a separate mutable list datatype.) That apart, there seemed to be less differences between Python and Racket Lists than there is between their strings.

Python has an insert method which allows an item to be inserted at specified point in the list. I was able to translate by using the Racket take and drop functions:
  Python
  names.insert(2, "Aretha"))
  Racket
  (append (take names 2) '("Aretha") (drop names 2))

As with Strings, Python supports supplying a negative index to indicate an index from the end of the list. This can be easily be emulated by using the length function to calculate the desired index. For lists, but not strings, Racket has the last function which makes it easy to get the last item in a list.

Translating in function required using Racket's index-of function and checking whether it returned a number or not.
Python
  'Elwood' in names
Racket
  (number? (index-of names "Elwood"))

It seems that it is only possible to sort lists in both Python and Racket if they have the same type of data.

Python:
>>> [1, "b", 3].sort()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'str' and 'int'

Racket:
> (sort '(1 "b" 3) >)
; >: contract violation
;   expected: real?
;   given: "b"
;   argument position: 1st
; [,bt for context]

In summary, I found translating the contents of Lists quite a bit easier than I did with Strings.

Next up 1.6 Files

Tuesday 21 July 2020

(practical-python->racket 1.4 Strings)

The format of the Practical Python Programming 1.4 Strings is a little different from Section 1.3 Numbers. The exercises are designed to be entered into the Python console rather than complete, albeit short, programs. I have created a single racket program, strings.rkt, that covers both the examples and the exercises.

Python seems to have a plehtora of string literal types which are deliminated between ' ', " ", ''' ''', """ """, f' ', f" ", r' ', r" ", b' ' and b" ". In Python there is no difference between a string deliminated with ' ' or one deliminated with " ". Both are single-lines strings that expand escaped characters. ''' and """ are used for multi-line strings. The other options are r strings (raw strings), b strings (binary string) and f strings (format strings).

As far as I know racket has two string literal forms, one deliminated with " ", the other binary strings deliminated with #" ". It was a pleasant surprise to find that Racket strings are multi-line.

Translating most of the examples and exercises from Pyhton to Racket was quite straightforward. I'll only mention the few that weren't.

In Python you can specify negative indices which count back from the end of the string. It's a nice convenience but isn't that difficult to manage without by calculating the index through reference to the string length.

Python's string replication using the * operator (hello_5 = "Hello" * 5) proved to be a little more challenging. In trying to come up with a solution, I stumbled across Racket's for loop function. It was quite a suprise to find a for loop in Racket. Using the for function and set!, I was able to come up with a translation to Racket.

(define sss "Hello")
(define original-sss (string-copy sss))
(for ([i (in-range 1 5)])
     (set! sss (string-append sss original-sss)))
(displayln sss)

My Racket isn't anywhere near as concise as the Python though it could form the basis for a crude string-replicate function. Personally, I feel that such a function would be so rarely used that it wouldn't be worth writing one.

Python's String find and index methods return the index of the start of a substring found within a string. My first attempt to translate them was (index-of (string->list s) t). However this would only work to find a single character. After a litle more searching, I found that by using a regular expression I could duplicate the Python methods - (car (car (regexp-match-positions s t))). (The car-car is needed as the Racket regexp-match-positions returns a list of pairs of start and end positions.) 

The regular expression to translate the Python rfind and rindex string methods which search backwards from the end of a string is a little more complicated - (car (last (regexp-match-positions (regexp (string-append ".*(" t ").*$")) s)))). It is perhaps easier to understand if split into two expressions:

(define reggie (regexp (string-append ".*(" t ").*$")))
(car (last (regexp-match-positions t s)))

To emulate the Python isalpha String method, I needed to resort to a regular expression again as I couldn't find anything similar in the Racket standard library. At first this seemed easy (regexp-match? #px"^[a-zA-Z]+$" s) worked well against English strings. I then remembered that Python has pretty good Unicode support. I checked and confirmed that Python's isalpha recognises more than a to z as "alpha". I was struggling to find a solution in Racket so I posted a message on the Racket Mailing List. Ryan Culpepper of the Racket Team not only very kindly explained how characters are classified in Unicode, he also showed me how this information could be accessed in Racket and how there are special regular expression character classes in Racket that can be used to process the Unicode character classification. 

If that wasn't enough he also pointed out the need to normalise Unicode code points in "composed" form when using those special reqex character classes. (regexp-match? #px"^\p{L}+$" (string-normalize-nfc s))) is my resulting equivalent of Python's isalpha.

One significant difference between Python and Racket strings, which was a little surprising to me, is that whilst Python strings are immutable Racket has both immutable and mutable strings. A racket string created using "" enclosed string literals is immutable, a string created with the string function is mutable. This short Racket repl session shows the difference:

> (define immutable "Hallo")
> (string-set! immutable 1 #\e)
; string-set!: contract violation
;   expected: (and/c string? (not/c immutable?))
;   given: "Hallo"
;   argument position: 1st
; [,bt for context]
> (define mutable (string #\H #\a #\l #\l #\o))
> (string-set! mutable 1 #\e)
> mutable
"Hello"

As far as I can tell Racket doesn't have any string interpolation features like Python's f strings. It was pretty straightforward to translate the f string example but much more wordy.
  Python:
    f'{shares} shares of {name} at $(price:0.2f}'
  Racket:
     (displayln
       (string-append
         (~a shares)
         " shares of "
         name
         " at $"
         (real->decimal-string price 2)))

Both Python and Racket are considered "Batteries included" languages. I'm worried that my translations of Python to Racket might give the impression that Python has the bigger batteries. So to ensure a little better balance on that score, Racket not only has equivalents of Python's lower() and upper() string methods but also has titlecase and foldcase options. 

Finally these are very basic String examples, as to be expected in a course of the nature of Practical Python Programming. They do not cover any of the nuances of supporting Unicode. I have been led to believe that whilst Python has pretty good Unicode support it falls down a little as it doesn't handle different locales well. As I understand Racket does fully support different locales.

Next, 1.5 Lists
    

Monday 13 July 2020

(practical-python->racket exercises 1.9 1.10 1.11)

The next step in the mortgage exercise is to "parameterise" the start month, end month and additional payment amount. It was fairly trivial to update the "practical" version to do so. The code is at ex_1_9_extra_payment_calculator_2.rkt.


To make the changes to the "HTDP" versions, I extended the main data structure to include the new data items. I had to be careful to adjust the number of payments before calculating the payment for a month. The code is at ex_1_9_extra_payment_calculator.rkt.



The penultimate step in the mortgage exercise is to display a table showing the total paid and outstanding balance for each month of the loan. For me, the sensible way to do this was to update the mortgage data, display the values and then call the pay function recursively with the updated mortgage. It sounded easy but I wasn't certain how to go about using a "local" variable in a function.  In the two parts of HTDP that I studied there was little discussion of the scope of bindings, though the local function was introduced. I had read somewhere that lexical scoping applied in Racket. So I tried out this simple code:

> (define a 1)
> (define (f) (define a 2) (displayln a))
> (displayln a)
1
> (f)
2
> (displayln a)

It turned out that it's easy to define a local binding in Racket. The code is at ex_1_10_making_a_table.rkt


However, I'm now puzzled as to why HTDP introduced the local function and didn't mention that defining a binding inside a function would result in it being local to the function.

Adding the code to display the table was very straightforward in the "practical" - ex_1_10_making_a table_2.rkt

The final step in the mortgage exercise is to eliminate the overpayment in the final month. This required the separation of the interest calculation and payment when working out the new mortgage balance each month. Then the monthly payment could be calculated as the minimum of the normal payment amount and the outstanding balance. This was straight forward in the "practical" version with an additional when expression.  (ex_1_11_mortgage_2.rkt).

The "HTDP" version required a couple of additional local bindings to get this done. (ex_1_11_mortgage.rkt).

To complete the introductory section on Numbers, lets compare the "practical" Python and Racket solutions. (This is my Python solution not that supplied by David Beazley).

Python:
# mortgage.py
#
# Exercise 1.11

principal = 500000.0
rate = 0.05
payment = 2684.11
total_paid = 0.0
number_payments = 0
extra_payment_start_month = 61
extra_payment_end_month = 108
extra_payment = 1000

while principal > 0:
    principal = principal * (1+rate/12)
    payment = min(payment, principal)
    principal = principal - payment
    number_payments += 1
    total_paid += payment
    if  number_payments >= extra_payment_start_month and \
        number_payments <= extra_payment_end_month:
        total_paid += extra_payment
        principal -= extra_payment 
    print(number_payments,
          round(total_paid, ndigits=2),
          round(principal, ndigits=2))
    
print('Total paid', round(total_paid, ndigits=2), end=' ')
print('in', number_payments, 'months')

Racket:
#lang racket

(define principal 500000.0)
(define rate 0.05)
(define payment 2684.11)
(define total-paid 0.0)
(define number-months 0)
(define extra-payment-start-month 61)
(define extra-payment-end-month 108)
(define extra-payment 1000)

(define (pay)
  (cond
    [(<= principal 0) total-paid]
    [else (set! number-months (add1 number-months))
          (set! principal (* principal (+ 1 (/ rate 12))))
          (set! payment (min payment principal))
          (set! principal (- principal payment))
          (set! total-paid (+ total-paid payment))
          (when (and (>= number-months extra-payment-start-month)
                     (<= number-months extra-payment-end-month))
            (set! total-paid (+ total-paid extra-payment))
            (set! principal (- principal extra-payment)))
          (display number-months)
          (display " ")
          (display total-paid)
          (display " ")
          (displayln principal)
          (pay)]))

(display "Total paid ")
(display (pay))
(display " in ")
(display number-months)

(displayln " months.")

The biggest conceptual difference to me is the need to employ a recursive function in Racket to repeat the calculation. The other noticeable difference at this stage is that the most people would consider the Python to be easier to read. (Though the need to use a continuation mark suggests frustration ahead for the unaware.)

Next up 1.4 Strings.

Thursday 9 July 2020

(practical-python->racket exercise 1.8 extra payments)

The second exercise takes the first a step further by calculating the affect of making an additional monthly payment of 1,000 dollars in the first year of the loan. Having come up with two versions of the mortgage payment example, which should I use for this example? I felt I'd get the most out of the exercise if I again came up with "HTDP" and "practical" versions.

Starting with the "HTDP" version, I realised that there was one improvement that I could make before I even started on the improvements. I added some constant definitions to add a little clarity:

; constants
(define LOAN_AMOUNT 500000.00)
(define INTEREST_RATE 0.05)
(define MONTHLY_PAYMENT 2684.11)

and changed the definition of "daves-mortgage":

(define daves-mortgage
  (make-mortgage LOAN INT_RATE MTHLY_PAYMENT 0 0.00))


This trivial change put me in a "refactoring" frame of mind. The next step I took was to wrap the superficially direct use of the mortgage-payment field in a function. This turned out to be a little heavier than I expected as I needed to pass the whole mortgage structure to the wrapper function (so that I can later calculate additional payment amounts.

This is the simple wrapper function:
(check-within (payment (make-mortgage 1000 .10 100 0.00 0.00))
                       100
                       1e-5)
(define (payment mortgage)
  (mortgage-payment mortgage))


When I came to actually make the changes to reflect the additional payments, there was a nuance due to the way that I was feeding the data back into the pay function recursively. I only needed to increase the payment by 1,000 dollars in the first month and then decrease it by 1,000 dollars in the 13th month. This is the resulting payment function:

(define (payment mortgage)
  (cond
    [(= (mortgage-num-payments mortgage) 0)
     (+ (mortgage-payment mortgage) 1000)]
    [(= (mortgage-num-payments mortgage) 12)
     (- (mortgage-payment mortgage) 1000)]
    [else (mortgage-payment mortgage)]))


The only other significant change was to return both the total paid and the number of months from the main pay function. I choose to return the two values as a pair. (A datatype I only learnt about when reading the Data Types chapter of The Racket Guide). I then used car and cdr to extract the two items from the pair. (It made me feel like a real lisp programmer ;-) ).


One area where my practical knowldege of Racket is severely lacking is formatting simple output. I really need to learn the Racker equivalent of Python's print(f'The number is {i}') or even print(i ,j).

Making the changes to the "practical" version was straightforward. I even remembered to use when instead of if. (if requires expressions for both the "then" and "else" cases.

The final programs are ex_1_8_extra_payments.rkt and ex_1_8_extra_payments_2.rkt

Now on to the rest of the exercises.


Thursday 2 July 2020

(practical-python->racket exercise 1.7 mortgage)

The first exercise is to calculate the total payments taken to pay off a 30-year fixed rate mortgage. David Beazley provides an initial solution to get his students started.


I started to think through how best to convert David's example into Racket. Almost without thinking, I fell back on the techniques covered in the first parts of How To Design Programs (HTDP). I designed a structure to hold the data and then wrote a recursive function to process it. (How else to replicate a while loop in Racket?) I added a couple of simple tests to check it was working. The program gives the same result as its Python equivalent but with its structure and function definitions, it lacks the immediacy of it:

#lang racket
(require test-engine/racket-tests)

; data definitions
(define-struct mortgage 
  [balance rate payment num-payments total-paid])

; data
(define daves-mortgage
  (make-mortgage 500000.00 0.05 2684.11 0.00 0.00))

; functions
(check-within (apply-interest 100 1.20) 110.0 1e-5)
(define (apply-interest balance rate)
  (* balance (add1 (/ rate 12))))

(check-within (pay daves-mortgage) 966279.6 1e-5)
(define (pay mortgage)
  (cond
    [(<= (mortgage-balance mortgage) 0) 
     (mortgage-total-paid mortgage)]
    [else (pay (make-mortgage
                (- (apply-interest
                    (mortgage-balance mortgage)
                    (mortgage-rate mortgage))      
                (mortgage-payment mortgage))
                (mortgage-rate mortgage)
                (mortgage-payment mortgage)
                (add1
                 (mortgage-num-payments mortgage))
                (+ (mortgage-total-paid mortgage)
                (mortgage-payment mortgage))))]))

(test)

; The "program"
(display "Total paid ")
(displayln (pay daves-mortgage))

So I set about trying to better emulate the Python version. In my first attempt, I passed indvidual values to the recursive function instead of a structure. It looked a little more like the Python version and was roughly half the length of the "HTDP" attempt (in terms of lines of code). But it still didn't feel very "practical".

The only way I could think of to make the code look more like the Python was to use "global, mutable variables" (in the same way that the Python does). In the part of HTDP that I had studied, there was never any mention of changing data, everything was immutable. I had somewhere seen "set" used in Racket code so I looked it up in the Racket Reference and did my worst. The code does look much more more like the Python but must rank as some of the worst Racket code ever written:

#lang racket

(define principal 500000.0)
(define rate 0.05)
(define payment 2684.11)
(define total_paid 0.0)

(define (pay)
  (cond
    [(<= principal 0) total_paid]
    [else (set! principal 
                (- (* principal (+ 1 (/ rate 12)))
                   payment))
          (set! total_paid (+ total_paid payment))
          (pay)]))

(display "Total paid ")
(displayln (pay))

Wednesday 1 July 2020

(practical-python->racket 1.3 numbers)

The first challenge that I faced in translating the examples of Practical Python Programming - 1.3 Numbers was the differences in the number type systems between Python and Racket.

Python has a flat number type system with four types: booleans, integers, floating point and complex (imaginary numbers).

Racket has a heirarchical number type system. There are two main branches in the heirarchy, exact and inexact. This is a somewhat simplified view of the Racket number type hierarchy:
    exact
      integer      e.g. 10 - like a Python integer
      rational     e.g. 1/2
      complex    e.g. 1 + 2i
    inexact
      real          e.g. 2.0 - like a Python floating point
      complex    e.g. 2.1 + 3.3i

Racket also has two specialists number types, flonum (floating point) and fixnum (integer), that are used when there is a need for improved performance. These have their own maths operators. (I haven't looked into them any further at this stage.)

When "translating" the Python code, I have equated its integer with Racket's exact and its floating point with Racket's inexact.

The second came from Python's boolean type. It's a very raw C-language type with very few barriers to how a programmer can use it. For all I know, Racket's boolean type may similarly be implemented using integers. However, as far as the Racket programmer is concerned, it supports only two values: true and false.

I didn't want my script to just raise an error and stop when I tried to add a number to a boolean. After searching the docs and a little trial and error, I worked out how to trap an error:

   (racket '(define c 4))
   (racket "(+ c true)")
   (expect "Contract violation")
   (with-handlers ([exn:fail:contract?
                 (lambda (err) 
                  (displayln "Contract Violation"))])
                 (+ c true))

The third was tame in comparison, working out how to convert an inexact float to an exact integer. I came up with using: 

   (inexact->exact (floor my-float-value))

Take a look at the numbers.rkt script, then run it either in Dr Racket or the Racket repl. Do the result make sense?