Re-implementing lists with closures in scheme - list

This is purely a curiosity question for me:
So for fun, one can easily redefine cons, car, and cdr like so:
(define cons+
(lambda (a b)
(lambda (f)
(f a b)
)))
(define car+
(lambda (f)
(f (lambda (a b) a))))
(define cdr+
(lambda (f)
(f (lambda (a b) b))))
This creates the basic functionality of a list using closures. In this context, what would be the best way to define an empty list?

There's no particular special value that you need as the empty list; you just need something and then to treat it as the empty list. In Common Lisp, a list is either a cons or the symbol NIL. You could take the same approach and use the symbol NIL, or you could use some other value, so long as you treat it consistently. Part of the beauty (and sometimes, the ugliness) of linked lists in the Lisp families is that they are intensional data structures. They're built out of a bit of structure and a lot of convention. That's why we can use cons cells to implement singly-linked lists, tree, etc.
Now, what does it mean to treat it consistently? Some of that will depend on exactly how you want it to behave. In Common Lisp, you can call car and cdr with the empty list, and you'll get back the empty list. In Scheme, you'll get an error. In the closure-based approach, you've got a "leaky" abstraction in the sense that you can call car+ and cdr+ with values that were not produced by cons+, and you might get a value back.
What interface do you want?
When implementing singly linked lists in the Lisp styles (conses and an empty list), you typically want an interface that will let you check:
(cons? (cons ...)) == true
(empty? empty) == true
(In terms of those, you can implement list?)
(define (list? x)
(or (cons? x)
(null? x)))
After what you've already done, it shouldn't be hard to implement a cons? function. But what about empty?? You actually have a lot of freedom here. You could implement empty? such that there are actually multiple objects that serve as a empty lists. As long as the contract works, it's OK.
Should anything else work?
At the moment, you've got a working implementation of ordered pairs, but it's a leaky abstraction in that some things that weren't created with cons could still be passed to car and cdr without any error. You've satisfied the axioms
(car (cons x y)) == x
(cdr (cons x y)) == y
but not the implications
(car z) == x ⇒ z == (cons x _)
(cdr z) == y ⇒ z == (cons _ y)
If you can find a way to make those implications true, so that car and cdr only work with things produced by cons, then you can probably figure out how to implement cons?, and you can use the same technique to generate a unique empty list object.

Related

Iterative filtering odd numbers in list

