1

My question is about rewriting a nested if conditions to a single cond with a branch having a local binding. I am very new to Racket, just making my first steps, so if my question is stupid, please, be lenient.

In brief the task is to write a function that takes a vector and searches a value in it. The vector contains mixed stuff -- pairs and non-pairs. The value of interest should be in the car of a pair.

The working solution uses a recursive helper function with nested ifs

[vlen (vector-length vec)]
[find-in-vector
(lambda (pos)
 (if (= pos vlen)                               ;; if the end of the vector has been reached
     #f                                         ;; then return false
     (let ([el (vector-ref vec pos)])           ;; Otherwise, extract current element from the vector,
       (if (and (pair? el) (equal? v (car el))) ;; if the element is a pair and its car is what we want
           el                                   ;; then return the element
           (find-in-vector (+ 1 pos))))))]      ;; otherwise keep searching the vector

I would like to rewrite it so that it uses cond that looks more compact. The below code is a possible implementation. The problem is that (vector-ref vec pos) is computed several times and this is what I would like to rewrite such that it is computed only once, like in the previous implementation with nested ifs

[vlen (vector-length vec)]
[find-in-vector
 (lambda (pos)
   (cond [(= pos vlen) #f]
         [(and (pair? (vector-ref vec pos))            ;; one
               (equal? v (car (vector-ref vec pos))))  ;; two
          (vector-ref vec pos)]                        ;; three is too many
         [#t (find-in-vector (+ 1 pos))]))])

And this is what I achieved at most: one call to (vector-ref vec pos) in test-expr and another call in result-expr

(cond
  [(= pos vlen) #f]
  [(letrec ([el (vector-ref vec pos)])      ;; extract current element from the vector
     (and (pair? el) (equal? v (car el))))  ;; and use it in conditionals
   (vector-ref vec pos)]                    ;; again, extract and return. FIXIT
  [#t (find-in-vector (+ 1 pos))]))])       ;; otherwise, keep searching

How can I further make el shared between the test-expr and result-expression? And I would like el to remain local to this particular cond-branch. The below code works incorrectly. AFAIU, the whole letrec expression is viewed as a text-expr of the cond?

(cond
 [(= pos vlen) #f]
 [(letrec ([el (vector-ref vec pos)])
    (and (pair? el) (equal? v (car el)))
    el)]
 [#t (find-in-vector (+ 1 pos))])
Nik O'Lai
  • 3,586
  • 1
  • 15
  • 17
  • the most basic way to introduce local binding is with `lambda`. Here's a tweak of the code from Chris's answer: `(cond ... ( ((lambda (el) (and (pair? el) (equal? v (car el)) el )) (vector-ref vec pos)) ) (else ...))`. Awkward, but works. – Will Ness Mar 11 '13 at 14:50
  • (although, I tested it in MIT Scheme, not in Racket). – Will Ness Mar 11 '13 at 14:56

1 Answers1

1

You can do it if you import SRFI 61 first:

(require srfi/61)
(define (find-in-vector vec v)
  (define vlen (vector-length vec))
  (let loop ((pos 0))
    (cond
      ((= pos vlen) #f)
      ((vector-ref vec pos)
       (lambda (el) (and (pair? el) (equal? v (car el))))
       => values)
      (else (loop (add1 pos))))))

The important thing that SRFI 61 provides is that it allows for the (<generator> <guard> => <receiver>) clause. Here, the generator is the thing that creates a common value to be used by both the guard and the receiver. Our receiver in this case is simply values, which returns the value it's given without any processing.


Update: Currently srfi/61 does not work correctly for programs that use #lang racket or the like (srfi/61 is using different bindings for => and else from what racket/private/cond provides). This has been recently fixed and should appear in a future Racket release.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • I was about to complain that `=>` does not work for me :). I have replaced `else` with `#t` but still have no idea how to handle `=>`. – Nik O'Lai Feb 27 '13 at 11:57
  • @NikO'Lai I do have a modified version of `srfi/61` that does work, but I'm reluctant to suggest people modify their Racket installation. This is actually not an easy problem to solve: right now, `srfi/61` only works with old-style programs, but with my change, it only works with new-style programs. I wish I know of a solution that will work with both old-style and new-style programs. – C. K. Young Feb 27 '13 at 12:28
  • oh.. no problem then. i can live with ifs :) thank you very much for your time!! – Nik O'Lai Feb 27 '13 at 12:49
  • @NikO'Lai My pleasure! In general (and in other Scheme implementations), the SRFI 61 `cond` is really the way to go, and I hope there will be a satisfactory solution for Racket soon, so you can take advantage of it. :-) – C. K. Young Feb 27 '13 at 14:09
  • @NikO'Lai My fix has now been [officially accepted](https://github.com/plt/racket/commit/9e93ee268269a7f289dd2a936670842e07db5237), so you should be able to use that and win. :-) – C. K. Young Feb 28 '13 at 15:15
  • Great! Things are going fast in Racket. What should I update (and how)? Or should I wait for the new download at http://racket-lang.org? – Nik O'Lai Mar 01 '13 at 09:16
  • @NikO'Lai If you can edit files in your Racket installation, just replace your `collects/srfi/61/cond.rkt` with [this](https://raw.github.com/plt/racket/master/collects/srfi/61/cond.rkt). You may have to remove the corresponding precompiled files (`cond_rkt.dep` and `cond_rkt.zo`) in `collects/srfi/61/compiled` too. – C. K. Young Mar 02 '13 at 15:43
  • @NikO'Lai Another alternative is to install a [nightly build](http://pre.racket-lang.org/installers/), but that updates _everything else_ too, and it may be less stable than a release. – C. K. Young Mar 02 '13 at 15:46
  • 1
    i have downloaded drracket-5.3.3.7 and checked. i confirm it works! my big thanks!! – Nik O'Lai Mar 11 '13 at 20:53