2013-03-30 6 views
1

Я работаю через SICP, и упражнение, над которым я работаю, запрашивает процедуру, которая возвращает последний элемент в списке. Я реализовал процедуру last-pair, чтобы сделать это, но я запутался, почему он возвращает список, а не номер:Почему это возвращает список '(5), а не число 5?

(define (last-pair alist) 
    (cond ((null? (cdr alist)) 
     (car alist))  ; still happens if this is just "car alist)" 
     (else 
     (last-pair (cdr alist))))) 

Когда я вызываю его в список целых чисел от 1 до 5, я получаю выход «(5):

> (last-pair (list 1 2 3 4 5)) 
'(5) 

Я ожидал 5, например, как (car (list 1 2 3 4 5)) вернется 1 не '(1).

Почему я могу получить '(5), а не 5?


Я использую DrRacket 5.3.3 и ракетка Scheme.

EDIT 1: MIT-Scheme не подходит для этого. last-pair возвращение товара 5 не '(5). Что правильно?!?

EDIT 2: Интересно, что в DrRacket (не в MIT-схеме), если вторая линия (cond ((null? (cdr alist)) отступом два пространства, когда процедура вызывается, она возвращает '(5). Но, когда вторая строка не имеет отступов, она возвращает 5. Это глюк? Я считаю, что все, что интерпретаторы Схемы должны следовать, это скобки, правильно?

EDIT 3: Я начинаю думать, что это сбой в DrRacket. Когда я помещаю определение процедуры в окно определения (обычно это верхняя панель редактора), независимо от отступа, процедура вернет 5. Но если я определяю его в окне интерфейса , , то отступ влияет на результат, как описано в Edit 2. (EDIT 4), независимо от отступов, он вернет '(5).

< отрезан от превентивной части с некоторым кодом о различиях в отступлении; теперь проблема заключается именно в том, где процедура определена, см. Редактирование 4 >

EDIT 4: Хорошо. Я упростил проблему.

  • В MIT-схеме, (last-pair (list 1 2 3 4 5)) возвращается 5, где last-pair определено выше. Независимо от отступов.
  • В DrRacket, когда процедура last-pair определена в окне определений, а затем я нажимаю «Запуск», (last-pair (list 1 2 3 4 5)) возвращает 5. Независимо от отступов.
  • В DrRacket, когда процедура last-pair определена в окне интерфейса (REPL), (last-pair (list 1 2 3 4 5)) returns '(5). Независимо от отступов.

Вот скриншот: Racket gives different results for same function defined in different windows

+0

У Вас есть скриншот? – soegaard

+1

Невозможно воспроизвести. Обратите внимание: если вы вводите определения во взаимодействиях, вы можете столкнуться с беспорядочным столкновением с существующим определением 'last-pair', определенным на языке Racket: http://docs.racket-lang.org/reference/pairs .html? #% 28def ._% 28% 28lib._racket% 2Flist..rkt% 29._last-pair% 29% 29 Гораздо безопаснее хранить все свои определения в панели «Определения» и просто обрабатывать панель «Взаимодействие» как место для изучения этих определений. – dyoo

+0

Также обратите внимание, что примечание комментария в Racket использует точки с запятой, а не хеши. У вашей реальной программы есть хеши? – dyoo

ответ

0

Лучше не используйте встроенное имя last-pair. Я предлагаю использовать что-то более подробное из того, что вы ожидаете, например, last-elem.

При переименовании своей функции обязательно переименуйте ее повсюду; т. е. изменить имя функции не только на сайте определения, но также и на каждом сайте вызова, включая, конечно, внутри своего тела, где он получает вызванный рекурсивно. Переименование должно выполняться усердно, в противном случае очень легко ввести новые ошибки.


О странном поведении REPL. Моя догадка, когда вы вошли

(define (last-pair alist)    ;; definition 
    (cond ((null? (cdr alist)) 
     (car alist))   
     (else 
     (last-pair (cdr alist))))) ;; call 

на РЕПЛ, тем last-pair на месте вызова еще называют встроенный в определении от окружающей среды «внешней», так что этот вызов не был рекурсивный. Если это так, REPL переопределил встроенный, просто звонок не был рекурсивным.

Я бы ожидать, что делает внутреннее определение с явным letrec должно исправить это, даже когда вошел в REPL:

(define (last-pair alist) 
    (letrec ((last-pair (lambda (alist)   ;; internal definition 
      (cond ((null? (cdr alist)) 
       (car alist))   
       (else 
       (last-pair (cdr alist))))))) ;; recursive call 
    (last-pair alist)))      ;; first call 

потому что первый вызов теперь называет в рекурсивную внутреннюю версию явно, находясь внутри letrec форма. Или, может быть, он тоже будет ввернут, но я был бы действительно удивлен, если он это сделал.:) Перевод define s без внутренних определений как простой lambda формы - это одно; беспорядок внутри явного letrec совсем другой.


Если это действительно работает это будет означать, что Ракетка РЕПЛ переводит простые определения, как (define (f x) ...body...) как простые lambda формы, (define f (lambda(x) ...body...)), а не как letrec формы, (define f (letrec ((f (lambda(x) ...body...))) f)). А также, что define на Racket REPL не изменяет старую привязку в глобальной среде, но добавляет новое привязку к ней поверх старой, shadowing старая привязка.

Это говорит еще один способ «исправить» в РЕПЛ — с set!:

> (define f #f) 
> (set! f (lambda(x) ...body...)) ; alter the old binding explicitly 
> (f x) 
+0

Извините, если я не был ясен, я попытался изменить свой вопрос, чтобы понять, что с REPL больше не было странного поведения. Единственная проблема заключалась в том, где я определил процедуру и что я назвал этой процедурой. Спасибо за подробный ответ. – kalaracey

+0

«не было более странного поведения с REPL» - после того, как вы переименовали функцию повсюду? Но я также хотел найти причину и проверить решения для ее исправления без переименования (оба из них). –

3

С (list 1 2 3 4 5) возвращается (cons 1 (cons 2 (cons 3 (cons 4 (cons 5 '()))))) последняя пара является (cons 5 '()).

В функции, chnage ((null? (cdr alist)) (car alist)) к ((null? (cdr alist)) alist), чтобы Retun последнюю пару (а не автомобиль последней пары

EDIT:.

Это объясняет разницу между результатами, которые вы видите в определение и окно взаимодействия. Основная причина путаницы является то, что last-pair является встроенным. Если вы используете имя my-last-pair вы увидите тот же результат в обоих окнах.

в окне определения (define (last-pair ... интерпретируется означает что вы хотите переопределить встроенную функцию. Поэтому last-pair ссылается рекурсивно на ваше собственное определение last-pair. Это в конечном итоге дает результат 5 в вашем примере

В окне взаимодействия рекурсивный вызов last-pair относится к встроенной версии. Поэтому, когда last-pair вызывается со списком (2 3 4 5), встроенная версия возвращает последнюю пару, которая равна (cons 5 '()), и это значение напечатано как (5).

Вкратце: путаница обусловлена ​​переопределением встроенной функции в окне взаимодействия. Переопределения обрабатываются, как ожидалось, в окне определения. Хотя запутывание есть причины, по которым ведет себя окно взаимодействия (исправление этой проблемы, в свою очередь, вызывает путаницу в другом месте).

+1

Я не думаю, что это то, что возвращает 'list', хотя повторение« cons »может заключаться в том, как генерируется возвращаемое значение. 'list' возвращает список (в данном случае) пяти элементов плюс null. Когда '(null? (Cdr alist))' имеет значение true, 'alist' - это список, такой как' (5) '. Затем '(список автомобилей)' должен возвращать '5'. – kalaracey

+0

Интересно, что в DrRacket (не в MIT-Scheme), если вторая строка '(cond ((null? (Cdr alist))' имеет отступы в два пробела, когда вызывается процедура, она возвращает '' (5) '. Но, если вторая строка не имеет отступов, она возвращает '5'. Является ли это сбой? Я считаю, что все интерпретаторы Схемы должны следовать скобки, правильно? – kalaracey

+1

Если alist (5), то правильно (car alist) 5. Значение 5 - последний элемент списка. Последняя пара (aka cons-cell) в списке (cons 5 '()). Так как функция называется last-pair, а не last-element , Я ожидаю, что он вернется (cons 5 '()), и это значение будет напечатано как (5). – soegaard

 Смежные вопросы

  • Нет связанных вопросов^_^