【问题标题】:Are Scheme functions one liners?方案功能是一种衬里吗?
【发布时间】:2025-12-15 09:30:02
【问题描述】:

我正在关注计算机程序的结构和解释,在尝试解决 Ex 1.3 时,我第一次尝试得到以下代码:

(define (sumsq a b c)(
(define highest (if (> (if (> a b) a b) (if (> a c) a c)) (if (> a b) a b) (if (> a c) a c)))
(define second_h (if (> (if (> a b) b a) (if (> a c) c a)) (if (> a b) b a) (if (> a c) c a)))
(+ (* highest highest) (* second_h second_h)))

它不起作用,我查找了解决方案并在 SICP Wiki 找到了它们

;; ex 1.3
;; implemented using only techniques covered to this point

(define (square x) (* x x))

(define (sum-of-squares x y)
  (+ (square x) (square y)))

(define (largest-two-of-three x y z)
  (if (>= x y)
      (sum-of-squares x (if (>= y z) y z))
      (sum-of-squares y (if (>= x z) x z))))

不同之处在于我使用多个语句来定义变量,然后对平方求和,而正确的方法是将我的每一行定义为函数。

Scheme 中的函数是一种衬里吗?还是我错过了整件事?

【问题讨论】:

    标签: function scheme sicp


    【解决方案1】:

    您应该使用适当的缩进和换行来了解您的程序流程。您的第一个提案如下所示:

    (定义(sumsq a b c) ((定义最高 (如果 (> (如果 (> a b) a b)) (如果(> a c) a c)) (如果(> a b) a b) (如果 (> a c) a c))) (定义第二个 h (如果 (> (如果 (> a b) b a)) (如果 (> a c) c a)) (如果 (> a b) b a) (如果 (> a c) c a))) (+ (* 最高最高) (* second-h second-h)))

    首先要注意:括号不匹配;开放的比封闭的多。仔细检查发现第二行的一个左括号是错误的。顺便说一句,这是在您的第一行末尾以某种方式悬空的那个。我会大胆猜测,当您尝试对此进行评估时,什么都没有发生,因为读者正在等待语句结束。

    正确的缩进非常重要。我认为 SICP 没有明确解释它,尽管示例通常是这样完成的。我找到了一个风格指南here

    第二个观察:你经常重复自己。在所有这些嵌套的 if 语句中,我不确定你是否真的得到了正确的值。查看您找到的解决方案,看看如何大大简化。

    您试图通过给子结果命名来打破复杂性。分解复杂性是好的,但通常最好不要命名结果,而是命名概念。想想你在做什么,然后为这些活动命名。这些是函数,它们构成了您最终几乎可以轻松解决问题的语言。

    【讨论】:

    • 是的,scheme确实等到了语句的结束,我试图平衡()但最后还是放弃了:(
    • 标准缩进确实有帮助。有些编辑器可以自动正确缩进,并帮助您进行括号匹配。我喜欢 emacs,但还有其他的。
    【解决方案2】:

    你写的(减去一个额外的括号)是:

    (define (sumsq a b c)
      (define highest
        (if (> (if (> a b) a b)
               (if (> a c) a c))
          (if (> a b) a b)
          (if (> a c) a c)))
      (define second_h
        (if (> (if (> a b) b a)
               (if (> a c) c a))
          (if (> a b) b a)
          (if (> a c) c a)))
      (+ (* highest highest) (* second_h second_h)))
    

    他们的解决方案将平方和平方和分成单独的函数,但我认为这并不重要。不写(+ (* a a) (* b b)) 阻止你必须命名你正在计算的两个值,这会让你在最后把函数写成一个大表达式,但还有更大的事情需要担心现在。

    我认为您遇到的问题是您的 (if.​​..) 表达式太大而难以理解。请注意,有两种模式多次出现:(if (> a b) a b)(if (> a b) b a)。这些是 max 和 min 函数,因此这样定义它们很有用:

    (define (min a b) (if (< a b) a b))
    (define (max a b) (if (< a b) b a))
    

    这样,您可以将解决方案重写为:

    (define (sumsq a b c)
      (define highest
        (if (> (max a b) (max a c))
          (max a b)
          (max a c)))
      (define second_h
        (if (> (min a b) (min a c))
          (min a b)
          (min a c)))
      (+ (* highest highest) (* second_h second_h)))
    

    再次简化它给出:

    (define (sumsq a b c)
      (define highest
        (max (max a b) (max a c)))
      (define second_h
        (max (min a b) (min a c)))
      (+ (* highest highest) (* second_h second_h)))
    

    注意这个写法更容易推理,(max (max a b) (max a c)) 显然是a bc 的最大值,实际上可以重写为(max (max a b) c)。不过,看看second_h,它的正确性并不明显。当a 是三个值中的最小值时会发生什么?

    他们在解决方案中使用的技巧是首先比较xy。如果x &lt; y,那么你知道y不是三个中最小的,所以它要么是最高的,要么是第二高的。您要使用的另一个数字是xz 中的较大者,因为这两个中的较小者将是三个中最小的一个,您想忽略它。 y &lt; x 时也适用类似的逻辑。

    【讨论】:

      【解决方案3】:

      Scheme 的一个想法是bottom-up programming,您可以在其中为每个概念操作创建一个函数。这是许多函数式编程语言中推荐的方法。

      使用这种方法,您最终会得到很多小函数,它们对参数实现一个逻辑运算。这样一来,您的代码最终会变得更加模块化和简洁。

      【讨论】:

        【解决方案4】:

        您的解决方案具有以下形式:(define (func param) (define...) (define...))

        但是define需要这种形式:(define (func param) body)

        主体是函数的实现......它做什么,它返回什么。你的身体只是更多的定义,从不做任何事情。这就解释了为什么 Scheme 解释器不接受您的解决方案。

        要回答“方案函数是单行的吗?”这个问题。您需要调查“开始”表单,如下所示: (begin (+ 1 1) (+ 2 2)) => 4

        在上面的例子中,(+ 1 1) 的结果只是被抛出,所以你可以看到 begin 只有当它里面的东西有副作用时才真正有意义。

        您应该知道,Scheme 的某些部分(尤其是 let 和 lambda)在其主体周围有隐式开始。所以这是有效的:

          (let ((x 1))
            (+ 1 1)
            (+ 2 2))
        

        即使没有开始。这使得代码更容易编写。

        最后,当您继续学习 Scheme 时,请始终尝试找到一种没有开始也没有副作用的方法。尤其是在大多数 Scheme 书籍的前几章中,如果你在想,“我想设置那个变量,然后我想做这个,然后这个......”你可能陷入了你的旧编程方式而不是做它是Scheme的方式。副作用根本没有什么问题,但大量使用它们意味着您并没有真正按照 Scheme 的最佳工作方式进行编程。

        【讨论】:

        • Scheme 确实允许这种形式: (define (func param) (define...) (define...) body ...) 嵌套的定义被转换成一个letrec,所以它相当于(define (func param) (letrec ((...) (...)) body...).
        【解决方案5】:

        练习 1.3 要求您定义一个过程,该过程接受三个数字作为参数并返回两个较大数字的平方和。使用内置的 Scheme 过程 squaremaxmin 很容易定义这样的过程,但是我们还没有在书中遇到这些过程,所以我会定义它们也是。

        (define (square x)
           (* x x))
        
        (define (max x y)
           (if (> x y) x y))
        
        (define (min x y)
           (if (< x y) x y))
        
        (define (sum-of-highest-squares x y z)
           (+ (square (max x y))
              (square (max (min x y) z))))
        

        sum-of-highest-squares 过程通过将 x 和 y 的最大值的平方(这两个的最大值从三个中的最小值消除)和其余两个最大值的平方(最小值x 和 y,这将是第一步留下的任何值)和 z。

        注意:这是来自我的博文SICP Exercises 1.1 - 1.5。还有一些链接可以将您带到那里的许多其他 SICP 解决方案。

        【讨论】: