Racket — вывод содержимого списка

#list #recursion #lisp #scheme #racket

#Список #рекурсия #lisp #схема #racket

Вопрос:

Я определил список (в Racket / Scheme):

 (define myList (cons 'data1 (cons 'data2 (cons 'data3 (cons 'data4 empty)))))
  

или

 (list 'data1 'data2 'data3 'data4)
  

И я хочу написать функцию, которая циклически просматривает список и выводит все значения списка.

 (define (outputListData list)
  (cond 
    [(null? list) list]
    [else (getListData)]))
  

С помощью какой функции я могу циклически просматривать содержимое списка? Я знаю, что можно использовать first amp; rest для получения данных списка, но я думаю, что это неправильный способ здесь.

Кстати: есть ли хорошая, компактная ссылка на racket, например php.net ? Я нахожу официальные документы Racket очень запутанными…

Комментарии:

1. Что вас смущает в документации Racket? Вы смотрели руководство по Racket на docs.racket-lang.org/guide ? Это более мягкое введение. Если вы все еще не уверены в рекурсии, возможно, вы также захотите ознакомиться с «Как разрабатывать программы», которая доступна бесплатно по адресу htdp.org .

Ответ №1:

Вы можете использовать цикл for. Пример:

 (for ([x (list 1 2 3)])
    (printf "~s -> ~sn" x (* x x)))
  

Конечно, есть более функциональные способы сделать это, но этот способ тоже работает. Вы, вероятно, захотите взглянуть на учебник, например, Как разрабатывать программы для выполнения рекурсивного подхода. Смотрите: http://www.ccs.neu.edu/home/matthias/HtDP2e /

Комментарии:

1. 1 Также отличный ответ, но лично я предпочитаю ответ от Jon 0. Кстати, очень полезная ссылка, большое спасибо!

2. Это лучший ответ на данный момент, ИМХО.

Ответ №2:

решение dyoo является приятным и кратким в схеме, подобной Racket, в которую встроены полезные процедуры итерации. К вашему сведению, однако, ваш ‘outputListData’ недалек от того, чтобы быть стандартным рекурсивным способом сделать это. Вам просто нужно изменить пару строк:

 (define (outputListData list)
  (cond 
    [(null? list) #f]             ; actually doesn't really matter what we return
    [else (printf "~sn" (first list))    ; display the first item ...
          (outputListData (rest list))])) ; and start over with the rest
  

Поскольку это процедура «императивного» типа, которая не предназначена для возврата значения, на самом деле не имеет значения, что мы делаем с пустым списком, если мы прекращаем повторяться (чтобы избежать бесконечного цикла). Если список не пуст, мы выводим первый элемент и начинаем рекурсивно с остальной части списка.

Кстати, вот еще один способ, которым вы могли бы написать что-то почти идентичное, если бы вам просто нужен был цикл «for» в середине какой-либо другой функции:

 (let loop ((l (list 'foo 'bar 'baz 'quux))) ; or put whatever input you need
   (cond ((null? l) #f)
         (else
           (printf "~sn" (first l))
           (loop (rest l)))))
  

Один из способов подумать об этом «named let» заключается в том, что он определяет вызываемую временную функцию loop , которая работает точно так же, как outputListData описано выше. Схема обладает приятным свойством, заключающимся в том, что она не будет увеличивать стек для «хвостовых вызовов», подобных этим, поэтому вы всегда можете написать то, что было бы «итеративным» for или while циклом в этом рекурсивном стиле.

Я настоятельно рекомендую The Little Schemer Фридмана и Феллайзена для краткого ознакомления с этим стилем написания функций! Я нашел это на странице Дугласа Крокфорда здесь.

Ответ №3:

Редактировать согласно комментариям: Использовать для каждого

 (for-each display myList)
  

Попробуйте это:

 (void (map display myList))
  

Разбиение его:

  • (void x) приводит к игнорированию x вместо возврата в REPL / родительское выражение в качестве значения.
  • (map function lst) : Для списка '(a1 a2 ... an) возвращает список '((function a1) (function a2) ... (function an)) .

Итак, мы используем map для отображения всех элементов, но поскольку нас интересует только побочный эффект, а не возвращаемое значение, мы вызываем void для возвращаемого списка.

Официальные документы:

Комментарии:

1. Используйте for-each , а не map , когда вы не используете возвращаемое значение. В качестве бонуса вы можете избавиться от void .

Ответ №4:

Я думаю, что решение, которое проще всего понять, это придумать так называемую функцию «пожиратель списков». Именно так мой университет ввел рекурсию и списки в Racket. Также большинство книг по Racket (например, «Как разрабатывать программы» или «Realm Of Racket») объясняют это таким образом. Это код:

 (define my-list (list 'data1 'data2 'data3 'data4)) 

(define (print-list a-list-of-data)
  (when (not (empty? a-list-of-data))
    (print (first a-list-of-data))
    (print-list (rest a-list-of-data))))
  

Если вы вызовете функцию с примером списка my-list, вы получите следующий вывод:

 (print-list my-list)

'data1'data2'data3'data4
  

Функция выполняет следующее: пока данный список не пуст, она захватывает первый элемент этого списка и передает его функции print. Затем он говорит себе сделать то же самое с остальной частью списка. (Он вызывает себя в остальной части списка.) Эта вторая часть — то, что они называют рекурсией.

Однако вы можете сократить это, используя функцию под названием map:

 (define (print-list a-list-of-data)
  (map print a-list-of-data))
  

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

Надеюсь, это помогло!

Комментарии:

1. будьте осторожны, r5rs map его аргументы могут обрабатываться в неуказанном порядке. Версия Racket обрабатывает их от первого до последнего. — на самом деле, ваша print-list функция похожа (for/list print lst) .