2015-11-08 2 views
1

Я новичок в Lisp. Я пытаюсь написать функцию, которая будет принимать список точечных списков (представляющих количество монет определенного значения), например.Цитирование по списку и добавление к нему нового

((1 . 50) (2 . 20) (3 . 10)) ;; one 50 cent coin, two 20 cent coins, three 10 cent coins 

и затем конвертировать его в список каждой монеты по значению, например.

(50 20 20 10 10 10) 

Не должно быть слишком сложно, не так ли? Это то, что у меня есть до сих пор. Тем не менее, он возвращает NIL. Есть идеи по исправлению этого?

(defun fold-out (coins) 
    (let ((coins-list (list))) 
    (dolist (x coins) 
     (let ((quantity (car x)) (amount (cdr x))) 
     (loop for y from 0 to quantity 
      do (cons amount coins-list)))) 
    coins-list)) 

ответ

3

Так как вы можете использовать loop, просто сделать

(defun fold-out (coins) 
    (loop 
    for (quantity . amount) in coins 
    nconc (make-list quantity :initial-element amount))) 

в качестве альтернативы, с помощью dolist:

(defun fold-out (coins) 
    (let ((rcoins (reverse coins)) (res nil)) 
    (dolist (c rcoins) 
     (let ((quantity (car c)) (amount (cdr c))) 
     (dotimes (j quantity) (push amount res)))) 
    res)) 
+0

спасибо. Оба решения действительно полезны, поскольку я только сейчас знакомлюсь с языком. – Charles

+0

Добро пожаловать, и вы были довольно близки: замените 'cons' на' push' и 'loop ... to' на' loop ... below' или 'loop repeat quantity' в вашем исходном коде, а результат был бы правильным (хотя и наоборот). – uselpa

+0

Ага, аккуратный. Глядя на документы для 'nconc' сейчас, я вижу, что это функция для конкатенации списков. Но в вышеприведенном решении он только вызывается с одним аргументом за раз ... так где же происходит конкатенация? Это макрос цикла, который заботится об этом? – Charles

1

Выражение (cons amount coins-list) возвращает новый список, но это не изменяет coins-list; поэтому конечным результатом является NIL.

Таким образом, вы можете изменить его на (setf coins-list (cons amount coins-list)), который будет явно изменять coins-list, и это сработает.

Однако в способе Lisp делать что-то (функциональное программирование) мы стараемся не изменять подобные вещи. Вместо этого мы делаем каждое выражение возвратом значения (нового объекта), который основывается на входных значениях, а затем передает этот новый объект другой функции. Часто функция, которую передается объекту, является той же самой функцией, которая выполняет передачу; это рекурсия.

2

Если бы я сделать это, я бы, вероятно, использовать вложенные циклы:

(defun fold-out (coins) 
    (loop for (count . value) in coins 
    append (loop repeat count 
        collect value))) 

Сохраняет справедливый бит печатать, вручную накапливая-Into-то и, в целом, относительно читабельным. Может делать с большим количеством docstring и, возможно, с некоторыми модульными тестами.

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