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