How do you match with guards in Racket?

443 Views Asked by At

In Scala you can do something like this:

def times[A](item: A, number: Int): List[A] = number match {
  case n if n <= 0 => Nil // Nil = '()
  case _ => 
    // equivalent to [_ (cons item (times item (- number 1)))]
    item :: times(item, number - 1)
}

Is it possible to do something like this using Racket's match form? I couldn't find it in the documentation

For those not familiar with Scala, the first case matches if the number is equal to or less than 0, the second case is just a wildcard that matches everything else

in other words, what would I write in the ??? spot to achieve similar functionality to what I described above?

(define (times item number)
  (match number
    [??? '()]
    [_ (cons item (times item (- number 1)))]))
2

There are 2 best solutions below

0
On BEST ANSWER

Racket's match has an optional #:when clause that lets you write this almost exactly as you did in Scala:

(define (times item number)
  (match number
    [n #:when (<= n 0) '()]
    [_ (cons item (times item (- number 1)))]))

I think that answers your question, literally. But more idiomatic Racket would be to use cond for something like this -- where it's a simple conditional test and you don't need any destructuring:

(define (times item number)
  (cond [(<= number 0) '()]
        [else (cons item (times item (- number 1)))]))

Although I'd probably flip the arms:

(define (times item number)
  (cond [(positive? number) (cons item (times item (- number 1)))]
        [else '()]))

Of course for something this simple you could use if:

(define (times item number)
  (if (positive? number)
      (cons item (times item (- number 1)))
      '()))

However I've grown to prefer using cond, as the Racket style guide recommends.

2
On

Turns out I wasn't looking hard enough, here's the answer:

(define (times item number)
  (match number
    [(? (lambda (n) (<= n 0))) '()]
    [_ (cons item (times item (- number 1)))]))

(? expr patt) defines a guard.

The above can perhaps be more succinctly written as:

(define (lteq-0? n) (<= n 0))

(define (times item number)
  (match number
    [(? lteq-0?) '()]
    [_ (cons item (times item (- number 1)))]))