Overloaded function failing giving Compiler recursion error - recursion

With the following code, I get #<CompilerException java.lang.UnsupportedOperationException: Can only recur from tail position (NO_SOURCE_FILE:4)> despite the fact that all recurs are in tail positions. If I remove the recur from the one-argument version, it stops complaining. Why is this happening?
(defn remove-duplicates "Removes duplicate elements of lst.
For example, given (1 2 3 1 4 1 2), remove-duplicates returns a sequence
containing the elements (1 2 3 4), in some order."
[lst] (recur (rest lst) (set (first lst)))
[lst uniques] (cond (zero? (count lst)) uniques
:else (cond
(some (partial = (first lst)) uniques)
(recur (rest lst) uniques)
:else
(recur (rest lst) (first lst)))))

You haven't split up the multi-arity bodies right. Should read (defn foo ([x] (...)) ([x y] (...))). This causes the compiler to think you're doing totally different stuff, which probably accounts for your issue.

First of all: you know that all you want is (def remove-duplicates set) or -- if you want a vector -- (def remove-duplicates-vec (comp vec set)), right?
Five things here:
As amalloy noticed, you should've added parens
As kotarak noticed, you can't recur between arities
You can't call (set (first lst)) because set wants coll. If you want, do something like (set (vector (first [1 2 3 2 3]))) but this is neither pretty nor idiomatic
Doing (cond pred1 code1 :else (cond pred2a code2a :else code2b)) could be made simplier: (cond pred1 code1 pred2a code2a :else code2b) -- what you did is treated cond macro as if it were if (which is a built-in as far as I know)
Your last tail-call is also wrong. Assume we've started with [1 2 3 2 1]
When you call it first you have following arguments: ([2 3 2 1] #{1}) (I've skipped the boring part)
Then you have last predicate true, so you go with ([3 2 1] 2) and this is obviously wrong because you wanted ([3 2 1] #{1 2}). You probably want to call (recur (rest lst) (conj uniques (first lst)))
Summing up:
(defn remove-duplicates
([lst] (remove-duplicates (rest lst) #{(first coll)}))
([lst uniques]
(cond
(zero? (count lst)) uniques
(some (partial = (first lst)) uniques)
(recur (rest lst) uniques)
:else
(recur (rest lst) (conj uniques (first lst))))))

Related

DrRacket - find the longest increasing subsequence

I want to create code for finding LIS. My code doesn't work very well. For example if the input is '(1 3 5 10 9 6 7), output should be '(1 3 5 6 7) but my program return '(1 3 5 10). What am I doing wrong? Have I code binary tree and then find the higher height? Or can I code this program in easiest way?
(define (return_sequence N lst)
(cond
[(empty? lst) '()]
[(< N (first lst)) (cons
(first lst)
(return_sequence (first lst) (rest lst))
)]
[else (return_sequence N (rest lst))]
)
)
(define (find_longest lst1 lst2)
(cond
[(empty? lst1) lst2]
[(empty? lst2) lst1]
[(< (length lst1) (length lst2)) lst2]
[else lst1]
)
)
(define (LIS lst)
(cond
[(empty? lst) '()]
[else (find_longest
(cons
(first lst)
(return_sequence (first lst) (rest lst)))
(LIS (rest lst))
)]
)
)
Before we begin, a question. Do you consider '(1 1) to be an increasing sequence? For now, I will assume you do not.
First, note that we can simplify find-longest as follows:
(define (find-longest lst1 lst2)
(if (<= (length lst1)
(length lst2))
lst1
lst2))
This is terribly inefficient, but we won't worry about that for now. It at least looks cleaner than your code.
I assume that you're trying to define (return_sequence N lst) to be the longest increasing subsequence of lst such that all elements of said subsequence are greater than N. I suggest you take a look at exactly what happens when you try (return_sequence 1 '(4 2 3)). We should expect the result to be '(2 3), but it is in fact '(4).
You need to think carefully about exactly how you want to handle the case where (< N (first lst)) and make sure you're not making a silly mistake (spoiler - you are making a silly mistake).
Edit: suppose that (< N (first lst)). The longest increasing subsequence of lst where all elements are greater than N either contains (first lst) or doesn't. To find the longest subsequence that doesn't, we compute (return_sequence N (rest lst)). To find the longest subsequence that does, we compute (cons (first lst) (return_sequence (first lst) (rest lst)). The relevant cond clause becomes
[(< N (first lst)) (find_longest
(cons (first lst)
(return_sequence (first lst) (rest lst)))
(return_sequence N (rest lst)))]
and this solves your problem.
On another note, it's extremely helpful to add in the value of minus-infinity, with the condition that this value is always less than any other value. In that case, we can just do
(define (LIS lst)
(return_sequence minus-infinity lst))
Doing this requires a little bit of cleverness, but it is possible.
In fact, one clever way to add minus_infinity is to pass into return_sequence not a number N, but the function (lambda (x) (< N x)). Then, instead of the line (< N (first list)), you would instead write (less_than_N (first list)). Then, you can (define (minus_infinity x) #t).
It's also possible to create a new symbol using gensym and have that be minus_infinity. You'd then want to do something rather cheeky like
(define minus_infinity (gensym))
(define (return_sequence N lst)
(let ((< (lambda (a b) (or (equal? a minus_infinity) (< a b)))))
cond ...)
(define (LIS lst)
(return_sequence minus_infinity lst))
with the cheeky bit being that we're not defining (<) recursively - the < that occurs in (lambda (a b) (or (equal? a minus_infinity) (< a b))) is the original <.
Finally, the algorithm you're trying is really slow. It's possible to solve this problem in O(n log n) where n = (length lst). Your solution will be an exponential time one, but improving it is highly nontrivial.

Writing an alternating series checker in racket

I am trying to make a program which determines if an inputted list alternates in sign. For example, my program would return true if given the list(s): [-1, 5, -10] or [5, -17, 25]. The program would return false if given the list(s): [-1, -5, 6] or [1, -2, -6].
I've tried making a simple cond statement which checks the sign of the first number in the list and then after checks the second number in the list to make sure the first number was positive and the second number was negative or the first number was negative and the second number was positive.
(define (alternating-signs-in-list? lst)
(cond
[(> (first lst) 0)
(cond [(< (first (rest lst)) 0) (alternating-signs-in-list? (rest lst))])]
[(< (first lst) 0)
(cond [(> (first (rest lst)) 0) (alternating-signs-in-list? (rest lst))])]
[else false]))
I expected the code presented to work but was met with an error stating:
first: expects a non-empty list; given: empty
This error occurred when I made the following check-expect:
(check-expect (alternating-signs-in-list? (cons 1 (cons -5 (cons 50 empty)))) true).
Why is the following error occurring and is there an easy fix I can make to get my code to start working. Thank you.
The trick is to compare pairs of elements at the same time (first and second of the list), until the list is exhausted. The error you're receiving is because you forgot to handle the empty-list case, and for this problem in particular, we also need to handle the case when the list has a single element left.
Before proceeding, it's useful to implement a same-sign? procedure, it's easy if we leverage the sgn procedure (that returns the sign of a number), and if we assume that 0 is positive:
(define (same-sign? n1 n2)
; they have the same sign if their `sgn` value is the same
(= (if (zero? n1) 1 (sgn n1))
(if (zero? n2) 1 (sgn n2))))
The main procedure goes like this:
(define (alternating-signs-in-list? lst)
(cond ((or (empty? lst) (empty? (rest lst))) #t) ; empty list / single element case
((same-sign? (first lst) (second lst)) #f) ; they are NOT alternating
(else (alternating-signs-in-list? (rest lst))))) ; advance recursion
Alternatively, we can also write the above using just boolean connectors:
(define (alternating-signs-in-list? lst)
(or (empty? lst)
(empty? (rest lst))
(and (not (same-sign? (first lst) (second lst)))
(alternating-signs-in-list? (rest lst)))))
Either way, it works as expected:
(alternating-signs-in-list? '(-1 5 -10))
=> #t
(alternating-signs-in-list? '(5 -17 25))
=> #t
(alternating-signs-in-list? '(-1 -5 6))
=> #f
(alternating-signs-in-list? '(1 -2 -6))
=> #f

Check for ascending order of a list in Racket

I'm new to racket and trying to write a function that checks if a list is in strictly ascending order.
'( 1 2 3) would return true
'(1 1 2) would return false (repeats)
'(3 2 4) would return false
My code so far is:
Image of code
(define (ascending? 'list)
(if (or (empty? list) (= (length 'list) 1)) true
(if (> first (first (rest list))) false
(ascending? (rest list)))))
I'm trying to call ascending? recursively where my base case is that the list is empty or has only 1 element (then trivially ascending).
I keep getting an error message when I use check-expect that says "application: not a procedure."
I guess you want to implement a procedure from scratch, and Alexander's answer is spot-on. But in true functional programming style, you should try to reuse existing procedures to write the solution. This is what I mean:
(define (ascending? lst)
(apply < lst))
It's shorter, simpler and easier to understand. And it works as expected!
(ascending? '(1 2 3))
=> #t
(ascending? '(1 1 2))
=> #f
Some things to consider when writing functions:
Avoid using built in functions as variable names. For example, list is a built in procedure that returns a newly allocated list, so don't use it as an argument to your function, or as a variable. A common convention/alternative is to use lst as a variable name for lists, so you could have (define (ascending? lst) ...).
Don't quote your variable names. For example, you would have (define lst '(1 2 3 ...)) and not (define 'lst '(1 2 3 ...)).
If you have multiple conditions to test (ie. more than 2), it may be cleaner to use cond rather than nesting multiple if statements.
To fix your implementation of ascending? (after replacing 'list), note on line 3 where you have (> first (first (rest list))). Here you are comparing first with (first (rest list)), but what you really want is to compare (first lst) with (first (rest lst)), so it should be (>= (first lst) (first (rest lst))).
Here is a sample implementation:
(define (ascending? lst)
(cond
[(null? lst) #t]
[(null? (cdr lst)) #t]
[(>= (car lst) (cadr lst)) #f]
[else
(ascending? (cdr lst))]))
or if you want to use first/rest and true/false you can do:
(define (ascending? lst)
(cond
[(empty? lst) true]
[(empty? (rest lst)) true]
[(>= (first lst) (first (rest lst))) false]
[else
(ascending? (rest lst))]))
For example,
> (ascending? '(1 2 3))
#t
> (ascending? '(1 1 2))
#f
> (ascending? '(3 2 4))
#f
If you write down the properties of an ascending list in bullet form;
An ascending list is either
the empty list, or
a one-element list, or
a list where
the first element is smaller than the second element, and
the tail of the list is ascending
you can wind up with a pretty straight translation:
(define (ascending? ls)
(or (null? ls)
(null? (rest ls))
(and (< (first ls) (first (rest ls)))
(ascending? (rest ls)))))
This Scheme solution uses an explicitly recursive named let and memoization:
(define (ascending? xs)
(if (null? xs) #t ; Edge case: empty list
(let asc? ((x (car xs)) ; Named `let`
(xs' (cdr xs)) )
(if (null? xs') #t
(let ((x' (car xs'))) ; Memoization of `(car xs)`
(if (< x x')
(asc? x' (cdr xs')) ; Tail recursion
#f)))))) ; Short-circuit termination
(display
(ascending?
(list 1 1 2) )) ; `#f`

Scheme sum of list

First off, this is homework, but I am simply looking for a hint or pseudocode on how to do this.
I need to sum all the items in the list, using recursion. However, it needs to return the empty set if it encounters something in the list that is not a number. Here is my attempt:
(DEFINE sum-list
(LAMBDA (lst)
(IF (OR (NULL? lst) (NOT (NUMBER? (CAR lst))))
'()
(+
(CAR lst)
(sum-list (CDR lst))
)
)
)
)
This fails because it can't add the empty set to something else. Normally I would just return 0 if its not a number and keep processing the list.
I suggest you use and return an accumulator for storing the sum; if you find a non-number while traversing the list you can return the empty list immediately, otherwise the recursion continues until the list is exhausted.
Something along these lines (fill in the blanks!):
(define sum-list
(lambda (lst acc)
(cond ((null? lst) ???)
((not (number? (car lst))) ???)
(else (sum-list (cdr lst) ???)))))
(sum-list '(1 2 3 4 5) 0)
> 15
(sum-list '(1 2 x 4 5) 0)
> ()
I'd go for this:
(define (mysum lst)
(let loop ((lst lst) (accum 0))
(cond
((empty? lst) accum)
((not (number? (car lst))) '())
(else (loop (cdr lst) (+ accum (car lst)))))))
Your issue is that you need to use cond, not if - there are three possible branches that you need to consider. The first is if you run into a non-number, the second is when you run into the end of the list, and the third is when you need to recurse to the next element of the list. The first issue is that you are combining the non-number case and the empty-list case, which need to return different values. The recursive case is mostly correct, but you will have to check the return value, since the recursive call can return an empty list.
Because I'm not smart enough to figure out how to do this in one function, let's be painfully explicit:
#lang racket
; This checks the entire list for numericness
(define is-numeric-list?
(lambda (lst)
(cond
((null? lst) true)
((not (number? (car lst))) false)
(else (is-numeric-list? (cdr lst))))))
; This naively sums the list, and will fail if there are problems
(define sum-list-naive
(lambda (lst)
(cond
((null? lst) 0)
(else (+ (car lst) (sum-list-naive (cdr lst)))))))
; This is a smarter sum-list that first checks numericness, and then
; calls the naive version. Note that this is inefficient, because the
; entire list is traversed twice: once for the check, and a second time
; for the sum. Oscar's accumulator version is better!
(define sum-list
(lambda (lst)
(cond
((is-numeric-list? lst) (sum-list-naive lst))
(else '()))))
(is-numeric-list? '(1 2 3 4 5))
(is-numeric-list? '(1 2 x 4 5))
(sum-list '(1 2 3 4 5))
(sum-list '(1 2 x 4 5))
Output:
Welcome to DrRacket, version 5.2 [3m].
Language: racket; memory limit: 128 MB.
#t
#f
15
'()
>
I suspect your homework is expecting something more academic though.
Try making a "is-any-nonnumeric" function (using recursion); then you just (or (is-any-numeric list) (sum list)) tomfoolery.

Scheme / Racket Best Practice - Recursion vs Variable Accumulation

I'm new to Scheme (via Racket) and (to a lesser extent) functional programming, and could use some advise on the pros and cons of accumulation via variables vs recursion. For the purposes of this example, I'm trying to calculate a moving average. So, for a list '(1 2 3 4 5), the 3 period moving average would be '(1 2 2 3 4). The idea is that any numbers before the period are not yet part of the calculation, and once we reach the period length in the set, we start averaging the subset of the list according the chosen period.
So, my first attempt looked something like this:
(define (avg lst)
(cond
[(null? lst) '()]
[(/ (apply + lst) (length lst))]))
(define (make-averager period)
(let ([prev '()])
(lambda (i)
(set! prev (cons i prev))
(cond
[(< (length prev) period) i]
[else (avg (take prev period))]))))
(map (make-averager 3) '(1 2 3 4 5))
> '(1 2 2 3 4)
This works. And I like the use of map. It seems composible and open to refactoring. I could see in the future having cousins like:
(map (make-bollinger 5) '(1 2 3 4 5))
(map (make-std-deviation 2) '(1 2 3 4 5))
etc.
But, it's not in the spirit of Scheme (right?) because I'm accumulating with side effects. So I rewrote it to look like this:
(define (moving-average l period)
(let loop ([l l] [acc '()])
(if (null? l)
l
(let* ([acc (cons (car l) acc)]
[next
(cond
[(< (length acc) period) (car acc)]
[else (avg (take acc period))])])
(cons next (loop (cdr l) acc))))))
(moving-average '(1 2 3 4 5) 3)
> '(1 2 2 3 4)
Now, this version is more difficult to grok at first glance. So I have a couple questions:
Is there a more elegant way to express the recursive version using some of the built in iteration constructs of racket (like for/fold)? Is it even tail recursive as written?
Is there any way to write the first version without the use of an accumulator variable?
Is this type of problem part of a larger pattern for which there are accepted best practices, especially in Scheme?
It's a little strange to me that you're starting before the first of the list but stopping sharply at the end of it. That is, you're taking the first element by itself and the first two elements by themselves, but you don't do the same for the last element or the last two elements.
That's somewhat orthogonal to the solution for the problem. I don't think the accumulator is making your life any easier here, and I would write the solution without it:
#lang racket
(require rackunit)
;; given a list of numbers and a period,
;; return a list of the averages of all
;; consecutive sequences of 'period'
;; numbers taken from the list.
(define ((moving-average period) l)
(cond [(< (length l) period) empty]
[else (cons (mean (take l period))
((moving-average period) (rest l)))]))
;; compute the mean of a list of numbers
(define (mean l)
(/ (apply + l) (length l)))
(check-equal? (mean '(4 4 1)) 3)
(check-equal? ((moving-average 3) '(1 3 2 7 6)) '(2 4 5))
Well, as a general rule, you want to separate the manner in which you recurse and/or iterate from the content of the iteration steps. You mention fold in your question, and this points in the right step: you want some form of higher-order function that will handle the list traversal mechanics, and call a function you supply with the values in the window.
I cooked this up in three minutes; it's probably wrong in many ways, but it should give you an idea:
;;;
;;; Traverse a list from left to right and call fn with the "windows"
;;; of the list. fn will be called like this:
;;;
;;; (fn prev cur next accum)
;;;
;;; where cur is the "current" element, prev and next are the
;;; predecessor and successor of cur, and accum either init or the
;;; accumulated result from the preceeding call to fn (like
;;; fold-left).
;;;
;;; The left-edge and right-edge arguments specify the values to use
;;; as the predecessor of the first element of the list and the
;;; successor of the last.
;;;
;;; If the list is empty, returns init.
;;;
(define (windowed-traversal fn left-end right-end init list)
(if (null? list)
init
(windowed-traversal fn
(car list)
right-end
(fn left-end
(car list)
(if (null? (cdr list))
right-end
(second list))
init)
(cdr list))))
(define (moving-average list)
(reverse!
(windowed-traversal (lambda (prev cur next list-accum)
(cons (avg (filter true? (list prev cur next)))
list-accum))
#f
#f
'()
list)))
Alternately, you could define a function that converts a list into n-element windows and then map average over the windows.
(define (partition lst default size)
(define (iter lst len result)
(if (< len 3)
(reverse result)
(iter (rest lst)
(- len 1)
(cons (take lst 3) result))))
(iter (cons default (cons default lst))
(+ (length lst) 2)
empty))
(define (avg lst)
(cond
[(null? lst) 0]
[(/ (apply + lst) (length lst))]))
(map avg (partition (list 1 2 3 4 5) 0 3))
Also notice that the partition function is tail-recursive, so it doesn't eat up stack space -- this is the point of result and the reverse call. I explicitly keep track of the length of the list to avoid either repeatedly calling length (which would lead to O(N^2) runtime) or hacking together a at-least-size-3 function. If you don't care about tail recursion, the following variant of partition should work:
(define (partition lst default size)
(define (iter lst len)
(if (< len 3)
empty
(cons (take lst 3)
(iter (rest lst)
(- len 1)))))
(iter (cons default (cons default lst))
(+ (length lst) 2)))
Final comment - using '() as the default value for an empty list could be dangerous if you don't explicitly check for it. If your numbers are greater than 0, 0 (or -1) would probably work better as a default value - they won't kill whatever code is using the value, but are easy to check for and can't appear as a legitimate average

Resources