Recursion 定义;减少;计划中的列表

Recursion 定义;减少;计划中的列表,recursion,scheme,undefined-behavior,fold,operation,Recursion,Scheme,Undefined Behavior,Fold,Operation,我加入了“BOR”来表示可见性。以下是我的输出: (define (BOR x y) (cond ((equal? x #t) #t) ((equal? y #t) #t) (else #f)) ) (define (reduce op list) (cond ((null? list) (cond ((BOR (equal? op +) (equal? op -)) 0) ((BOR (

我加入了“BOR”来表示可见性。以下是我的输出:

(define (BOR x y)
  (cond
    ((equal? x #t)  #t)
    ((equal? y #t)  #t)
  (else           #f))
)

(define (reduce op list)
  (cond
    ((null? list)
      (cond
        ((BOR (equal? op +) (equal? op -))  0)
        ((BOR (equal? op *) (equal? op /))  1)
        ((equal? op BOR)                    #f)
        ((equal? op BAND)                   #t)
      (else                               #f)))
  (else  (op (car list) (reduce op (cdr list)))))
)

(display (reduce + '(1 2 3 4 5)))
(newline)
(display (reduce - '(100 20 30)))
(newline)
看来我的定义是正确的,但并没有按照我的意愿来计算减法和除法。我试图删除BOR,在检查空列表后留下6个条件进行检查,输出没有任何变化

您可能会注意到这种行为有点像这样:

120
110
这种触发器行为只有在除法或减法时才会发生。我所有的其他手术都做得很好。这是我的家庭作业,请不要直接回答。也许我遗漏了scheme递归的一些关键行为模式?任何帮助都将不胜感激。

我们可以写出
(reduce-'(100 20 30 5 40))
;它变成

(-100(-20(-30(-5(-40)))


或者使用中缀表示法(100-(20-(30-(5-40)),这解释了触发器问题。

这就是上一个示例中发生的情况:

(reduce - '(100 20 30 5 40)) is called.
This returns (+ (- (+ (- 100 20) 30) 5) 40).
Which is equivalent to 100 - 20 + 30 - 5 + 40 = 145.
但你想要的可能是

(reduce - '(100 20 30 5 40))
=> (- 100 (- 20 (- 30 (- 5 (- 40 0)))))
=> (- 100 (- 20 (- 30 (- 5 40)))
=> (- 100 (- 20 (- 30 -35)))
=> (- 100 (- 20 65))
=> (- 100 -45)
=> 145
这和

> (- 100 20 30 5 40)
5
因此,您需要在
else
子句中更改递归


一个主要问题是,您编写
reduce
的方式会留下一个“悬空”操作,它迫使您在过程中知道要应用哪一个最终元素,以便不干扰结果:0表示
+
-
,1表示
*
//code>,
\f
表示
\t
对于
。这意味着你的
reduce
必须适应你给出的每一个可能的过程,这是不可持续的

有更简单的方法,例如:

> (- (- (- (- 100 20) 30) 5) 40)
5
测试:

(define (reduce op lst)
  (let loop ((res (car lst)) (lst (cdr lst)))
    (if (null? lst)
        res
        (loop (op res (car lst)) (cdr lst)))))

我知道您需要
作为函数;这可以更简单地表达:

> (reduce - '(100 20 30 5 40))
5
> (reduce + '(1 2 3 4 5))
15
> (reduce / '(100 5 4))
5


希望这能有所帮助。

您已经将
reduce
定义为a;它通常在严格的(即非)语言中定义为a,如Scheme:

(define (BOR x y)
  (or x y))

(define (BAND x y)
  (and x y))
现在
(reduce-100(list2030540))
返回(((100-20)-30)-5)-40=5

请注意左边的括号;这就是为什么它被称为左折叠。另外,请注意我安排应用于组合运算
op
的参数顺序。它与通常的相反,但它更适合这里使用的
-
运算。

处理零参数和一参数情况 Scheme中已经定义了+和*运算,以接受0个参数,并在这些情况下返回标识函数。减法和除法函数没有此属性,主要是因为它们对一个参数有一个特例,因为使用
(/2)更方便
return
1/2
比return
2
更方便,拥有
(-2)
2
更方便

形式
不是函数,但也定义为可以取任何数字(包括零)参数。
如果至少有一个参数为true,则返回true;如果没有参数,则返回
#f
如果至少有一个参数为false,则返回false;如果调用时没有参数,则返回
#t

处理交换性 加法和乘法是可交换和关联的,因此您可以向要减少的序列的任一端添加一个标识元素,而不会出现问题。例如,无论如何插入括号,0+a+b+c将与a+b+c+0相同。但是,减法和除法没有此属性。a-b-c通常不相同作为c-b-a,0-a-b与a-b-0不同

这意味着您的reduce函数需要说明元素的组合顺序,以及标识元素(或初始元素)应注入的位置。这通常是典型的折叠,而不仅仅是
reduce

使用初始值实现reduce 我知道你说你不想要解决方案,因为这是家庭作业,但我认为这段代码与你自己的代码会有很大的不同,这不是一个大问题。不仅如此,堆栈溢出的答案应该对每个人都有用,而不仅仅是原始的询问者。最后,reduce是一个非常常见的函数,不难找到implem它的暗示

(define (reduce op z lst)   
  (cond ((null? lst) z)    ; just pass it in as another argument
    (else (reduce op 
                  (op z (car lst))  ; NB! `z` as first arg
                  (cdr lst)))))
这是一个右关联折叠,将
init
插入列表中最后一个元素的右侧。也就是说,对于
(reduce fn'(a b c)i)
,您可以

(define (reduce fn list init)
  (if (null? list) init
      (fn (car list)
          (reduce fn (cdr list) init))))
这意味着你能做到

(fn a (fn b (fn c i)))
对于布尔情况,您需要像
这样的函数,但这些函数非常简单:

(reduce + '(...) 0)
(reduce - '(...) 0)
(reduce * '(...) 1)
(reduce / '(...) 1)
那你就可以了

(define (%or x y)
  (or x y))

(define (%and x y)
  (and x y))
请注意,右关联reduce将identity元素放在除法和减法情况下所需的位置,因为您最终将
'(a b c d)
转换为

(a-(b-(c-(d-0)))

(a/(b/(c/(d/1)))

然而,这很可能不是你想要的,因为这意味着你得到了你描述的“触发器”行为。例如

a/(b/c)=(ac)/b≠ (a/b)/c

对于除法和减法,您可能需要一个左关联的reduce。实际上,在Scheme中,这些方法更有效,因为它们可以以尾部递归方式实现,因此使用更少的堆栈空间

(reduce %or  '(...) #t)
(reduce %and '(...) #f)
这是我感觉最自然的左联想折叠方式,因为初始值放在列表的左侧,所以我们有
((i•a•b)•c)
。当然,问题是对于除法和减法,我们真的需要“初始值”您可以编写一个这样做的版本,但它有点复杂,因为您必须检查三种情况:(i)列表没有元素;(ii)列表有一个元素;以及(iii)列表至少有两个元素:

(define (reduce fn init list)
  (if (null? list) init
      (reduce fn (fn init (car list)) (cdr list))))

我实现了。-=+和/=*。但是我无法理解为什么会发生这种情况。您正在将特殊值编码到
(define (reduce fn list final)
  (cond
    ((null? list)
     init)
    ((null? (cdr list)) 
     (fn (car list) init))
    (else
     ;; do a normal left-associative reduce over the 
     ;; list, but add the final element at the end.
     (let red ((init (fn (car list) (cadr list)))
               (list (cddr list))
       (if (null? list) (fn init final)
           (red (fn init (car list)) (cdr list))))))))