(Lisp)计算更改代码

时间:2015-01-30 16:48:02

标签: loops recursion count lisp common-lisp

我最近开始学习lisp,我认为一个有趣的问题是Count Change算法已被尝试多次但是我发现很难甚至理清如何处理这个和思想我会尝试使用循环,但它只是没有工作。我将在下面进行两次无用的尝试,但如果有人有任何建议甚至我应该如何考虑在lisp方面的这个问题,我将非常感激。 我更喜欢使用递归解决方案。

我一直在查看有关计算变化的问题,但它们都倾向于面向对象,而我需要更具功能性的东西。 这不是家庭工作,只是私人学习!

(defun dollar (amount)   
(let ((l 0) (j 0) (array (make-array '(5 10 20 50 100 200 500 1000 2000 5000 100000))) (results (make-array 50 :initial-element nil))
        (do (l 10 (+ l 1))
       (do ((= j (aref array l)) amount (+ j 1))
         (+ (aref array j) (- (aref results j) (aref array l))))))
                   ))

(defun coin-change (amount coins)
(cond ((< amount 0) 0)
      ((= amount 5) 1)
      ((null coins) 0)
      (t (+ (make-change-with-coins (- amount (car coins)) coins)
            (make-change-with-coins amount (cdr coins)))))

样本输入将是(硬币变更20&#39;(5 10 20 50 100 200 500 1000 1000 2000 5000 100000))将返回4

2 个答案:

答案 0 :(得分:4)

标准格式有助于正确获取代码结构。阅读有关新语言的某些文档有助于实现更多目标。

这是你写的:

(defun dollar (amount)
  (let ((l 0)
        (j 0)
        (array (make-array '(5 10 20 50 100 200 500 1000 2000 5000 100000)))
        (results (make-array 50 :initial-element nil))
        (do (l 10 (+ l 1))
            (do ((= j (aref array l)) amount (+ j 1))
                (+ (aref array j) (- (aref results j) (aref array l))))))))

Dollar没有正确的语义。 Make-array将维度作为第一个参数,您很可能希望将do表单作为let的正文。我在这里使用了矢量文字。

(defun dollar (amount)
  (let ((l 0)
        (j 0)
        (array #(5 10 20 50 100 200 500 1000 2000 5000 100000))
        (results (make-array 50 :initial-element nil)))
    (do (l 10 (+ l 1))
        (do ((= j (aref array l)) amount (+ j 1))
            (+ (aref array j) (- (aref results j) (aref array l)))))))

Do首先获取绑定列表,然后是包含结束条件和返回表单的表单,最后是形成正文的表单。

(defun dollar (amount)
  (let ((l 0)
        (j 0)
        (array #(5 10 20 50 100 200 500 1000 2000 5000 100000))
        (results (make-array 50 :initial-element nil)))
    (do ((l 10 (+ l 1)))
        (#| end condition here |#
         #| some more forms
         that return something |#)
      (do ((= j (aref array l)) ; uh, so this binds the variable "=" to j
                                ; and updates it to (aref array l) on iteration
           amount    ; an empty binding, initially nil
           (+ j 1))  ; binds the variable "+" to j and updates it to 1
          (+           ; tries to evaluate the variable "+" as a form...
           (aref array j)   ; no effect
           (- (aref results j) (aref array l)))))))  ; would return this

我尝试纠正外部do的形状并注释内部do。你可以看到它完全没有意义。

(defun coin-change (amount coins)
  (cond ((< amount 0) 0)
        ((= amount 5) 1)
        ((null coins) 0)
        (t (+ (make-change-with-coins (- amount (car coins)) coins)
              (make-change-with-coins amount (cdr coins))))))

这看起来至少在语义上是正确的,但我不知道它应该如何工作(我不知道make-change-with-coins做了什么。)

我认为首先阅读一本好的入门书(我喜欢Practical Common Lisp)并仔细阅读Common Lisp Hyperspec(CLHS)是明智的。

答案 1 :(得分:3)

我不清楚第一个功能应该做什么。

第二个几乎没问题,这是一个固定的版本:

(defun coin-change (amount coins)
  (cond
    ((< amount 0) 0)
    ((= amount 0) 1)
    ((= (length coins) 0) 0)
    (t (+ (coin-change (- amount (first coins)) coins)
          (coin-change amount (rest coins))))))

这个想法是:

  • 负数量不能匹配(0路)
  • 零可以完全一种方式匹配(不给任何硬币)
  • 如果金额> 0且我们没有剩余硬币类型,那么就没有办法
  • 否则我们可以A)给出第一个硬币类型的一个,并计算我们可以匹配剩余部分的方式,B)计算不使用第一个硬币类型的方式。答案是A + B

请注意,这将为大量提供巨大的计算时间,因为它没有利用一个重要的事实,即从某种硬币类型开始匹配一定数量的方式的数量不依赖于我们如何得到这个数量(即过去的历史)。 通过添加缓存,您可以获得更快的动态编程解决方案,因为每次计算只需执行一次。