Scheme - Sorting a List of Lists - list

So, I'm working on sorting in Scheme (MIT Scheme). I've figured out how to sort a basic list of integers, however I can't quite seem to figure out how to sort a list of lists. Here's the code for sorting a list of integers via selection sort:
(define sortdata
(lambda (lst min)
(cond ((null? lst) min) ;null list, return current
((< (car lst) min) (sortdata (cdr lst) (car lst))) ;compare with head of list
(else (sortdata (cdr lst) min))
)
)
)
I'm not very well versed in scheme as I've just started using it. Rather than sort a list like this one: ( 4 8 9 2 6 15 3), I'm trying to sort a lists of lists, but only by one field the sublist (the age in this case). Such as this: (("bob" 17)("mary" 14)) [Assume the first field is the name, and second is the age.]
EDIT: And as such, the sorted list would then look like this: (("mary" 14)("bob" 17))
Much thanks to anyone who can help or point me to a good reference!

Introduction
The basic elements of a search are:
Table: a collection of records
Record: a collection of fields
Field: a value of interest.
Make a language for your records
(define name car)
(define grade cadr)
;; record record -> record
(define (return-record-with-greater-name record1 record2)
(if (name>? record1 record2)
record1
record2))
(define name>?
(make-record-compare-function string> name))
Make a language for comparing records
;;; This is a partial implementation
;; [any? any? -> boolean] [listof any? -> any?] -> [listof any? -> any?]
(define (make-record-compare-function predicate field)
(lambda (record1 record2)
(predicate (field record1) (field record2))))
Make a language for searching tables
;;; This is a partial implementation
;; [any? any? -> any?] -> [listof any? -> any?]
(define (make-table-search comparator)
(define (inner table best-match)
(if (null? table)
best-match
(inner (cdr table)
(comparator best-match
(car table)))))
(lambda (table)
(inner (cdr table)
(car table))))
Usage
racket> (define record1 (list "mary" 14))
racket> (define record3 (list "Ben" -42))
racket> (define record2 (list "bob" 17))
racket> (define sort-by-name
(make-table-search return-record-with-greater-name))
racket> (sort-by-name (list record3 record1 record2))
'("mary" 14)

Since you are using a comparative sorting algorithm it would be fairly easy to sort other things than integers. Basically, what you need to do is parametrize your comparator. In your current example this is the function <, however this can be anything!
Note Your code does not work but you are almost there. So I will leave it as is and just show you how you could do it.
(define sortdata
(lambda (lst min comparer)
(cond ((null? lst) min) ;null list, return current
((comparer (car lst) min) (sortdata (cdr lst) (car lst))) ;compare with head of list
(else (sortdata (cdr lst) min)))))
In this case your comparar could be:
(lambda (x y) (> (cadr x) (cadr y)))
This lambda will take the age of each person (cadr) and compare them with eachother.
Hope this helps!

Related

SICP/Scheme: Procedure that accepts a list of pairs and an element, returns a list of pairs

