Конд с локальной привязкой

Мой вопрос о переписывании вложенных условий if для одного cond с веткой, имеющей локальную привязку. Я очень новичок в Racket, просто делаю свои первые шаги, поэтому, если мой вопрос глуп, пожалуйста, будьте снисходительны.

Вкратце задача состоит в том, чтобы написать функцию, которая берет вектор и ищет в нем значение. Вектор содержит смешанные вещи - пары и непары. Значение интереса должно быть в автомобиле пары.

В рабочем решении используется рекурсивная вспомогательная функция с вложенными 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

Я хотел бы переписать его так, чтобы он использовал cond это выглядит более компактным. Приведенный ниже код является возможной реализацией. Проблема в том, что (vector-ref vec pos)вычисляется несколько раз, и это то, что я хотел бы переписать так, чтобы оно вычислялось только один раз, как в предыдущей реализации с вложенными 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))]))])

И это то, чего я достиг максимально: один звонок (vector-ref vec pos) в test-expr и другой вызов в 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

Как я могу сделать дальше? el делится между test-expr и result-expression? И я хотел бы el оставаться локальным для этой конкретной конд-ветви. Приведенный ниже код работает неправильно. AFAIU, весь letrec выражение рассматривается как текст-выражение 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))])

1 ответ

Решение

Вы можете сделать это, если сначала импортируете SRFI 61:

(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))))))

Важным моментом, который обеспечивает SRFI 61, является то, что он позволяет (<generator> <guard> => <receiver>) пункт. Здесь генератор - это то, что создает общее значение, которое будет использоваться как охранником, так и получателем. Наш приемник в этом случае просто values, который возвращает значение, которое он дал без какой-либо обработки.


Обновление: В настоящее время srfi/61 не работает правильно для программ, которые используют #lang racket или т.п (srfi/61 использует разные привязки для => а также else из чего racket/private/cond обеспечивает). Это было недавно исправлено и должно появиться в будущем выпуске Racket.

Другие вопросы по тегам