abhirag
2019-3-22 10:38:34

@abhirag has joined the channel


abhirag
2019-3-22 11:19:23

Hi all, I have been trying to get the hang of racket syntax by implementing some simple algorithms. One of them was reversing a list. First I implemented a function which takes in an immutable list and returns the reversed list: (define (reverse-list-rec iml) (define/match (do-reverse-list iml acc) [('() acc) acc] [((cons head tail) acc) (do-reverse-list tail (cons head acc))]) (do-reverse-list iml '())) then I tried implementing a function which takes in a mutable list, reverses it in-place and also returns the reversed list: (define (reverse-list-iter! ml) (let loop ([prev '()] [curr ml]) (cond [(null? curr) prev] [else (let ([next (mcdr curr)]) (set-mcdr! curr prev) (loop curr next))]))) The issue here is that it does return the reversed list but the in-memory mutation is not equal to the reversed list: >> (define mutable-list (mlist 1 2 3 4 5)) >> (reverse-list-iter! mutable-list) (mcons 5 (mcons 4 (mcons 3 (mcons 2 (mcons 1 '()))))) >> mutable-list (mcons 1 '())


abhirag
2019-3-22 11:20:25

So I thought I would refer to the official implementation of mreverse! : https://github.com/racket/compatibility/blob/5b67b56fe7f5b1a717e2aacf797a603ad4fed68d/compatibility-lib/compatibility/mlist.rkt#L153 which to my surprise is similar to mine and has the same behavior: >> (define mutable-list (mlist 1 2 3 4 5)) >> (mreverse! mutable-list) (mcons 5 (mcons 4 (mcons 3 (mcons 2 (mcons 1 '()))))) >> mutable-list (mcons 1 '())


abhirag
2019-3-22 11:23:58

Now I am not sure if mreverse! is supposed to behave like this but I would appreciate any help in regards to how would I go about writing a function which can reverse a mutable-list in-place. That would help me understand mutability a bit more in racket (even though its not the rackety way of doing things). Thanks


soegaard2
2019-3-22 12:36:26

@abhirag The problem is that (define mutable-list ...) points to the first pair in the original list.


soegaard2
2019-3-22 12:37:03

After the list is reversed, mutable-list still points at the same pair - but since the list is reversed, that pair is now the last pair in the list.


soegaard2
2019-3-22 12:37:31

So (mcons 1 ()) means that the last pair of the reversed list is a list with a single 1.


soegaard2
2019-3-22 12:37:39

And that’s correct.


soegaard2
2019-3-22 12:38:06

So to see the reversed list, you will need to store where the last pair is, before reversing the list.


soegaard2
2019-3-22 12:41:56
#lang racket
(require compatibility/mlist)

(define mutable-list (mlist 1 2 3 4 5))
(define last-pair (mcdr (mcdr (mcdr (mcdr mutable-list)))))
last-pair
(mreverse! mutable-list)
last-pair

soegaard2
2019-3-22 12:42:00

The output is:


soegaard2
2019-3-22 12:42:10
(mcons 5 '())
(mcons 5 (mcons 4 (mcons 3 (mcons 2 (mcons 1 '())))))
(mcons 5 (mcons 4 (mcons 3 (mcons 2 (mcons 1 '())))))

abhirag
2019-3-22 12:45:24

Thanks a lot @soegaard2 :slightly_smiling_face:


abhirag
2019-3-22 12:47:44

I understand what’s happening now, to make sure that ml actually points to the last-pair in your explanation, I probably would have to do an assignment before the return of the function if I am understanding stuff correctly


abhirag
2019-3-22 12:50:29

I think mutability is confusing me, probably a (set! ml prev) before return might fix this, I’ll try it out


soegaard2
2019-3-22 12:52:07

Mutability is confusing.


soegaard2
2019-3-22 12:52:20

Luckily you are experimenting in Racket.


soegaard2
2019-3-22 12:52:48

I remember the good old days with single linked lists in C. A mistake always led to a crash.


abhirag
2019-3-22 12:54:43

yeah at this point of time, immutability makes compete sense to me but still wanted to make sure that I understood mutability in racket case I absolutely need it


abhirag
2019-3-22 12:56:27

thanks a lot for helping out, I’ll have to try and understand this a bit more


abhirag
2019-3-22 12:57:14

(set! ml prev) inside the function doesn’t seem to be doing anything, mutability is the worst


soegaard2
2019-3-22 12:58:08

The reason (set! ml prev) doesn’t work is that ml is only visible inside your reverse function.


soegaard2
2019-3-22 13:00:52

Try:


soegaard2
2019-3-22 13:00:56
#lang racket
(require compatibility/mlist)

(define (mlast-pair xs)
  (cond
    [(null? xs)        #f]
    [(null? (mcdr xs)) xs]
    [else              (mlast-pair (mcdr xs))]))

(define xs (mlist 1 2 3 4 5))
(define last-pair (mlast-pair xs))
(mreverse! xs)
last-pair

abhirag
2019-3-22 13:04:04

I understand that part now, but I guess trying to make sure that xs returns the last-pair on return of the function wouldn’t be possible


abhirag
2019-3-22 13:04:17

becuase xs remains out of scope to that function


abhirag
2019-3-22 13:04:40

in python I would use something like nonlocal or global to qualify that identifier


abhirag
2019-3-22 13:05:02

but I guess I can’t do that in racket


soegaard2
2019-3-22 13:05:17

Instead you can make (reverse-list-iter! ml) return the last pair.


abhirag
2019-3-22 13:06:38

apologies if I am not making my self clear, I might be confused, returning xs would be alright, but I can’t rebind xs outside


abhirag
2019-3-22 13:06:44

from insode the fucntion right?


abhirag
2019-3-22 13:06:58

so that when I return from the function


soegaard2
2019-3-22 13:07:04

You can use set! to change a global binding.


abhirag
2019-3-22 13:07:14

the outside binding also points to the reversed list


abhirag
2019-3-22 13:10:50

I think I get it now, I’ll try and understand the whole thing including all the pointers you gave :slightly_smiling_face:


abhirag
2019-3-22 13:10:59

thanks a lot again @soegaard2


soegaard2
2019-3-22 13:11:10

No problem.


rokitna
2019-3-22 15:37:41

Python’s nonlocal declarations prevent its = from acting as a local variable declaration. Racket’s set! never acts as a local variable declaration, so in a sense it’s always nonlocal.


rokitna
2019-3-22 15:49:57

It sounds like you want a variant of mreverse! that mutates every reference to the given mutable cons cell so that it refers to a different mutable cons cell instead. Generally that’s not possible since there may be references reverse-list-iter! has no way to get at.

There are some other ways to approximate that:

  • Your utility could mutate the mcar of each cell in the list instead of the mcdr, so that the reversed list still starts with the same mutable cons cell.

  • Your utility could be a syntax transformer where (reverse-list-iter! xs) means (set! xs (reverse xs)). This way the xs variable is modified and you don’t even need a mutable list.