Конд с локальной привязкой
Мой вопрос о переписывании вложенных условий 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.