почему применяется map и к списку выражений возвращается только одно логическое значение? - PullRequest
0 голосов
/ 21 апреля 2020
(define (cart . lists)
  (cond ((null? lists) '())
        ((= (length lists) 1) (map list (first lists)))
        (else
         (append-map (lambda(x)
                       (map (lambda(y) (cons y x))
                            (first lists)))
                     (apply cart (rest lists))))))

(define (numbers n)
   (define (reversed-numbers n)
     (if (= n 0)
         '()
         `(,n . ,(reversed-numbers (- n 1)))))
   (reverse (reversed-numbers n)))

(define (gen-truth n vals)
       (apply cart (map (lambda(x) list vals) (numbers n))))

(gen-truth 2 '(#t #f))  returns: '((#t #t) (#f #t) (#t #f) (#f #f))

(define and-l (lambda x 
    (if (null? x)
        #t
        (if (car x) (apply and-l (cdr x)) #f))))

почему: (apply map and-l (gen-truth 2 '(#t #f)))

возвращает '(#f #f)? я ожидаю, что он возвращает логическое значение для каждого из подвыражений, содержащих пары логических значений.

1 Ответ

1 голос
/ 21 апреля 2020

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