Homework: Week 1
1. Explain why new-if will not work
I answered this whilst writing up my notes from reading chapter 1.1.
It essentially boiled down to the new if function not being a special form so all of it's parameters are evaluated at the point the function is called in applicative order. Therefore a continuous infinite loop starts as one of the parameters calls itself.
Comments after reading answers
My answer is very terse and could use an example by running through what I believe the interpreter to be doing.
2. Write a procedure squares
that takes a sentence of numbers as it's argument and returns a sentence of the squares of the numbers
In this code I use recursion twice in order to complete the problem. Firstly is a generic helper to return the length of a sentence. As stolen from the documentation. The second is the squares procedure itself which iterated over the list adding the square of each element to a new list and once the initial list is empty it returns the new list.
#lang racket
(require (planet dyoo/simply-scheme:2:2))
(define (square n)
(* n n))
(define (sentence-length sent)
(cond ((null? sent) 0)
(else (+ 1 (sentence-length (cdr sent))))))
(define (squares-iter nums new-list)
(if (equal? (sentence-length nums) 0)
new-list
(squares-iter (butfirst nums) (sentence new-list (square (first nums))))))
(define (squares nums)
(squares-iter nums (sentence)))
(squares (sentence 10 11 12))
Comments after reading answers
Changes to improve my code:
- Using empty as the base case for the function will stop an infinite loop.
- You do not need to store your new sentence in a variable. Calling the procedure sentence will evaluate it's arguments to you can trigger recursion by having squares as one of the parameters.
3. Write a procedure switch that takes a sentence as its argument and returns a sentence in which every instance ofg the words I or me is replaced by you, while every instance of you is replaced by me except at the beginning of the sentence where it's replaced by I
As in the question above I have leveraged recursion to iterate over the incoming sentence and used a the conditional statement cond
to determine which words need to be replaced based on the requirements of the question.
#lang racket
(require (planet dyoo/simply-scheme:2:2))
; requirements:
; 1. replace "I" with "you"
; 2. replace "me" with "you"
; 3. replace "you" with "me" unless start of sentence
; 4. replace "you" with "I" if start of sentence
(define example (sentence 'You 'told 'me 'that 'I 'should 'wake 'you 'up))
(define example_answer (sentence 'I 'told 'you 'that 'you 'should 'wake 'me 'up))
(define (sentence-length sent)
(cond ((null? sent) 0)
(else (+ 1 (sentence-length (cdr sent))))))
(define (switch-iter sen new-sen c)
(if (equal? (sentence-length sen) 0)
new-sen
(cond ((or (equal? (first sen) 'I) (equal? (first sen) 'me)) (switch-iter (butfirst sen) (sentence new-sen 'you) (+ c 1)))
((and (not(equal? c 0)) (equal? (first sen) 'you)) (switch-iter (butfirst sen) (sentence new-sen 'me) (+ c 1)))
((and (equal? c 0) (equal? (first sen) 'You)) (switch-iter (butfirst sen) (sentence new-sen 'I) (+ c 1)))
(else (switch-iter (butfirst sen) (sentence new-sen (first sen)) (+ c 1))))))
(define (switch sen)
(switch-iter sen (sentence) 0))
(if (equal? (equal? (switch example) example_answer) #t) (display (sentence 'Test 'passed)) (display (sentence 'Test 'failed))) (newline)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; second attempt as answering question 3 after reading the answer for question 2
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (switch_ sen)
(if (empty? sen)
'()
(cond ((equal? (first sen) 'I) (se 'you (switch (bf sen))))
((equal? (first sen) 'me) (se 'you (switch (bf sen))))
((equal? (first sen) 'you) (se 'me (switch (bf sen))))
; I don't know how to tell if we are at the start of the sentence or not
(else (se (first sen) (switch (bf sen)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; third attempt as answering question 3 after reading the answer (and then hiding it away)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (switch sen)
(se (switch-first (first sen))
(switch-rest (bf sen))))
(define (switch-first wd)
(cond ((equal? wd 'You) 'I)
((equal? wd 'I) 'you)
((equal? wd 'me) 'you)
(else wd)))
(define (switch-rest sen)
(if (empty? sen)
'()
(cond ((equal? (first sen) 'I) (se 'you (switch-rest (bf sen))))
((equal? (first sen) 'me) (se 'you (switch-rest (bf sen))))
((equal? (first sen) 'you) (se 'me (switch-rest (bf sen))))
(else (se (first sen) (switch-rest (bf sen)))))))
; the way Mr.Harvery writes this means that you don't need to write out the call to the switch-rest function in each condision, instead just return the word
; this is cleaner for that one function, but also uses up another function.
(define (switch-rest_ sent)
(if (empty? sent)
'()
(se (switch-one (first sent))
(switch-rest (bf sent)) )))
(define (switch-one wd)
(cond ((equal? wd 'you) 'me)
((equal? wd 'I) 'you)
((equal? wd 'me) 'you)
(else wd) ))
Comments after reading answers
I need to be more concise with my code. There is always a cleaner way to do it. Less repetition more clarity.
4. Write a predicate ordered?
that takes a sentence of numbers as its argument and returns a true value if the numbers are in ascending order, or a false value otherwise
Much like the other two I am leveraging recursion. This time the twist is a nested if statement, to determine if we are at the end of the sentence or not.
#lang racket
(require (planet dyoo/simply-scheme:2:2))
(define example_true (sentence 1 2 3 3 4 5 6 7 8 9 10))
(define example_false (sentence 1 2 3 4 5 6 7 8 9 0))
(define example_false_also (sentence 5 2 3 4 5 6 7 8 9 1))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; first attempt as answering question 4
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (sentence-length sent)
(cond ((null? sent) 0)
(else (+ 1 (sentence-length (cdr sent))))))
(define (ordered-iter nums)
(display nums) (newline)
(if (equal? (sentence-length nums) 1)
#t
(if (not (< (first nums) (first (butfirst nums))))
#f
(ordered-iter (butfirst nums)))))
(define (ordered?_ nums)
(ordered-iter nums))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; second attempt as answering question 4 after reading the answer for previous questions
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (ordered_? nums)
(cond ((< (first nums) (first (bf nums))) (ordered? (bf nums)))
((empty? (bf nums)) #t)
(else #f)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Mr.Harvey's answer
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (ordered_brian? sent)
(cond ((empty? (bf sent)) #t)
((> (first sent) (first (bf sent))) #f)
(else (ordered? (bf sent))) ))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; last attempt as answering question 4 after reading the answer
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (ordered_nope? nums)
(display nums) (newline)
(cond ((< (first nums) (first (bf nums))) (ordered? (bf nums)))
((empty? (bf nums)) #t)
(else #f)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; last attempt as answering question 4 after reading the answer
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (ordered? nums)
(cond ((empty? (bf nums)) #t)
((<= (first nums) (first (bf nums))) (ordered? (bf nums)))
(else #f)))
(ordered? example_true)
(ordered? example_false)
(ordered? example_false_also)
Comments after reading answers
Brian is clear in his comment about the assumptions that the procedure makes. This clarify could be used in a comment within a codebase or implemented into code to protect the procedure from being called wrongly. On my second attempt I wrote almost exactly the answer that Mr.Harvey provides. His logic is opposite to mine, if the the first element in the array is less than, I call the function recursively until the array is empty or there are descending numbers. Mr.Harvey does the opposite. He checks to see if the number is descending and returns false, otherwise the number is ascending and the function is called recursively, unless it's empty, in which case true is returned.
Actually, there is a small difference. My function will return false if there are two adjacent numbers of the same value. Which is still ascending order... SO, Mr.Brian wins. All hail.
To fix my code I thought I could just change my equality operator and leave the structure of the code as it was. I was wrong. Instead I found that I was getting an error: Invalid argument to FIRST: '()
. This is weird to me because I intuitively thought that comparing an empty sentence element with less-than and less-than-or-equal-to would give the same results. I'm not sure I totally understand why this is the case. But, it does make sense to put the base case as the first conditional. That way we fix the issue of ever running into an empty array and having equality tests with non homogenous value types.
There could be an argument that one of the two answers is more or less efficient than the other. But that entirely depends on the dataset and weather or not the sentences of numbers are more likely to contain
5. Write a procedure ends-02
tghat takes a sentence as it's argument and returns a sentence containing only those words of the argument whose last letter is E
#lang racket
(require (planet dyoo/simply-scheme:2:2))
(define example '(please put the salami above the blue elephant))
(define answer '(please the above the blue))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; first attempt as answering question 4
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (sentence-length sent)
(cond ((null? sent) 0)
(else (+ 1 (sentence-length (cdr sent))))))
(define (ends-e-iter sen new-sen)
(display sen) (newline)
(display new-sen) (newline) (newline)
(cond ((equal? (sentence-length sen) 0) (display 'end))
((equal? 'e (last (first sen))) (ends-e-iter (bf sen) (sentence new-sen (first sen))))
(else (ends-e-iter (bf sen) new-sen))))
(define (ends-e_ sen)
(ends-e-iter sen (sentence)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; second attempt as answering question 4 after reading the answers for the previous questions
; couldn't work out how to retain the element ending in e without just calling the function with the whole of the sentence
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;(define (ends-e_second sen)
; (cond ((empty? sen) '())
; ((equal? (first (first sen)) 'e) ())))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Mr.Harvey's answer
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (ends-e_brian sent)
(cond ((empty? sent) '())
((equal? (last (first sent)) 'e)
(se (first sent) (ends-e (bf sent))))
(else (ends-e (bf sent)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Writing my answer again after reading Mr.Harvey's answer and then looking away
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (ends-e sen)
(cond ((empty? sen) '())
((equal? (last (first sen)) 'e) (se (first sen) (ends-e (bf sen))))
(else (ends-e (bf sen)))))
(ends-e example)
(ends-e_brian example)
Comments after reading answers
I forget that you can construct a sentence as the return value and then call the function recursively within that sentence. It's a neat trick and one which was used to solve question 3.
6. Write a test to determine if the functions or
and and
are special forms or not and discuss the benefits of the built-in functions being special forms or ordinary functions
I believe I've proved that the built-in functions or
and and
are both special forms. Using the display function I can see what parameter is being evaluated. In both the true and false states of each function all display
calls should execute if they are not special forms, however they do not. This is useful because they have the opportunity to be more efficient. In the false state of or
only the first evaluation needs to be true in order to return true, even if both are true.
In the case of and
the built in function only has the opportunity to be more efficient in the falsy state, because both need to be evaluated as true to return true. Only one parameter needs to be false and the function can return false regardless of the next parameter therefore it would be more efficient to check the parameters one at a time.
#lang racket
(require (planet dyoo/simply-scheme:2:2))
; Prove that display is 'truthy'
(if (display 'true) '(display is truthy) '(display is not truthy))
(or (display '1) (display '2) (display '3)) (newline)
(or #f #f (display '3)) (newline)
(and (display '1) (display '2) (display '3)) (newline)
(and #f (display '2) (display '3)) (newline)
Comments after reading answers
I have not proven that they are not special forms. I have only proven that they do not evaluate left to right. I can only know they are special forms because the book said so.
There are a few reasons listed in Mr.Harvey's answer that say why special forms are a good idea.
a. efficiency. b. conditions that depend on each other i.e. if you define a variable in one parameter and use it in the next. c. expectations; you want to know that procedures which look like they would evaluate in a special form, apply in they way you expect them to. Otherwise the language becomes confusing. d. standardization; if everything in the language is special, then we don't know what standard is. e. functional objects; special forms aren't able to be treated like normal objects.