build-list (ошибка - ожидается процедура) Racket / Scheme - PullRequest
1 голос
/ 25 марта 2012

Попытка создать функцию, которая генерирует доску n *

(new-board 2)

должна производить

(list (make-posn 0 0) (make-posn 0 1) (make-posn 1 0) (make-posn 1 1))

Текущее исполнение моего кода выглядит следующим образом:

(define (new-board y)
    (build-list y (lambda (x) (build-list x (make-posn y x))))
      )

Я был почти уверен, что это сработает, но, учитывая мои текущие знания и опыт работы с Racket, я не смог найти ошибку.

Я набрал:

> (new-board 3)

и получил ошибку:

build-list: expects a procedure (arity 1); given (make-posn 3 0)

Я совершаю чудовищное преступление, вызывая список сборки внутри списка сборки?Пожалуйста, дайте мне знать.Спасибо!

Ответы [ 3 ]

2 голосов
/ 25 марта 2012

Если вы посмотрите подпись (контракт) build-list 1 , вы увидите, что это

 build-list : Nat (Nat -> X) -> (listof X)

Таким образом, требуется (натуральное) число, а затем функция , которая ожидает натуральное число и возвращает элемент типа (X), который вы хотите включить в список.Итак, в вашем случае, каким конкретным типом вы хотите, чтобы X был для каждого звонка, который вы делаете, на build-list (в каждом случае он может быть разным).В случае внутреннего build-list похоже, что вы пытаетесь составить список posn с.Однако (make-posn y x) немедленно создает один posn и не является функцией, которую ожидает build-list.Поэтому, так же как вы предоставляете функцию (lambda (x) ...) для внешней build-list, вы также должны предоставлять функцию (lambda (...) ...) для внутренней функции.

Выбор имени x для параметра первого lambda может быть немного запутанным.Что я могу сделать, так это изменить имя параметра функции new-board на N, так как кажется, что вы хотите создать доску из N строк (и столбцов).И цель первого build-list - создать каждую из этих строк (или столбцов, в зависимости от того, как вы хотите об этом думать).Так что, если у вас было:

 (define (new-board N)
   (build-list N (lambda (x) ...)))

И затем вы используете это как:

 (new-board 5)

, это уменьшит / упростит / оценит следующим образом:

 ==> (build-list 5 (lambda (x) ...))
 ==> (list ( (lambda (x) (build-list ... x ...))  0 )
           ( (lambda (x) (build-list ... x ...))  1 )
           ( (lambda (x) (build-list ... x ...))  2 )
           ( (lambda (x) (build-list ... x ...))  3 )
           ( (lambda (x) (build-list ... x ...))  4 )
 ==> (list (build-list ... 0 ...)
           (build-list ... 1 ...)
           (build-list ... 2 ...)
           (build-list ... 3 ...)
           (build-list ... 4 ...))

Такнет ничего плохого во вложении build-list.Посмотрите, сможете ли вы теперь выяснить, как заставить внутренний сборочный список работать над созданием списка posns, если текущая строка зафиксирована в определенном x значении.

2 голосов
/ 25 марта 2012

Об этой процедуре:

(define (new-board y)
    (build-list y (lambda (x) (build-list x 
                                          (make-posn y x))))) ;error!

Давайте посмотрим, что build-list получает в качестве параметров. Первый параметр - y, число, а второй параметр - процедура, но вы передаете результат вычисления make-posn, который не является процедурой, это значение. И это причина ошибки, которую вы получаете.

РЕДАКТ. 1:

Теперь я понимаю, что вы намеревались. Я могу придумать решение, но оно немного сложнее, чем вы имели в виду:

(define (new-board n)
  (flatten
   (map (lambda (x)
          (map (lambda (y)
                 (make-posn x y))
               (build-list n identity)))
        (build-list n identity))))

(define (flatten lst)
  (if (not (list? lst))
      (list lst)
      (apply append (map flatten lst))))

Вот как это работает:

  • build-list просто используется для генерации чисел от 0 до n-1, и я передаю identity в качестве процедуры, поскольку для каждого числа
  • Для каждого числа в списке мы также хотим создать другой список, опять же от 0 до n-1, потому что требуются все координаты на доске. Например, если n равно 3, координаты '((0 0) (0 1) (0 2) (1 0) (1 1) (1 2) (2 0) (2 1) (2 2))
  • Я использую map внутри map для построения вложенных списков, метод, заимствованный из здесь (см. «Вложенные отображения»)
  • Наконец, мне пришлось сгладить сгенерированные списки, и это то, что делает flatten (в противном случае мы закончили бы списком списков списков)

РЕДАКТИРОВАТЬ 2:

Если подумать, я нашел еще более простой способ, ближе к тому, что вы имели в виду. Обратите внимание, что flatten процедура неизбежна:

(define (new-board n)
  (flatten
   (build-list n
               (lambda (x)
                 (build-list n
                             (lambda (y)
                               (make-posn x y)))))))

Теперь, когда вы наберете это:

(new-board 2)

Результат, как и ожидалось:

(#(struct:posn 0 0) #(struct:posn 0 1) #(struct:posn 1 0) #(struct:posn 1 1))
1 голос
/ 26 марта 2012

Кстати, если вам разрешено использовать полный Racket, есть хороший способ выразить вычисления с помощью for циклов:

(define (new-board n)
  (for*/list ([i n]
              [j n])
    (make-posn i j)))

Другой способ получить тот же результат, но сДругой подход заключается в использовании арифметического трюка с quotient и remainder.

(define (new-board n)
  (build-list (* n n)
              (lambda (k)
                (make-posn (quotient k n)
                           (remainder k n)))))
...