0

I need to implement a procedure called inverse-tree that receives a tree whose nodes data values are numbers and booleans and returns the equivalent tree whose nodes satisfy the following:

  • If the equivalent node of the original tree is a number, then the resulting tree’s node is −1· that node value
  • If the equivalent node of the original tree is a boolean, then the resulting tree’s node is the logical not of that node value

Examples:

> (inverse-tree ’())
’()
> (inverse-tree ’(5))
’(-5)
> (inverse-tree ’(0))
’(0)
> (inverse-tree ’(#f))
’(#t)
> (inverse-tree ’(#t))
’(#f)
> (inverse-tree ’(-5 (1 (-2) (3) (#f)) (#t)))
’(5 (-1 (2) (-3) (#t)) (#f))

The representation of a (possibly empty or non-complete) tree in Scheme is as follows: the first element in every nesting level represents the root of the sub-tree. The rest of the elements are the children (each of them is a tree, of course). A leaf is represented by a list with only one element (the leaf value). A completely empty tree is represented by the empty list.

more info at Scheme altering tree values.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Adam Morad
  • 167
  • 1
  • 7
  • What are you having problems with? Traversing a tree or something else? – molbdnilo Apr 24 '18 at 10:56
  • I suspect that this is an exercise in whether you've understood list recursion (and the usefulness of `map`), so you might want to review that. – molbdnilo Apr 24 '18 at 10:59
  • How can I traverse the tree and change the values at the same time? – Adam Morad Apr 24 '18 at 11:07
  • how can I traverse a list? (define (t xs) (cond ((pair? xs) (cons (car xs) (t (cdr xs)))) (else xs))). how can I traverse a list and change the values at the same time? (define (tc f xs) (cond ((pair? xs) (cons (... (car xs)) (tc f (cdr xs)))) (else xs))) how can I traverse a tree and change the values at the same time? .... – Will Ness Apr 24 '18 at 11:10
  • You transform the tree in pretty much the same way that `map` traverses a list and "changes" it at the same time. That is, you create a new tree with the same structure but with different values. Start with writing down the possible cases of a tree shape (i.e. empty, leaf, and non-empty) then define what the result should be for each case. – molbdnilo Apr 24 '18 at 11:10
  • There can be more then 2 - the number is flexible. And it needs to be in the same function - no helper functions. – Adam Morad Apr 24 '18 at 11:15
  • OK, so what have you tried? Can you follow the logic in the snippets in my comment above? First assume there's only one child, always. It's a valid tree after all. – Will Ness Apr 24 '18 at 11:17
  • I followed it but I only have one parameter which is the tree itself. (define inverse-tree (lambda (tree) "Implement me")) – Adam Morad Apr 24 '18 at 11:18

4 Answers4

2

Write down what the possible inputs are (there are three cases in the description of what a tree looks like) and define the result for each case.

Here is the general tree-traversal you need to implement:

  • If the tree is empty:
    • Produce the empty tree
  • If the tree is a leaf:
    • Produce an inverted leaf
  • Otherwise:
    • Invert the node's value
    • Produce a new tree by combining this inverted value with the results of inverting each subtree

You're probably going to want to read about map in your favourite Scheme book.

It's possible (but I suspect that it's a later exercise) to generalise this so it applies an arbitrary function in the tree, just like map does with lists.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • I ain't allowed to use map unfortunately. – Adam Morad Apr 24 '18 at 11:27
  • @AdamMorad You can also recurse explicitly, of course (but "boo" to your teacher for not encouraging abstraction). – molbdnilo Apr 24 '18 at 11:30
  • @molbdnilo "BOO" for teaching recursion? I would think 36k user, top 0.49% this week can refrain from this type of comment. – rsm Apr 25 '18 at 10:10
  • @rsm Normally, list recursion is done in excruciating detail before approaching trees, and ultimately teaches you how to use abstractions like `map` and `fold`. *Teaching* recursion is good, and so is teaching good habits like abstraction and reuse. Forcing bad habits, like avoiding `map` even when it's the perfect tool *and* a good illustration of both abstraction and reuse, deserves a "boo" in my book. – molbdnilo Apr 25 '18 at 11:27
  • @molbdnilo And how did you figure out this excercise was *"Forcing bad habits"*? This is not discussion forum, your personal opinions are out of place here. Again - I would expect 36k user to know better. EOT – rsm Apr 25 '18 at 11:33
1

The explanation for the tree structure doesn't seem to work. (1 (-2) (3) is a node, but 1 is not a parent. However it seems you can look at the whole thing as a binary tree where pairs are nodes and the empty list is an empty tree and any other value is a leaf node.

The abstraction would look like this:

(define make-tree cons)
(define tree-left car)
(define tree-right cdr)
(define tree? pair?)
(define empty-tree '())

You can map over the values of a tree like this:

(define (map-tree proc tree)
  (cond ((eq? empty-tree tree) empty-tree)
        ((tree? tree) 
         (make-tree (map-tree proc (tree-left tree))
                    (map-tree proc (tree-right tree))))
        (else (proc tree))))

Example:

(map-tree (lambda (v) (+ v 1)) '(1 (2) 3))
; ==> (2 (3) 4)

Obviously you need to replace the first argument with a procedure that works like this:

(inverse #f) ; ==> #t
(inverse #t) ; ==> #f
(inverse 5)  ; ==> -5
;; optional
(inverse 'xxx) ; ==> xxx

Then it's easy to make your procedure:

(define (inverse-tree tree)
  (map-tree inverse tree))

(inverse-tree ’(-5 (1 (-2) (3) (#f)) (#t)))
; ==> (5 (-1 (2) (-3) (#t)) (#f))

Now if you cannot use abstractions you would need to use the substitution rules. eg.

(define (tree-inc v)
  (map-tree (lambda (v) (+ v 1)) v))

; ==
(define (tree-inc tree)
  (cond ((eq? '() tree) '())
        ...
        (else (+ (car tree) 1))))

There you have it. More difficult to read and reason about than one that uses abstractions. Good luck with that.

Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • `[1 [-2] [3]]` is a tree. `[1 _ _ ]` is a top node of that tree. `[1]` is a tree, and a leaf node. `[1 []]` is a tree, a node with one empty child. `[1 [-2] [3 [] [4 [] [5] []] [] [6]] [7]]` is a tree, a node with 4 children. Its breadth-first enumeration is `[1 -2 3 7 4 6 5]`. I don't see a problem with that. – Will Ness Apr 24 '18 at 15:29
  • @WillNess The OP writes "the **first element** in every nesting level represents the root of the sub-tree". thus `[1 [-2] [3]]` is a tree with the parent being `1`. It probably is that the there are no reference to any parent except that in connection to the list of nodes in the `cdr` the current (whole) node is the parent, but that is implied. My code still works since the nodes would be processed in the same manner with the binary tree goggles as with the real model. – Sylwester Apr 24 '18 at 16:48
  • I'm not allowed to use abstraction. If you can give more details on the last solution and explain how can I do it in 1 function because that is the request. – Adam Morad Apr 24 '18 at 17:23
  • @AdamMorad You simply expand the abstractions. Look at the the `tree-inc` example. You see the body is replaced with the body of `map-tree` and the call to `proc` is replaced with the body of the argument `(+ v 1)` and `v` is of course `(car tree)`. Pure substitutions. – Sylwester Apr 24 '18 at 17:57
  • Is it possible to use it in the following manner and using the negate/not key words? I'm not allowed to change the function definition `(define inverse-tree (lambda (tree) (cond ((eq? '() tree) '()) ((pair? tree) (make-tree (inverse-tree (car tree)) (inverse-tree (cdr tree)))) (else (tree)))))` – Adam Morad Apr 24 '18 at 18:37
  • @AdamMorad where would you consider using `not`? Btw the alternative `(tree)` will try to call `tree` as a procedure. – Sylwester Apr 24 '18 at 19:24
  • breadth-first enumeration can be different. consider `[3 [4 [5]] [] [] [] [] [] [] [6]]`. no matter the amount of `[]`s in front of `[6]` the BF enumeration for rose-tree interpretation is always `[3 4 6 5]`, but with binary it becomes `[3 4 5 6]` when there are sufficiently many of them. so there is a difference. – Will Ness Apr 25 '18 at 06:57
  • @WillNess Yes. One cannot iterate all trees as if it was binary, but in this particular case where just the structure mattered and not the order the result will be identical. in CL all tree functions would work on these trees as well, eg. `copy-tree` and 'tree-equal', even though they clearly represent binary trees. If CL has a breadth-first function it would not work since the model does matter. – Sylwester Apr 25 '18 at 10:42
  • @AdamMorad Since you know at the `else` that you are dealing with a single value you can continue terms like `((boolean? tree) (not tree))` etc. – Sylwester Apr 25 '18 at 10:44
1

After a lot of digging and understanding the syntax better I cam up with a simple 1 function solution:

(define (inverse-tree tree)
  (cond ((eq? '() tree) '())
        ((pair? tree) 
         (cons (inverse-tree (car tree))
                    (inverse-tree (cdr tree))))
        (else ((lambda(x) (if (boolean? x) (not x) (- x))) tree))))
Adam Morad
  • 167
  • 1
  • 7
  • You can squash the `if` and put terms in the `cond` like `((boolean? tree) (not tree))` and `((number? tree) (- tree))` and perhaps keep the `else` for values that are not numbers or boolean. What should happen if you try `(inverse-tree '(1 (2) (#f) (symbol)))` ? In your current implementation pigs will fly. If that is ok is entirely up to the assignment, but at work I would throw an exception or handle that case. – Sylwester Apr 25 '18 at 10:50
  • 1
    What @Sylwester is telling you is that `((lambda(x) (if (boolean? x) (not x) (- x))) tree)` === `(if (boolean? tree) (not tree) (- tree))` and `(cond ... (else (if T C A)))` === `(cond ... (T C) (else A))`. – Will Ness Apr 25 '18 at 11:28
1

I think that should work:

  (define inverse-tree
  (lambda (tree)
    (if (null? tree)
        '()
        (if (number? (car tree))
            (cons (* -1 (car tree)) (inverse-tree (cdr tree)))
            (if (boolean? (car tree))
                (cons (not (car tree)) (inverse-tree (cdr tree)))
                (cons (inverse-tree (car tree)) (inverse-tree (cdr tree))))))))