A Лучше and-l
Процедура
Проблема в том, что процедура and-l
работает так, как, вероятно, не ожидалось. Здесь x
помещается в список перед передачей в тело процедуры. Рассмотрим:
scratch.rkt> (define bad-proc (lambda x x))
scratch.rkt> (bad-proc '(#t #f))
'((#t #f))
Вероятно, можно ожидать, что процедура and-l
будет проверять, содержит ли список все истинные значения, а процедура, переданная в тело процедуры:
scratch.rkt> (and-l-bad '(#t #f))
#t
scratch.rkt> (and-l-bad '(#f #f))
#t
scratch.rkt> (and-l-bad '(#t #t))
#t
Вот правильная версия and-l
; обратите внимание, что нет необходимости в apply
:
(define and-l
(lambda (x)
(if (null? x)
#t
(and (car x) (and-l (cdr x))))))
Тестирование новой процедуры:
scratch.rkt> (and-l '(#t #f))
#f
scratch.rkt> (and-l '(#f #f))
#f
scratch.rkt> (and-l '(#t #t))
#t
Теперь, когда and-l
работает должным образом, обратим наше внимание на :
(apply map and-l (gen-truth 2 '(#t #f)))
Опять же, здесь apply
не требуется. Здесь даже нет смысла делать (apply map and-l ;....)
. Процедура apply
принимает процедуру и список в качестве аргументов и использует элементы списка в качестве аргументов процедуры. Итак, (apply + '(1 2 3 4))
эквивалентно (+ 1 2 3 4)
. Эта возможность не нужна в текущем контексте; все, что нужно, это map
, чтобы применить and-l
к каждому списку логических значений в списке, возвращаемом gen-truth
:
scratch.rkt> (gen-truth 2 '(#t #f))
'((#t #t) (#f #t) (#t #f) (#f #f))
scratch.rkt> (map and-l (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
Спасение оригинала and-l
Процедура
Обратите внимание, что and-l-bad
работает не со списком аргументов, а с неопределенным числом аргументов, которые заключены в список и переданы в тело процедуры:
scratch.rkt> (and-l-bad #t #f #f)
#f
scratch.rkt> (and-l-bad #t #t #t)
#t
scratch.rkt> (and-l-bad #f #f #t)
#f
С этим в Обратите внимание, что цель OP может быть достигнута без перезаписи and-l-bad
в and-l
, как указано выше. Вместо этого переосмыслите (apply map and-l (gen-truth 2 '(#t #f)))
:
scratch.rkt> (map (lambda (x) (apply and-l-bad x)) (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
Здесь map
применяет функцию к каждому логическому списку в списке, возвращаемом gen-truth
. Эта функция принимает логический список и применяет к нему apply
с and-l-bad
, например, (apply and-l-bad '(#t #f)
-> (and-l-bad #t #f)
, что именно то, что ожидает and-l-bad
.
and-pro c
Процедура and-l-bad
действительно похожа на and
в том, что она принимает неопределенное количество аргументов и возвращает результат объединения аргументов с and
:
scratch.rkt> (and-l-bad #t #t #f #t)
#f
scratch.rkt> (and #t #t #f #t)
#f
Но есть важное отличие: and-l-bad
- это процедура , а and
- специальная форма . Процедуры всегда оценивают всех своих аргументов, но специальные формы имеют специальные правила оценки. Специальная форма and
оценивает свои аргументы последовательно, пока результат не станет известен, либо возвращая первое #f
, встречающееся в этой последовательности, либо последнее значение последовательности. Это оценка короткого замыкания .
Лучшее название для and-l-bad
, возможно, будет and-proc
. Исходное имя and-l
предполагает, что and
было применено к списку (что не соответствует действительности), тогда как and-proc
подчеркивает, что это процедура , которая ведет себя аналогично and
.
Зачем вам сначала нужно что-то вроде and-proc
? Ну, apply
принимает процедуру для первого аргумента, поэтому (map (lambda (x) (apply and x)) (gen-truth 2 '(#t #f)))
не будет работать. Но это будет работать:
scratch.rkt> (map (lambda (x) (apply and-proc x)) (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
Здесь мы использовали and
-подобную процедуру , где and
сама не будет работать. Недостатком and-proc
является то, что он всегда оценивает всех своих аргументов; это не всегда желательно.
Как избежать написания новой and
-подобной процедуры
Написание новой процедуры для обработки с использованием and
здесь могло бы быть вообще избегаем:
scratch.rkt> (map (lambda (x) (andmap identity x)) (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
Здесь (lambda (x) (andmap identity x))
- это процедура, которая принимает свои аргументы и объединяет их с and
. То есть ((lambda (x) (andmap identity x)) '(#t #f))
эквивалентно (and (identity #t) (identity #f))
. Эта процедура сопоставляется со списком логических списков, как и раньше.
Это эквивалентно and-l
, определенному в верхней части этого ответа, и and-l
на самом деле может быть лучше определено как:
(define (and-l xs)
(andmap identity xs))