Reversing a simple function in a "creative" way in racket - scheme

I need some help :D.
I have written this procedure that turns a string into a list of numbers:
(define (string->encodeable string)
(map convert-to-base-four (map string->int (explode string))))
I need a function that does the exact opposite. In other words, takes a list of a list of numbers in base 4, turn it into base 10, and then creates a string. Is there a "creative" way to reverse my function or do I have to write every opposite step again. Thank you so much for your help.

A standard Scheme implementation using SRFI-1 List library
#!r6rs
(import (rnrs base)
(only (srfi :1) fold))
(define (base4-list->number b4l)
(fold (lambda (digit acc)
(+ digit (* acc 4)))
0
b4l))
(base4-list->number '(1 2 3))
; ==> 27
It works the same in #lang racket but then you (require srfi/1)
PS: I'm not entirely sure if your conversion from base 10 to base 4 is the best solution. Imagine the number 95 which should turn into (1 1 3 3). I would have done it with unfold-right in SRFI-1.

Depends on how you define "creative". In Racket you could do something like this:
(define (f lst)
(number->string
(for/fold ([r 0]) ([i (in-list lst)])
(+ i (* r 4)))))
then
> (f '(1 0 0))
"16"
> (f '(1 3 2 0 2 1 0 0 0))
"123456"

The relationship you're looking for is called an isomorphism
The other answers here demonstrate this using folds but at your level I think you should be doing this on your own – or at least until you're more familiar with the language
#lang racket
(define (base10 ns)
(let loop ((ns ns) (acc 0))
(if (empty? ns)
acc
(loop (cdr ns) (+ (car ns)
(* 4 acc))))))
(displayln (base10 '(3 0))) ; 12
(displayln (base10 '(3 1))) ; 13
(displayln (base10 '(3 2))) ; 14
(displayln (base10 '(3 3))) ; 15
(displayln (base10 '(1 0 0))) ; 16
(displayln (base10 '(1 3 2 0 2 1 0 0 0))) ; 123456

#naomik's answer mentioned isomorphisms. When you construct an isomorphism, you're constructing a function and its inverse together. By composing and joining isomorphisms together, you can construct both directions "at once."
;; Converts between a base 4 list of digits (least significant first, most
;; significant last) and a number.
(define iso-base4->number
(iso-lazy
(iso-cond
;; an iso-cond clause has an A-side question, an A-to-B isomorphism,
;; and a B-side question. Here the A-side is empty and the B-side is
;; zero.
[empty? (iso-const '() 0) zero?]
;; Here the A-side is a cons, and the B-side is a positive number.
[cons?
(iso-join
cons
(λ (r q) (+ (* 4 q) r))
[first iso-identity (curryr remainder 4)]
[rest iso-base4->number (curryr quotient 4)])
positive?])))
This code contains all the information needed to convert a base 4 list into a number and back again. (The base 4 lists here are ordered from least-significant digit to most-significant digit. This is reversed from the normal direction, but that's okay, that can be fixed outside.)
The first cond case maps empty to zero and back again.
The second cond case maps (cons r q) to (+ (* 4 q) r) and back again, but with q converted between lists and numbers recursively.
Just as a cons cell can be split using first and rest, a positivive number can be split into its "remainder-wrt-4" and its "quotient-wrt-4". Since the remainder is a fixed size and the quotient is an arbitrary size, the remainder is analogous to first and the quotient is analogous to rest.
The first and remainder don't need to be converted into each other, so the first iso-join clause uses iso-identity, the isomorphism that does nothing.
[first iso-identity (curryr remainder 4)]
The rest and quotient do need to be converted though. The rest is a list of base 4 digits in least-to-most-significant order, and the quotient is the number corresponding to it. The conversion between them is iso-base4->number.
[rest iso-base4->number (curryr quotient 4)]
If you're interested in how these isomorphism forms like iso-const, iso-cond, and iso-join are defined, this gist contains everything needed for this example.

Related

How to solve this Racket problem using higher order functions?

I am stuck on Q2.
Q1. Write a function drop-divisible that takes a number and a list of numbers, and returns a new list containing only those numbers not "non-trivially divisible" by the the number.
This is my answer to Q1.
(define (drop-divisible x lst)
(cond [(empty? lst) empty]
; if the number in the list is equal to the divisor
; or the number is not divisible, add it to the result list
[(or (= x (first lst))(< 0 (remainder (first lst) x))) (cons (first lst) (drop-divisible x (rest lst)))]
[else (drop-divisible x (rest lst))]))
(module+ test
(check-equal? (drop-divisible 3 (list 2 3 4 5 6 7 8 9 10)) (list 2 3 4 5 7 8 10)))
Q2. Using drop-divisible and (one or more) higher order functions filter, map, foldl, foldr. (i.e. no explicit recursion), write a function that takes a list of divisors, a list of numbers to test, and applies drop-divisible for each element of the list of divisors. Here is a test your code should pass
(module+ test
(check-equal? (sieve-with '(2 3) (list 2 3 4 5 6 7 8 9 10)) (list 2 3 5 7)))
I can come up with a snippet that only takes the second list, which does the same work as the solution to Q1.
(define (sieve-with divisors lst)
(filter (lambda (x) ((lambda (d)(or (= d x)(< 0 (remainder x d)))) divisors)) lst))
I tried to modify the snippet with 'map' but couldn't make it work as intended. I also can't see how 'foldr' may possibly be used here.
In this case, foldl is the right tool to use (foldr will also give a correct answer, albeit less efficiently, when the divisors are in increasing order). The idea is to take the input list and repeatedly applying drop-divisible on it, once per each element in the divisors list. Because we accumulate the result between calls, in the end we'll obtain a list filtered by all of the divisors. This is what I mean:
(define (sieve-with divisors lst)
; `e` is the current element from the `divisors` list
; `acc` is the accumulated result
(foldl (lambda (e acc) (drop-divisible e acc))
lst ; initially, the accumulated result
; is the whole input list
divisors)) ; iterate over all divisors
I used a lambda to make explicit the parameter names, but in fact you can pass drop-divisible directly. I'd rather write this shorter implementation:
(define (sieve-with divisors lst)
(foldl drop-divisible lst divisors))
Either way, it works as expected:
(sieve-with '(2 3) '(2 3 4 5 6 7 8 9 10))
=> '(2 3 5 7)

Alternating Sum Using Foldr/Foldl (Racket)

Back again with another Racket question. New to higher order functions in general, so give me some leeway.
Currently trying to find the alternating sum using the foldr/foldl functions and not recursion.
e.g. (altsum '(1 3 5 7)) should equal 1 - 3 + 5 - 7, which totals to -4.
I've thought about a few possible ways to tackle this problem:
Get the numbers to add in one list and the numbers to subtract in another list and fold them together.
Somehow use the list length to determine whether to subtract or add.
Maybe generate some sort of '(1 -1 1 -1) mask, multiply respectively, then fold add everything.
However, I have no clue where to start with foldl/foldr when every operation is not the same for every item in the list, so I'm having trouble implementing any of my ideas. Additionally, whenever I try to add more than 2 variables in my foldl's anonymous class, I have no idea what variables afterward refer to what variables in the anonymous class either.
Any help or pointers would be greatly appreciated.
We can leverage two higher-order procedures here: foldr for processing the list and build-list for generating a list of alternating operations to perform. Notice that foldr can accept more than one input list, in this case we take a list of numbers and a list of operations and iterate over them element-wise, accumulating the result:
(define (altsum lst)
(foldr (lambda (ele op acc) (op acc ele))
0
lst
(build-list (length lst)
(lambda (i) (if (even? i) + -)))))
It works as expected:
(altsum '(1 3 5 7))
=> -4
Your idea is OK. You can use range to make a list of number 0 to length-1 and use the oddness of each to determine + or -:
(define (alt-sum lst)
(foldl (lambda (index e acc)
(define op (if (even? index) + -))
(op acc e))
0
(range (length lst))
lst))
As an alternative one can use SRFI-1 List Library that has fold that allows different length lists as well as infinite lists and together with circular-list you can have it alterate between + and - for the duration of lst.
(require srfi/1) ; For R6RS you import (srfi :1)
(define (alt-sum lst)
(fold (lambda (op n result)
(op result n))
0
(circular-list + -)
lst))
(alt-sum '(1 3 5 7))
; ==> -4

function that calculates non-zero inputs of a vector

I'm new to scheme and having difficulty understanding vectors in scheme. I need to create a function that calculates the number of non-zero inputs in
a vector. I need to do this by not converting the vector into a list.
For exampl3.
(non-zero-dim #(3 0 2 4 0 2))
returns 4
My code so far is
(define non-zero-input
(lambda (vector)
(let ((size (vector-length vector)))
do ((position 0 (+ position 1))
(total 0
(if ((not (zero? vector-ref vector position)))
(+ total 1))
(((= position size) total)))))))
However I'm getting this error :do: bad syntax in: (do ((position 0 (+ position 1)) (total 0 (if ((not (zero? vector-ref vector position))) (+ total 1)) (((= position size) total))
How do i fix this error ?
Using the built-in procedures vector-length and vector-filter-not, you can simplify your function as:
(define (non-zero-dim vec)
(vector-length (vector-filter-not zero? vec)))
For example,
> (non-zero-dim #(3 0 2 4 0 2))
4
Some things to consider:
Keep track of your brackets. For example, (zero? vector-ref vector position) should be (zero? (vector-ref vector position)), whereby arity of zero? is one, and arity of vector-ref is two. Similarly with do ... vs. (do ... etc.
if statements must have an else clause (ie. (if condition then else)). For example, (if true 1) would fail, but (if true 1 2) would pass. Hence, (if ((not (zero? vector-ref vector position))) (+ total 1)) would fail.
The racket/vector package has vector-count, which returns the number of elements of a vector that satisfy a given predicate. This makes counting the non-zero values trivial:
#lang racket/base
(require racket/function racket/vector)
(define (non-zero-dim vec)
(vector-count (negate zero?) vec))
(println (non-zero-dim #(3 0 2 4 0 2)) ; 4

If a list is found in another list?

I started to learn Racket and I don't know how to check if a list is found in another list. Something like (member x (list 1 2 3 x 4 5)), but I want that "x" to be a a sequence of numbers.
I know how to implement recursive, but I would like to know if it exists a more direct operator.
For example I want to know if (list 3 4 5) is found in (list 1 2 3 4 5 6 )
I would take a look at this Racket Object interface and the (is-a? v type) -> boolean seems to be what you are looking for?, simply use it while looping to catch any results that are of a given type and do whatever with them
you may also want to look into (subclass? c cls) -> boolean from the same link, if you want to catch all List types in one go
should there be a possiblity of having a list inside a list, that was already inside a list(1,2,(3,4,(5,6))) i'm afraid that recursion is probally the best solution though, since given there is a possibility of an infinit amount of loops, it is just better to run the recursion on a list everytime you locate a new list in the original list, that way any given number of subList will still be processed
You want to search for succeeding elements in a list:
(define (subseq needle haystack)
(let loop ((index 0)
(cur-needle needle)
(haystack haystack))
(cond ((null? cur-needle) index)
((null? haystack) #f)
((and (equal? (car cur-needle) (car haystack))
(loop index (cdr cur-needle) (cdr haystack)))) ; NB no consequence
(else (loop (add1 index) needle (cdr haystack))))))
This evaluates to the index where the elements of needle is first found in the haystack or #f if it isn't.
You can use regexp-match to check if pattern is a substring of another string by converting both lists of numbers to strings, and comparing them, as such:
(define (member? x lst)
(define (f lst)
(foldr string-append "" (map number->string lst)))
(if (regexp-match (f x) (f lst)) #t #f))
f converts lst (a list of numbers) to a string. regexp-match checks if (f x) is a pattern that appears in (f lst).
For example,
> (member? (list 3 4 5) (list 1 2 3 4 5 6 7))
#t
One can also use some string functions to join the lists and compare them (recursion is needed):
(define (list-in-list l L)
(define (fn ll)
(string-join (map number->string ll))) ; Function to create a string out of list of numbers;
(define ss (fn l)) ; Convert smaller list to string;
(let loop ((L L)) ; Set up recursion and initial value;
(cond
[(empty? L) #f] ; If end of list reached, pattern is not present;
[(string-prefix? (fn L) ss) #t] ; Compare if initial part of main list is same as test list;
[else (loop (rest L))]))) ; If not, loop with first item of list removed;
Testing:
(list-in-list (list 3 4 5) (list 1 2 3 4 5 6 ))
Output:
#t
straight from the Racket documentation:
(member v lst [is-equal?]) → (or/c list? #f)
v : any/c
lst : list?
is-equal? : (any/c any/c -> any/c) = equal?
Locates the first element of lst that is equal? to v. If such an element exists, the tail of lst starting with that element is returned. Otherwise, the result is #f.
Or in your case:
(member '(3 4 5) (list 1 2 3 4 5 6 7))
where x is '(3 4 5) or (list 3 4 5) or (cons 3 4 5)
it will return '(3 4 5 6 7) if x ( searched list '(3 4 5) ) was found in the list or false (#f) if it was not found
or you can use assoc to check if your x is met in one of many lists, or :
(assoc x (list (list 1 2) (list 3 4) (list x 6)))
will return :
'(x 6)
There are also lambda constructions but I will not go in depth since I am not very familiar with Racket yet. Hope this helps :)
EDIT: if member gives you different results than what you expect try using memq instead

Do two things in Racket "for loop"

I'm running a for loop in racket, for each object in my list, I want to execute two things: if the item satisfies the condition, (1) append it to my new list and (2) then print the list. But I'm not sure how to do this in Racket.
This is my divisor function: in the if statement, I check if the number in the range can divide N. If so, I append the item into my new list L. After all the loops are done, I print L. But for some unknown reason, the function returns L still as an empty list, so I would like to see what the for loop does in each loop. But obviously racket doesn't seem to take two actions in one "for loop". So How should I do this?
(define (divisor N)
(define L '())
(for ([i (in-range 1 N)])
(if (equal? (modulo N i) 0)
(append L (list i))
L)
)
write L)
Thanks a lot in advance!
Note: This answer builds on the answer from #uselpa, which I upvoted.
The for forms have an optional #:when clause. Using for/fold:
#lang racket
(define (divisors N)
(reverse (for/fold ([xs '()])
([n (in-range 1 N)]
#:when (zero? (modulo N n)))
(displayln n)
(cons n xs))))
(require rackunit)
(check-equal? (divisors 100)
'(1 2 4 5 10 20 25 50))
I realize your core question was about how to display each intermediate list. However, if you didn't need to do that, it would be even simpler to use for/list:
(define (divisors N)
(for/list ([n (in-range 1 N)]
#:when (zero? (modulo N n)))
n))
In other words a traditional Scheme (filter __ (map __)) or filter-map can also be expressed in Racket as for/list using a #:when clause.
There are many ways to express this. I think what all our answers have in common is that you probably want to avoid using for and set! to build the result list. Doing so isn't idiomatic Scheme or Racket.
It's possible to create the list as you intend, as long as you set! the value returned by append (remember: append does not modify the list in-place, it creates a new list that must be stored somewhere) and actually call write at the end:
(define (divisor N)
(define L '())
(for ([i (in-range 1 N)]) ; generate a stream in the range 1..N
(when (zero? (modulo N i)) ; use `when` if there's no `else` part, and `zero?`
; instead of `(equal? x 0)`
(set! L (append L (list i))) ; `set!` for updating the result
(write L) ; call `write` like this
(newline)))) ; insert new line
… But that's not the idiomatic way to do things in Scheme in general and Racket in particular:
We avoid mutation operations like set! as much as possible
It's a bad idea to write inside a loop, you'll get a lot of text printed in the console
It's not recommended to append elements at the end of a list, that'll take quadratic time. We prefer to use cons to add new elements at the head of a list, and if necessary reverse the list at the end
With all of the above considerations in place, this is how we'd implement a more idiomatic solution in Racket - using stream-filter, without printing and without using for loops:
(define (divisor N)
(stream->list ; convert stream into a list
(stream-filter ; filter values
(lambda (i) (zero? (modulo N i))) ; that meet a given condition
(in-range 1 N)))) ; generate a stream in the range 1..N
Yet another option (similar to #uselpa's answer), this time using for/fold for iteration and for accumulating the value - again, without printing:
(define (divisor N)
(reverse ; reverse the result at the end
(for/fold ([acc '()]) ; `for/fold` to traverse input and build output in `acc`
([i (in-range 1 N)]) ; a stream in the range 1..N
(if (zero? (modulo N i)) ; if the condition holds
(cons i acc) ; add element at the head of accumulator
acc)))) ; otherwise leave accumulator alone
Anyway, if printing all the steps in-between is necessary, this is one way to do it - but less efficient than the previous versions:
(define (divisor N)
(reverse
(for/fold ([acc '()])
([i (in-range 1 N)])
(if (zero? (modulo N i))
(let ([ans (cons i acc)])
; inefficient, but prints results in ascending order
; also, `(displayln x)` is shorter than `(write x) (newline)`
(displayln (reverse ans))
ans)
acc))))
#sepp2k has answered your question why your result is always null.
In Racket, a more idiomatic way would be to use for/fold:
(define (divisor N)
(for/fold ((res null)) ((i (in-range 1 N)))
(if (zero? (modulo N i))
(let ((newres (append res (list i))))
(displayln newres)
newres)
res)))
Testing:
> (divisor 100)
(1)
(1 2)
(1 2 4)
(1 2 4 5)
(1 2 4 5 10)
(1 2 4 5 10 20)
(1 2 4 5 10 20 25)
(1 2 4 5 10 20 25 50)
'(1 2 4 5 10 20 25 50)
Since append is not really performant, you usually use cons instead, and end up with a list you need to reverse:
(define (divisor N)
(reverse
(for/fold ((res null)) ((i (in-range 1 N)))
(if (zero? (modulo N i))
(let ((newres (cons i res)))
(displayln newres)
newres)
res))))
> (divisor 100)
(1)
(2 1)
(4 2 1)
(5 4 2 1)
(10 5 4 2 1)
(20 10 5 4 2 1)
(25 20 10 5 4 2 1)
(50 25 20 10 5 4 2 1)
'(1 2 4 5 10 20 25 50)
To do multiple things in a for-loop in Racket, you just write them after each other. So to display L after each iteration, you'd do this:
(define (divisor N)
(define L '())
(for ([i (in-range 1 N)])
(if (equal? (modulo N i) 0)
(append L (list i))
L)
(write L))
L)
Note that you need parentheses to call a function, so it's (write L) - not write L. I also replaced your write L outside of the for-loop with just L because you (presumably) want to return L from the function at the end - not print it (and since you didn't have parentheses around it, that's what it was doing anyway).
What this will show you is that the value of L is () all the time. The reason for that is that you never change L. What append does is to return a new list - it does not affect the value of any its arguments. So using it without using its return value does not do anything useful.
If you wanted to make your for-loop work, you'd need to use set! to actually change the value of L. However it would be much more idiomatic to avoid mutation and instead solve this using filter or recursion.
'Named let', a general method, can be used here:
(define (divisors N)
(let loop ((n 1) ; start with 1
(ol '())) ; initial outlist is empty;
(if (< n N)
(if(= 0 (modulo N n))
(loop (add1 n) (cons n ol)) ; add n to list
(loop (add1 n) ol) ; next loop without adding n
)
(reverse ol))))
Reverse before output since items have been added to the head of list (with cons).

Resources