Here is a task I have to solve: Write an iterative function, that receives a list with numbers and creates a new list, which only consists of the even numbers from the received list.
I even found a few posts with similar questions and solutions, but I couldn't use them as help, because in these solution they were using the "car" and "cdr" commands, which we haven't had in our schedule yet. How can do this? I would start like this:
(define filter-odds
(lambda(x)
(if (odd? x)
...)
Terrible oversight: no one has mentioned How To Design Programs, here. This kind of program is right smack in the middle of HtDP's path. Specifically, this is a totally straightforward application of the List template from section 9.3, "Programming with Lists." Perhaps your class is already using HtDP?
Well, in the absence of car ⁄ cdr accessors, your only other choice is using reduce from SRFI1:
(define filter-odds
(lambda (lst)
(reduce-right
(lambda (e ; list's element
acc) ; accumulated results from the right
(if (odd? e)
.... ; combine e and the results-so-far
.... )) ; ignore e
'()
lst )))
But, right reduce most likely won't be iterative. Left reduce usually is. It will build the result in reverse order though, but you can reverse a list iteratively in another pass, using the same approach, without filtering at all (i.e. using the test procedure which always succeeds).
With the first ⁄ rest accessors available though, all you need to do is to write down the implementation for reduce yourself, then fuse it into the definitions above:
(define reduce
(lambda (f acc ls)
(if (null? ls)
acc
(reduce f (f acc (first ls)) .....))))
In other words, just follow the above code skeleton and add replace the call to f with some checks for odd? or something, to get the filter-odd?-reversed; then figure out the f to use so as to define the reverse-list function.

if statement in lambda Scheme

I'm a new schemer. I just want to ask if I can include if-statements in a lambda? For example, (lambda (x) (if e1 e2 e3)). I don't see why not, but my program just keep failing if I write this way.
Thanks a lot!
Here's my code here.I'm trying to implement filter with higher-order functions as an exercise. Since #sepp2k has answered that it's totally fine to include ifs in lambda, I'd guess it's the problem of my use of foldr?
If anyone can give some insight into this to help me understand the way it works, I'd really appreciate it!
(define filter (f xs)
(if (null? xs) '()
(foldr (lambda (elem ys) ((if (f elem) (cons elem ys)
(cons '() ys)))) '() xs)))
Of course we can use an if inside a lambda, in fact, any valid expression can go inside a lambda. There are more serious errors in your code:
There are additional (and unnecessary) parentheses surrounding the innermost if expression. Scheme will interpret them as a function application, and that's not what you want to do here
The else of the if expression is wrong, you just have to pass the accumulator without modifications
The function definition is incorrect
Also the whole function can be simplified, there's no need for the outermost if and the indentation can be improved. Try this:
(define (filter f xs)
(foldr (lambda (elem ys)
(if (f elem) ; if the predicate is true
(cons elem ys) ; then `cons` element to accumulator
ys)) ; otherwise leave accumulator alone
'()
xs))
It works as expected:
(filter even? '(1 2 3 4 5 6))
=> '(2 4 6)
Yes, it is perfectly valid to use ifs inside lambdas. If your program fails that must be because of other reasons.
Extra parens around the call to if. Scheme overloads grouping (list of expressions) and function-calls (+ 2 2).
FWIW, you might also use foldl, as foldr takes up O(|elements of the input|) memory.
Going deeper, lambdas are what's called a "special form" in scheme. That means that they obey different evaluation rules. I only bring this up because it gives some insight into a key feature of scheme: everything is just a form. define? Special form. lambda? Special form. Nothing you couldn't write yourself if you really felt like it. That means that with a lot of special forms, even if they seem automagic/baked-in, they don't have any esoteric caveats lying around.
Also, just for future reference, here are a couple of gems for your scheming: MIT Scheme Reference (lambdas), Racket Guide (Pairs and Lists)

What's the point of building a list with a non-list tail?

The Emacs lisp manual states about the function nconc that:
Since the last argument of nconc is not itself modified, it is reasonable to use a constant list, such as '(4 5), as in the above example. For the same reason, the last argument need not be a list
And indeed I can write
(setq x '(1 2 3))
=> (1 2 3)
(nconc x 0)
=> (1 2 3 . 0)
but that yields a totally broken list:
(length x)
=> eval: Wrong type argument: listp, 0
(butlast x)
=> butlast: Wrong type argument: listp, 0
How can I retrieve the original list? (reverse (cdr (reverse '(1 2 3 . 0)))) doesn't cut it either.
In which contexts is this a useful pattern? In the standard distribution some functions in minibuffer.el use it, in particular completion-all-completions and the like.
They're not "broken" lists; they're actually known as improper lists (as opposed to nil-terminated lists, which are proper lists). Many list functions, such as length and butlast that you just named, expect proper lists, and listp returns true only for proper lists.
Improper lists are used in association lists (where the associations are often not proper; though the alist itself must be proper).
If you want to make an improper list proper, you have two options:
Remove the "improper" element.
Treat the improper element as the last element of the proper list.
Here's a procedure I wrote called properise which will do the former:
(defun properise (x)
(let ((r nil))
(while (consp x)
(push (pop x) r))
(nreverse r)))
(If you want the latter behaviour, add (unless (null x) (push x r)) just before the nreverse line.)
Generally I would avoid creating data structures like that, where the last element is a cons cell with some object other than NIL in the cdr... It makes debugging harder, it's a hack, makes code more difficult to understand, ...
I'm still unsure why this is a good pattern, but here's an easy way of getting a proper list out of an improper one, without making a copy:
(defun nmake-proper-list (x)
(let ((y (last x)))
(setcdr y nil)
x))

Racket - output content of a list

I have defined a list (in Racket/Scheme):
(define myList (cons 'data1 (cons 'data2 (cons 'data3 (cons 'data4 empty)))))
or
(list 'data1 'data2 'data3 'data4)
And I want to write a function that cycles through the list and outputs all values of the list.
(define (outputListData list)
(cond
[(null? list) list]
[else (getListData)]))
With what function can I cycle through the content of the list? I know one can use first & rest to get list data, but I guess that's not the right way here.
BTW: Is there a good, compact racket reference like php.net? I find the official Racket docs very confusing ...
You can use a for loop. Example:
(for ([x (list 1 2 3)])
(printf "~s -> ~s\n" x (* x x)))
There are more functional ways to do this, of course, but this way works too. You'll probably want to look at a textbook like How To Design Programs to do the recursive approach. See: http://www.ccs.neu.edu/home/matthias/HtDP2e/
dyoo's solution is nice and succinct in a Scheme like Racket that has useful iteration routines built in. Just FYI, though, your 'outputListData' is not far from being the standard recursive way to do this. You just need to change a couple of lines:
(define (outputListData list)
(cond
[(null? list) #f] ; actually doesn't really matter what we return
[else (printf "~s\n" (first list)) ; display the first item ...
(outputListData (rest list))])) ; and start over with the rest
Since this is an "imperative" kind of procedure that isn't designed to return a value, it doesn't really matter what we do with an empty list so long as we stop recurring (to avoid an infinite loop). If the list isn't empty, we output the first element and start over recursively with the rest of the list.
BTW, here's another way you could write something almost identical if you just needed a "for" loop in the middle of some other function:
(let loop ((l (list 'foo 'bar 'baz 'quux))) ; or put whatever input you need
(cond ((null? l) #f)
(else
(printf "~s\n" (first l))
(loop (rest l)))))
One way to think about this "named let" is that it defines a temporary function called loop, which works just like outputListData above. Scheme has the nice property that it won't grow the stack for "tail calls" like these, so you can always write what would be an "iterative" for or while loop in this recursive style.
I highly recommend The Little Schemer by Friedman and Felleisen for a quick intro to this style of function writing! I found it through Douglas Crockford's page here.
Edit as per comments: Use for-each
(for-each display myList)
Try this:
(void (map display myList))
Breaking it down:
(void x) causes x to be ignored instead of returned to the REPL/parent expression as a value.
(map function lst): For a list '(a1 a2 ... an) returns the list '((function a1) (function a2) ... (function an)).
So we use map to display all the items, but since we only care about the side-effect and not the return value, we call void on the returned list.
Official docs:
void
map
I think the solution that is the easiest to understand it to come up with a so called "list-eater" function. This is the way my university introduced recursion and lists in Racket. Also most books on Racket (i.e. "How To Design Programs" or "Realm Of Racket") explain it this way. This is the code:
(define my-list (list 'data1 'data2 'data3 'data4))
(define (print-list a-list-of-data)
(when (not (empty? a-list-of-data))
(print (first a-list-of-data))
(print-list (rest a-list-of-data))))
If you call the function with the example list my-list, you will get the following output:
(print-list my-list)
'data1'data2'data3'data4
The function does the following: As long as the given list is not empty, it grabs the first element of that list and passes it to the function print. Then, it tells itself to do the exact same thing with the rest of the list. (It calls itself on the rest of the list.) This second part is what they call recursion.
However, you can shorten that by using a function called map:
(define (print-list a-list-of-data)
(map print a-list-of-data))
This basically says that you want the function print to be called on each element of the given list. The output is exactly the same.
Hope it helped!

Scheme matching elements

I'm currently playing a bit around with scheme, and I've been stuck for quite a while trying to figure out how to implement a test, that checks if any element in 'xs' is also stored in 'l'.
That is the mathematical function '\in', just for each element in the list xs.
Do you want to write it yourself for practice, or do you just need the functionality?
If you just want the functionality then use the answer by larsmans (I have never used that, but it looks good to me).
To implement try the following (disclaimer: I have not tested this)
(define has-intersect?
(lambda (xs l)
(cond
[(null? xs) #f]
[(member? (car xs) l) #t]
[else (has-intersect? (cdr xs) l)])))
In mathematical terms, you're checking whether the intersection of two lists/sets is non-empty. If you have an SRFI-1 library, this is trivial:
(not (null? (lset-intersection l xs)))
An implementation of SRFI-1 can be found in SLIB.
(Disclaimer: I'm a former SLIB contributor.)