IncognitHo
IncognitHo

Reputation: 23

How can I permanently change the value of a parameter that was passed into a function in Racket?

I'm having trouble changing the value of a passed in parameter. The set! function is only working on an already defined variable outside of the function. I want my function to change the values associated with the passed in parameters without the function returning anything. For example: I have the global variable deck1 and I want to make a function that adds the first element of the list to another list and then delete that element from the first list. Something like this:

(define deck1 (list 1 2 3 4 5))
(define hand1 (list 6 7))    

(define (hit-card deck hand)                                                                                  
    (set! hand (append hand (first deck)))
    (set! deck (rest deck)))

If I ran the function on deck1 and hand1 (hit-card deck1 hand1), I want the result to be

deck1: 1 2 3 4 5 ==> 2 3 4 5

hand1: 6 7 ==> 6 7 1

Upvotes: 1

Views: 561

Answers (2)

chansey
chansey

Reputation: 1409

I think what you want is call-by-reference, but Racket is call-by-value by default.

There are typically two ways to solve this problem in Racket.

  1. Simulate call-by-reference via box and unbox
#lang racket

(define deck1 (box (list 1 2 3 4 5)))
(define hand1 (box (list 6 7)) )

(define (hit-card deck hand)
  (set-box! hand (cons (first (unbox deck)) (unbox hand)))
  (set-box! deck (rest (unbox deck))))

(hit-card deck1 hand1)

(unbox deck1) ; => '(2 3 4 5)
(unbox hand1) ; => '(1 6 7)
  1. Create a small call-by-reference language embedded in your program via macro.
#lang racket

(define-syntax-rule (define-get/put-id id get put!)
    (define-syntax id
      (make-set!-transformer
       (lambda (stx)
         (syntax-case stx (set!)
           [id (identifier? (syntax id)) (syntax (get))]
           [(set! id e) (syntax (put! e))])))))

(define-syntax-rule (define-cbr (id arg ...) body ...)
  (begin
    (define-syntax id
      (syntax-rules ()
        [(id actual (... ...))
         (do-f (lambda () actual)
               (... ...)
               (lambda (v)
                 (set! actual v))
               (... ...))]))
    (define-for-cbr do-f (arg ...)
      ()
      body ...)))

(define-syntax define-for-cbr
  (syntax-rules ()
    [(define-for-cbr do-f (id0 id ...)
       (gens ...) body ...)
     (define-for-cbr do-f (id ...)
       (gens ... (id0 get put)) body ...)]
    [(define-for-cbr do-f ()
       ((id get put) ...) body ...)
     (define (do-f get ... put ...)
       (define-get/put-id id get put) ...
       body ...)]))

The program above created a new special form define-cbr which something like define in Racket but is call-by-reference.

(define deck1 (list 1 2 3 4 5))
(define hand1 (list 6 7))

(define-cbr (hit-card deck hand)
  (set! hand (cons (first deck) hand))
  (set! deck (rest deck)))

(hit-card deck1 hand1)

deck1 ; => '(2 3 4 5)
hand1 ; => '(1 6 7)

More details, see https://docs.racket-lang.org/guide/pattern-macros.html

Upvotes: 2

ignis volens
ignis volens

Reputation: 9252

You can't, and wanting to do this is almost always a sign of confusion: Racket is a mostly-functional language and wants you to write programs whose component parts are functions, not procedures with arbitrary side-effects. If the argument to the function is bound to a mutable object you can mutate that object. Lists (and more generally structures made out of conses) are not mutable in Racket normally.

Upvotes: 0

Related Questions