如何创建一个lambda程序?

How to create a lambda procedures?

我需要使用 Scheme 完成大学课程的作业。我以前从未在 Scheme 中编写过代码,所以我不知道自己在做什么。我们的任务是定义一个匿名函数来计算二次函数的判别式。我将 运行 保留在错误中:“无效的 `define'。任何帮助将不胜感激。

(define roots (lambda(abc))(
  (lambda(discriminant))(
    list(/(+(-b) discriminant)(*2a))
        (/(-(-b) discriminant)(*2a))
  )
        (sqrt - (*bb)(*4ac))
  )

首先,您应该了解一下 Scheme 代码是什么样的;找到一些示例代码(在您的教科书或在线,或在此处关于 SO 的答案中)并注意如何使用括号和 whitespace 。然后效仿。你不能在 Scheme(或任何 Lisp)中任意放置括号或任意删除 whitespace.

例如,在发布的代码中 (-b) 有两处错误。首先,-b被视为一个符号,而不是b值的否定。此外,将符号放在括号中表示过程调用;给定一个 s 表达式 (f x)f 要么是句法关键字(在这种情况下 (f x) 被解释为宏调用),要么 (f x) 被解释为过程调用.如果它是过程调用并且 f 未绑定到过程,则会引发异常。因此 (-b) 尝试调用名为 -b 的过程,该过程不存在(除非您已定义它),引发异常。您可以使用 (- b),在 - 过程和符号 b 之间使用 space;这计算出 b.

值的否定

类似地,*2a被解释为符号,而不是表达式;将 *2a 放在括号之间被解释为过程调用。解释器(或编译器)期望 *2a 是一个不带参数的过程。您需要添加 spaces: (* 2 a);这被解释为使用参数 2a.

调用过程 *

(*bb)(*4ac) 有完全相同的问题。第二种情况很有趣,因为当它被正确书写时,它说明了前缀表示法的优点之一。由于 * 是关联的,因此多个值相乘的顺序无关紧要。要用前缀表示法天真地表达 4 * a * c,您可以编写 (* 4 (* a c)),明确排序乘法。您也可以将其写为 (* (* 4 a) c),以不同的顺序相乘。相乘的顺序无关紧要,所以只要您的语言支持这种表示法,您也可以只写 (* 4 a c)。事实证明,Scheme 和其他 Lisp 确实支持这种表示法。

已发布代码中 s 表达式表示法的另一个问题(在解决上述问题后):(sqrt - (* b b) (* 4 a c))。这是试图在参数 -(* b b)(* 4 a c) 上调用 sqrt 过程。但是 sqrt 不是高阶过程(即它不将过程作为参数),实际上它只接受一个参数。它的目的是将 - 过程应用于参数 (* b b)(* 4 a c),在求平方根之前减去它们:(sqrt (- (* b b) (* 4 a c))).

第一个 lambda 表达式的形式参数列表仅包含一个参数:abc。和以前一样,这是一个错误。目的是定义三个参数:不要吝啬 spaces:(lambda (a b c)).

另一个重要的问题是 lambda 表达式中存在语法错误:(lambda (a b c)) 没有正文,但是 lambda 表达式 must 在它的主体中至少有一个表达式。这可能是为了包装后面的 lambda 表达式。类似地,内部 lambda 表达式缺少它的主体。它可能是为了包装后面的 (list ;;...) 形式。

完成后,内部 lambda 表达式本身位于一对括号内,将表达式 (sqrt (- (* b b) (* 4 a c))) 作为其参数。这是 let 绑定的 lambda 形式。因此,内部 lambda 接受一个参数 discriminant,并评估作为其主体的 list 形式。由于内部 lambda 表达式本身出现在 s 表达式的第一个位置,它是过程调用的一部分,然后这个内部匿名过程在其参数上被调用,将 discriminant 绑定到值通过评估该参数获得,即 (sqrt (- (* b b) (* 4 a c)))。这一切都发生在外部 lambda 内部,它采用三个参数 abc。因此,root 是一个接受三个参数并返回根列表的函数,在将判别计算的结果绑定到 discriminant 之后(作为一种既简化根表达式又确保判别式只需计算一次)。

这是修复后的代码。请注意,我只添加了一些 space 并添加或移动了一些括号;没有其他改变:

(define roots
  (lambda (a b c)
    ((lambda (discriminant)
       (list (/ (+ (- b) discriminant) (* 2 a))
             (/ (- (- b) discriminant) (* 2 a))))
     (sqrt (- (* b b) (* 4 a c))))))

注意这是什么样子的。在 Lisps 中,你几乎不应该让括号单独挂在行上,你应该总是在参数之间放置一个 space。请记住,一切都是过程调用。

这是一个交互示例。请注意,您 可以 将负数表示为 -1 而不是 (- 1) (如果您愿意,您可以这样做)。您不能使用变量 -b.

来表示负值
> (roots 1 0 -1)
(1 -1)
> (roots 1 8 15)
(-3 -5)