I'm having some issues with this problem I've been given. It's not homework, it's actually a problem I've encountered at a test and to deepen my understanding I would like to solve it successfully.
The problem is:
make a procedure element-count that takes a list of pairs "lst" and an element "el" and returns a list of pairs where, if the element "el" exists in "lst" as the first member, then the second member gets increased by 1. Otheriwse we add to the end of "lst" a pair (cons el 1).
The wanted output: element-count '((a 2)) b) -> '((a 2 (b 1))
This is what my code so far looks like:
#lang sicp
(define (append list1 list2)
(if (null? list1)
list2
(cons (car list1) (append (cdr list1) list2))))
(define (count-element lst el)
(cond ((eq? (car lst) el) (+ (cdr lst) 1))
(else (append lst (cons (el 1))))))
The first issue I'm having is that it tells me that "b" is undefined. Obviously, b doesn't need to be defined since I want it in my output. How should I fix this? The environment I'm working in is DrRacket.
The main question is how should I go about defining a list of pairs? Or is my code actually okay? Lists and pairs are still quite confusing to me so I apologize for silly questions.
You are pretty close to the correct solution; I think there is just some confusion about how lists and pairs are defined. Here's an implementation and usage of count-element that does what you want:
(define (count-element lst el)
(cond ((eq? (car lst) el) (+ (cdr lst) 1))
(else (append lst (list (cons el 1))))))
(count-element (list (cons 'a 2)) 'b)
There are two things to note.
First, the way to define a list of pairs is like this:
(list (cons 'a 1) (cons 'c 2))
or
'((a . 1) (c . 2))
Second, this is the correct way to use your append method:
(append lst (list (cons 'b 1)))
or
(append lst '((b . 1)))
That's because append is defined so that you pass in two lists, and it combines them into a single new list. That's why the second parameter is a list containing the pair you want to append.

Finding the occurence element in the list in racket

Assume (list "apple" "orange" "apple" "grape" "orange")and produce (list (list 2 "apple") (list 2 "orange") (list 1 "grape")).
The most common fruit will occur first in the produced list.
In the case of ties, order the tied pairs with the fruit in increasing alphabetical order.
use abstract list function such as map,filter, foldr and quicksort in local. no recursion.
i'm not sure how to do it without recursion.
i wrote like this:
(define (function list)
(cond
[(empty? list) empty]
[else
(local
(define (helper1 a b)
(cond
[(equal? a b) a]
[else b]))
(define T (foldr helper1 (first list) (rest list)))
(define (count a)
(cond
[(equal? a T) true]
[else false]))
(define new-list (quicksort (length (filter count list)) >))]
The most efficient way is to use a (mutable) hash table:
(define (count-by-type lst)
; create hash
(define h (make-hash))
; update hash, creating entries if needed, otherwise adding 1 to existing entry
(map (lambda (e) (hash-update! h e add1 0)) lst)
; create list of (count key) elements from hash and sort accordingly
(sort (map (lambda (e) (list (cdr e) (car e))) (hash->list h))
(lambda (x y) (or (> (car x) (car y))
(and (= (car x) (car y)) (string<? (cadr x) (cadr y)))))))
testing:
> (count-by-type (list "apple" "orange" "apple" "grape" "orange"))
'((2 "apple") (2 "orange") (1 "grape"))
I just rehashed my own answer from a previous question. This seems to be a similar assignment, but without struct
Using a hash you could do it with only one pass through the unsorted list, then produced a list that then was sorted with a special <-function that sorts by count, then fruit.
These hints are for a functional solution. First sort the argument (sort list-of-fruits string>?). that the in descending order and oposite of your result. .
Given the list has at least one element:
(let rec ((cur (car sorted-fruits)) (cnt 1) (lst (cdr sorted-fruits)) (acc '()))
(cond ((equal? cur (car lst)) (rec cur (add1 cnt) (cdr lst) acc))
(else (rec (car lst) 1 (cdr lst) (cons (list cnt cur) acc)))))
This will produce a list in ascending order with counts.
If you sort again:
(sort list-of-counts-and-fruit (lambda (x y) (>= (car x) (car y)))
sort in Racket is stable. That means if you have two with equal counts in the list they will end up in their original order. The original order was the ascending animal order so the result is ordered by count descending, then name ascending.
I guess your procedure can be made by chaining these together, perhaps using let to store intermediates to make expressions shorter and more readable.

Two Element List Scheme

I need to write a function that determines if the given list is a pair of elements. The program will simply respond #t if the list contains exactly two elements or #f if it does not, such that:
(zipper? '((a 1)(b 2))) => #t
and
(zipper? '((foo 100)(bar 2 3))) => #f
I'm still fairly new to Scheme so any help would be much appreciated!
Thanks!
It isn't clear if the "correct" input for the procedure is an arbitrary list or a two-element list. If it's strictly a two-element list, this will work:
(define (is-two-element-list? lst)
(and (list? lst)
(= (length lst) 2)))
(define (zipper? lst)
(and (is-two-element-list? lst)
(is-two-element-list? (first lst))
(is-two-element-list? (second lst))))
… And if it's an arbitrary-length list whose elements we want to check, this will work in Racket, using andmap:
(define (zipper? lst)
(andmap is-two-element-list? lst))
If you are not using Racket, then this solution using every will work in any interpreter with SRFIs:
(require srfi/1)
(define (zipper? lst)
(every is-two-element-list? lst))
Either way, notice that the trick was defining the is-two-element-list? procedure, which verifies the two-element-list property, after that we can apply it as needed.
Think of it this way. If the zipper list is '() then the answer is #t. If the zipper list is not '() then if the first element is two elements and the rest is another zipper?, then return #t.
(define (zipper? list)
(or (null? list)
(and (= 2 (length (car list)))
(zipper? (cdr list)))))
or maybe you mean:
(define (zipper? list)
(or (not (pair? list))
(and (= 2 (length list))
(zipper? (list-ref list 0))
(zipper? (list-ref list 1)))))
every element, at any level, has two elements.
> (zipper? '((a 1 2) '(b)))
#f
> (zipper? '(a b))
#t
> (zipper? '(((a (b b)) c) (1 2)))
#t

How to check whether a list only contains sorted (w.r.t first element) 2-elements pairs using Scheme?

I have written this function to 'check' if the list contains only 2-elements pairs like these: ((1 2)(3 1)(6 2)) --- (sorted based on first elements & no repetition of 1st elements). But I am getting errors, can anyone please give an idea:
(define Bag?
(lambda setlist
(cond ((null? setlist) '())
((and (pair? (caar setlist)) (= (length (caar setlist)) 2)))
((> (caar setlist) 0) (< (caar setlist) (car (cdr (car setlist) ))))
(else(Bag? (cdr setlist))))
))
The list traversal doesn't look right. The lambda is incorrectly declaring its parameter, and the second condition is wrong, you're not advancing the recursion there.
It'd better to start from scratch. I'll give you a high-level solution in Racket stating what needs to be checked, it's up to you to rewrite it in terms of simpler procedures:
(define (bag? setlist)
(and (apply < (map car setlist))
(andmap (lambda (e) (and (pair? e) (= 2 (length e))))
setlist)))
The above verifies that the first-elements in each pair appear sorted in ascending order, and that each pair in the list contains exactly two elements.

Scheme List Derangement (Rearrangement of sorts)

im trying to write a function in Scheme where i accept a list and return all the different derangements (look below for definition) as a list of lists
derangement: A list where no item is in the same place as the original list
ex: '(a b c) -> '(cab)
any help is appreciated!
Compute all of the permutations of the input list and then filter out the ones that have an element in the same position as the input list. If you need more detail, leave a comment.
Edit 1:
Define (or maybe it's defined already? Good exercise, anyway) a procedure called filter that takes as its first argument a procedure p and a list l as its second argument. Return a list containing only the values for which (p l) returns a truthy value.
Define a procedure derangement? that tests if a list l1 is a derangement of l2. This will be handy when paired with filter.
The most obvious solution would be something like this:
(define filtered-permutations
(lambda (lst)
(filter
(lambda (permuted-list)
(deranged? permuted-list lst))
(permute lst))))
Since the number of derangements is considerably lower than then number of permutations, however, this is not very efficient. Here is a solution that mostly avoids generating permutations that are not derangements, but does use filter once, for the sake of simplicity:
(define deranged?
(lambda (lst1 lst2)
(if (null? lst1)
#t
(if (eq? (car lst1) (car lst2))
#f
(deranged? (cdr lst1) (cdr lst2))))))
(define derange
(lambda (lst)
(if (< (length lst) 2)
;; a list of zero or one elements can not be deranged
'()
(permute-helper lst lst))))
(define derange-helper
(lambda (lst template)
(if (= 2 (length lst))
(let ((one (car lst))
(two (cadr lst)))
(filter
(lambda (x)
(deranged? x template))
(list (list one two) (list two one))))
(let ((anchor (car template)))
(let loop ((todo lst)
(done '())
(result '()))
(if (null? todo)
result
(let ((item (car todo)))
(if (eq? item anchor)
;; this permutation would not be a derangement
(loop (cdr todo)
(cons item done)
result)
(let ((permutations
(map
(lambda (x)
(cons item x))
(derange-helper (append (cdr todo) done)
(cdr template)))))
(loop (cdr todo)
(cons item done)
(append result permutations)))))))))))