1

我刚刚在使用 Scheme 的过程中遇到了另一个障碍。可以肯定地说,我的桌子已经受够了我的脑袋……我已经编写了一个函数来查找班级作业列表中的最小和最大数字。逻辑是合理的(我认为是......)并且一切正常,但是,只有第一个函数调用的值是从(定义(迭代器 aList minNum maxNum))返回的。我在调试器中注意到的是,在每次递归/函数调用之后,我看到(使用 DrRacket)该函数调用被推入堆栈。一旦递归最后一次发生并且代码跳转到 (list minNum maxNum) 的返回,它不会返回它应该返回的值,因为我可以看到值是正确的,而是看到函数调用从堆栈中出来一个接一个,直到它到达第一个。因此,将返回列表中前两个值的初始值。我知道堆栈是 FIFO,但是,我什至没有尝试将任何东西推入堆栈。从理论上讲,我只想再次调用该函数并继续传递值……对此的任何指导将不胜感激。

 (define (findMinMax aList)
  (define (iterator aList minNum maxNum)
   (when(not(null? aList))
    (cond
    ((> minNum (car aList))
     (set! minNum (car aList)))
    ((< maxNum (car aList))
     (set! maxNum (car aList))))
   (iterator (cdr aList) minNum maxNum))
   (list minNum maxNum))
 (cond ; setting the first two atoms in a list appropriately to the min and max variables.
 ((< (car aList) (car(cdr aList)))
  (iterator (cdr (cdr aList)) (car aList) (car(cdr aList))))
 ((> (car aList) (car(cdr aList)))
  (iterator (cdr (cdr aList)) (car(cdr aList)) (car aList)))
 (else
  (iterator (cdr (cdr aList)) (car aList) (car(cdr aList))))))
4

4 回答 4

2

您需要重写代码以不使用任何一个,set!或者when因为它阻碍了您学习 Scheme。在使用 Lisp 方言和 Algol 方言编写时,您必须以不同的方式思考,因此请尝试仅在递归中进行更改,而不是在过程主体中使用set!和使用 3 种方式if和一个表达式。

(define (my-length lst)
  (define (length-aux lst n)
    (if (null? lst)
        n ; base case, return the length
        (length-aux (cdr lst) (+ n 1)))) ; instead of set! we put the new value as argument

  (length-aux lst 0)) ; one expression that calls the auxiliary procedure to do the calculations

您的内部程序可以很简单:

(define (iterator aList minNum maxNum)
  (if (null? <???>)
      (list minNum maxNum)
      (iterator <???> 
                (min <???> <???>)
                (max <???> <???>))))

或者也许用if而不是min/max

(define (iterator aList minNum maxNum)
  (if (null? <???>)
      (list minNum maxNum)
      (let ((n (car aList))) ; use n to compare with minNum/maxNum
        (iterator <???> 
                  (if <???> <???> <???>)
                  (if <???> <???> <???>))))
于 2013-10-21T08:23:36.660 回答
2

您对 Scheme 的熟练程度已经比 SO 上的大多数人要好得多!有一个非常有用的 Scheme 语法关键字 'named-let' 可以很容易地定义内部的递归函数定义。这是一个带您进入下一个级别的示例:

(define (findMinMax list)
  (assert (not (null? list)))
  (let finding ((list (cdr list))
                (lMin (car list))
                (lMax (car list)))
    (if (null? list)
        (values lMin lMax)
        (finding (cdr list)
                 (min lMin (car list))
                 (max lMax (car list))))))

另请注意,我使用values语法形式返回两个值。而且,我使用了内置函数minmax. 此外,finding尾递归的使用意味着 Scheme 编译器将此递归调用转换为迭代调用,因此不需要堆栈帧。

于 2013-10-21T14:23:03.370 回答
1

你有一些误导性的缩进。缩进步长为 1 确实不可取。2 可以,但在学习 Scheme 时 4 更好。

实际上,您只需要在语法方面进行一些最小的更改。代替

(define (findMinMax aList)
    (define (iterator aList minNum maxNum)
        (when (not (null? aList))
            (cond
                ((> minNum (car aList))           ; change the local 
                    (set! minNum (car aList)))    ;   variable's value
                ((< maxNum (car aList))
                    (set! maxNum (car aList))))
            (iterator (cdr aList) minNum maxNum)) ; pass new values on, but
                                          ; ignore recursive result, and
        (list minNum maxNum))             ; return the current values instead!

你需要:

(define (findMinMax aList)
    (define (iterator aList minNum maxNum)
        (if (not (null? aList))
            (begin
                (cond
                    ((> minNum (car aList))
                        (set! minNum (car aList)))
                    ((< maxNum (car aList))
                        (set! maxNum (car aList))))
                (iterator (cdr aList) minNum maxNum)) ; return recursive result!
            (list minNum maxNum)))                    ; ELSE - base case

最初的电话只是:

    (iterator (cdr aList) (car aList) (car aList)))
于 2013-10-21T14:54:04.617 回答
0

是的,远离片场!否则您将无法了解方案功能方面的精髓。如果某些东西非常混乱,您可以使用它,但这种情况很少见。

这里的很多答案都是用递归来表达的,但通常更容易理解的是高阶函数

在某些实现中,内置的 min 和 max 都是按照折叠来定义的。

(define (min first . L) (fold (lambda (x y) (if (< x y) x y)) first L))
(define (max first . L) (fold (lambda (x y) (if (> x y) x y)) first L))

(define (MinMax first . L)
  (define (internal y x)
    (let ((min (car x))
          (max (cdr x)))
      (cons (if (< min y) min y)
            (if (> max y) max y))))
  (fold internal (cons first first) L)) 

请注意,当您可以将所做的事情适应高阶函数时,代码会变得多么清晰。两行定义ADT,两行告诉折叠如何携带本地状态,一行用于实际过程。

;;示例调用

> (minmax 0 9 3 4 7 6 -2)

;Value 4: (-2 . 9)
于 2013-10-21T18:22:49.063 回答