Hi I'm trying some exercises on Scheme and I'm able to do them using Python (using hashmaps). I have a web link graph made of website links, so that for each web site considered, I know
those websites that contain links to it.
I'm trying to make a function to represent the reversed web link graph. I'm trying to using lists for the representation.
If the input is [[a, b, c], [b, c, e], [c, b, a]] this means the site a is linked to from the sites b and c, the site b is linked to from c and e, and the site c is linked to from the sites b and a.
Thus my function should return the list: [[a, c], [b, a, c], [c, a, b], [e, b]]. In this case, the first element of each sublist is the source site and the tail represent all the destination sites that it links to.
Thanks for your help!
This is the Python code that seems to works, just for reference:
def reverse_linkgraph(G):
hash_map = {}
for u, *v in G:
for to in v:
if to not in hash_map:
hash_map[to] = set()
hash_map[to].add(u)
rev_g = []
print(hash_map)
for k,v in hash_map.items():
rev_g.append([k, *v])
return rev_g
Here's a racket equivalent to your python code:
#lang racket
(define (reverse-linkgraph list-of-links)
(let* ((empty-set (seteq))
(link-table (for/fold ([table (make-immutable-hasheq)])
((links list-of-links))
(let ((link-to (car links)))
(for/fold ([table table])
((link-from (cdr links)))
(hash-update table link-from (lambda (link-set) (set-add link-set link-to))
empty-set))))))
(hash-map link-table (lambda (from to) (cons from (set->list to))) #t)))
;;; ((a c) (b a c) (c a b) (e b))
(writeln (reverse-linkgraph '((a b c) (b c e) (c b a))))
The only real major difference is that it uses functional, immutable hash tables and sets, instead of updating mutable ones in place, and thus constructs a hash table of sets using nested folds to incrementally build up the final one.
Related
Tasked with adding a to end of (b c) to make (b c a)
So far when I try
(print (cons 'a '(b c)))
I get (a b c)
but when I do
(print (cons '(b c) 'a))
I get ((b c) . a)
All the other similar questions on Stack seem to be more complicated than this problem so I was wondering if there was an easy fix.
A list is a chain of pairs. The elements are the cars of each pair, the cdr is a reference to the next pair in the chain, or an empty list for the last pair in the chain.
When you use (cons 'a '(b c)) you create a new pair in front of the existing list (b c), so the result is still a list.
But when you use (cons '(b c) 'a), you're creating a pair whose cdr is the symbol a, not a list. And the last pair in the list (b c) still has its cdr pointing to the empty list.
You need to copy the first list, and when you get to the end, you have to make the cdr point to a list containing a. You can do this with a recursive procedure.
(define (list-append old-list new-el)
(if (null? old-list)
(list new-el)
(cons (car old-list)
(list-append (cdr old-list) new-el))))
(list-append '(b c) 'a)
The logic is:
If we try to append to an empty list, just return a list containing the new element
Otherwise, append the new element to the tail of the original list with the recursive call, and then put the first element in front of that (using the (cons new-element old-list) method that you showed in your first example).
What is the simplest way to roll a list ?
Consider the following list :
myList : [0,1,4,6,3]
I am looking for a roll() function that would do :
(%i0) roll(myList,1)
(%o0) [3,0,1,4,6]
(%i1) roll(myList,-1)
(%o1) [1,4,6,3,0]
I can achieve the same result by calling :
myItem : pop(myList)
myList : append(myList,myItem)
Problem is that this works in one direction only (there is no pop_back() function to my knowledge (?)) and that it is a two liner. Any better way to do that ?
Well, there isn't a built-in function for that. But I think you can use rest to get the effect you want.
(%i10) rotate (e, n) :=
if atom(e) then e
else block ([a : args(e)],
apply (op(e),
append (rest (a, length(a) - n), rest (a, -n)))) $
(%i11) foo : [a, b, c, d, e, f, g];
(%o11) [a, b, c, d, e, f, g]
(%i12) rotate (foo, 2);
(%o12) [f, g, a, b, c, d, e]
(%i13) rotate (foo, 7);
(%o13) [a, b, c, d, e, f, g]
This works for all expressions, not just lists.
(%i16) rotate (f(1,2,3), 2);
(%o16) f(2, 3, 1)
This implementation doesn't take negative n or n greater than the number of arguments, although I think it would be easy to handle that.
I've assumed that rotate moves elements at lesser indices into greater indices. Again, if you want the default to go in the other direction, I think it would be easy to do that.
EDIT: Actually it isn't necessary to separate out op(e) and args(e). You can call rest(e, ...) when e is not a list and it does the right thing. So a more concise version is:
rotate (e, n) :=
if atom(e) then e
else append (rest (e, length(e) - n), rest (e, -n)) $
I want to get the second value of '(a b c) and I don't want to use cadr.
I can get the right answer:
(car (cdr '(a b c)))
'b
But when I built the function:
(define test (lambda (list) (car (cdr (list)))))
(test '(a b c))
I get the following error:
. . application: not a procedure;
expected a procedure that can be applied to arguments
given: '(a b c)
arguments...: [none]
I really don't know what's this error means.
There are incorrect parentheses in your code, surrounding the list parameter - in Scheme this: (f) means "apply the f function with no arguments", so in your code this: (list) is trying to invoke the list parameter as if it were a function, which is not, raising an error.
Also notice that it's a bad idea to call list the parameter, there's already a built-in procedure with that name; that's why I renamed it to lst. This should fix it:
(define test
(lambda (lst)
(car (cdr lst))))
(test '(a b c))
=> b
I'm trying to implement this logic in Clojure (just an example):
a = 1 + 5
b = a + 3
c = a + 4
d = c + a
return f(a, b, c, d)
The best code I've managed to write so far looks like:
(let [a (+ 1 5) b (+ a 3) c (+ a 4) d (+ c a)] (f a b c d))
This looks rather cumbersome, mostly because in my real-life case these "add" operations are much more complicated and may take a few lines of code. I would rather prefer to write it like (Lisp style):
(set a (+ 1 5))
(set b (+ a 3))
(set c (+ a 4))
(set d (+ c a))
(f a b c d)
Is it possible in Clojure?
No and that is by intent, as the (set ...) calls you're describing imply the use of mutable state in the way languages like Java and C# do. This is something Clojure actively avoids in order to manage state in a more sane way, something that really becomes important in concurrency. For more information I refer you to the Clojure website.
Furthermore, the let form is not cumbersome, it is a useful scoping tool:
In your example a, b,c and d are all local to let. What this means is that once the instruction pointer steps out of the let, all of those bindings are forgotten.
In contrast, even if your (set...) example were to work, you would have polluted your namespace with all of these ephemeral names.
Actually, you almost found the best solution possible in Clojure:
(let [a (+ 1 5)
b (+ a 3)
c (+ a 4)
d (+ c a)]
(f a b c d))
You can't write Clojure code in imperative style, because it's a functional language. You can't freely use defs either, because all variables in Clojure are immutable. So, ones defined the can't be changed. So, if you want to temporary bind some variables, you should use let.
The set function in Clojure creates a set data type from another collection container as opposed to mutating the values of the variables. However, you could do the following:
(def a (+ 1 5))
(def b (+ a 3))
(def c (+ a 4))
(def d (+ c a))
(f a b c d)
The let statement allows you to do the same thing but not "pollute" your top-level namespace with the a, b , c, and d values. However, if you want to be able to hang on to and reference a, b, c, and d, a def would do the trick.
This is #7 of of 99 Lisp problems: transform a list, possibly holding lists as elements into a `flat' list by replacing each list with its elements (recursively). I have tried several solutions, e.g from #2680864 or from here. They all work, but I run into a problem if I am flattening a list containing a quoted element. E.g.:
> '(a 'b c)
(A 'B C)
> '(a (quote b) c)
(A 'B C)
> (flatten '(a 'b c))
(A QUOTE B C)
In the latter case I would like to get:
(A 'B C)
It seems that the internal representation of ' gets in the way for this task! SBCL, CLISP, ECL, ... they all behave the same way.
Quoted elements in a list? That usually does not make sense. Why would you have it quoted?
(a b c) is a list of three symbols. Why would you quote elements in the list? like in (a 'b c)? Why? What would be the purpose of the quote?
In Common Lisp ' is a readmacro which expands 'a into (QUOTE A). Since this is a normal list, a typical flatten operation will collect the symbols QUOTE and A into the flat list. This is because a flatten function typicall checks whether something is an atom or not. If you don't want this, your flatten function needs to check if something is an atom or a two-element list with QUOTE as its first symbol.
But as I said above, the default usage is just to flatten symbols, since quoted symbols are usually not useful inside a list. You need to extend the flatten function otherwise.
For example:
(defun flatten (l &key (test #'atom))
(cond ((null l) nil)
((funcall test l) (list l))
(t (loop for a in l nconc (flatten a :test test)))))
CL-USER > (flatten '(a (('b) c) ('d) )
:test (lambda (item)
(or (atom item)
(and (eq (first item) 'quote)
(null (cddr item))))))
(A (QUOTE B) C (QUOTE D))