如何以两种不同的方式定义一个返回输入一半的函数?

时间:2013-12-28 13:04:13

标签: lisp common-lisp

我正在阅读一本关于符号计算的温和介绍,并提出了这个问题。基本上,以前的内容涉及用小的函数组成更大的函数。 (如同2-将由两个1-(lisp的递减运算符)组成)

所以其中一个问题是定义函数HALF的两种不同方法是什么,它返回其输入的一半。我已经能够拿出明显的一个(将数字除以2)然后卡住了。我想从这个数字中减去HALF得到一半然后上半部分也需要计算...(我不认为作者打算在书中这么快就引入递归,所以我很可能错误)。

所以我的问题是另一种方式是什么?并且只有两种方式吗?

编辑:实施例HALF(5)得到2.5

P.S - 这本书涉及教学LISP,我对此一无所知,但显然有一个特定的倾向于使用较小的块来构建更大的块,所以请尝试使用该方法回答。

P.P.S - 到目前为止我发现了这一点,但这是一个完全不同的主题 - How to define that float is half of the number? 这里有书的pdf - http://www.cs.cmu.edu/~dst/LispBook/book.pdf(ctrl + f“两种不同的方式”)

2 个答案:

答案 0 :(得分:2)

似乎是在描述peano arithmetic。在实践中,它的工作方式与使用杯子和水桶进行流体计算的方式相同。

您可以将来自源的杯子添加到目标桶中,直到源为空。乘法和除法只是先进的加法和减法。为了减半,您需要从源到两个桶进行更改,直到源为空。当然,这可以ceilfloor,具体取决于您选择使用哪个存储桶作为答案。

(defun halve (x)
  ;; make an auxillary procedure to do the job
  (labels ((loop (x even acc)
             (if (zerop x)
                 (if even (+ acc 0.5) acc)
                 (loop (- x 1) (not even) (if even (+ acc 1) acc)))))
    ;; use the auxillary procedure
    (loop x nil 0)))

最初我提供了一个Scheme版本(因为你刚刚标记了lisp)

(define (halve x)
  (let loop ((x x) (even #f) (acc 0))
    (if (zero? x)
        (if even (+ acc 0.5) acc)
        (loop (- x 1) (not even) (if even (+ acc 1) acc)))))

答案 1 :(得分:0)

编辑:好的,让我们看看我是否可以逐步描述这一点。我也将功能分成多行。

(defun half (n) 
;Takes integer n, returns half of n
    (+ 
         (ash n -1) ;Line A
         (if (= (mod n 2) 1) .5 0))) ;Line B

所以整个功能都是一个附加问题。它只是添加两个数字,但要计算这两个数字的值,需要在“+”函数中进行额外的函数调用。

A行:这对n执行位移。 -1表示函数将n向右移一位。为了解释这一点,我们必须查看位串。
假设我们有数字8,用二进制表示。然后我们将它向右移动一个。
 1000 | - > 100 | 0
垂直条是数字的结尾。当我们向右移动一个时,最右边的位弹出并且不是数字的一部分,留下100。这是4的二进制。
我们得到相同的值,但是如果我们执行9的转换:
1001 | - > 100 | 1
一次,我们再次获得值4.我们可以从这个例子中看到,位移会截断该值,我们需要一些方法来解释奇数上丢失的.5,这就是B行进来的地方。

B行:首先测试该行是否为偶数或奇数。它通过使用模数运算来完成此操作,该运算返回除法问题的剩余部分。在我们的例子中,函数调用是(mod n 2),它返回n的余数除以2.如果n是偶数,则返回0,如果是奇数,则返回1。 可能会让你失望的是lisp“=”函数。它将条件作为其第一个参数。下一个参数是条件为真时“=”函数返回的值,最后一个参数是条件为假时返回的值。 因此,在这种情况下,我们测试是否(mod n 2)等于1,这意味着我们正在测试n是否为奇数。如果它是奇数,我们将.5添加到我们的A行的值,如果它不是奇数,我们从A行的值中添加任何值(0)。