0

我编写了以下程序来计算方案中低于 1000 的 3 和 5 的所有倍数之和。但是,它给了我一个不正确的输出。任何帮助将非常感激。

(define  (multiples)
   (define  (calc  a sum ctr cir)
      (cond (> a 1000) (sum)
            (= ctr 7) (calc (+ a (list-ref cir 0)) (+ sum a) 0 (list 3 2 1 3 1 2 3))
             (else (calc (+ a (list-ref cir ctr)) (+ sum a) (+ 1 ctr) (list 3 2 1 3 1 2 3)))))
    (calc 0 0 0 (list 3 2 1 3 1 2 3)))
4

6 回答 6

1

您可以使用 accumulator( sumparameter) 和一个target参数来测试何时停止求和,从而将命令式风格的解决方案简单地移植到函数式 Scheme:

(define (multiples)
  (define (multiples-iter num sum target)
    (if (> num target)
      sum
      (multiples-iter (+ 1 num)
                      (if (or (zero? (mod num 3)) (zero? (mod num 5)))
                        (+ sum num)
                        sum)
                      target)))
  (multiples-iter 0 0 1000))
于 2012-12-21T16:43:46.253 回答
1

一个问题是您的代码在cond子句周围缺少一对括号。在该行(cond (> a 1000) (sum)中,条件只是>while a并且1000被解释为要评估的形式,如果>为真(它是),因此将返回 1000 作为结果。

另外两个问题(被第一个问题所掩盖)是当 ctr 达到 7 时您将其初始化为 0,而它应该设置为下一个值,即 1,并且您在结果中包含 1000。

您的函数的更正版本是

(define  (multiples)
   (define  (calc  a sum ctr cir)
      (cond ((>= a 1000) sum)
            ((= ctr 7) (calc (+ a (list-ref cir 0)) (+ sum a) 1 (list 3 2 1 3 1 2 3)))
             (else (calc (+ a (list-ref cir ctr)) (+ sum a) (+ 1 ctr) (list 3 2 1 3 1 2 3)))))
    (calc 0 0 0 (list 3 2 1 3 1 2 3)))

同样的算法也可以定义为这样的非递归函数:

(define (multiples)                                                             
  (do ((cir (list 3 2 1 3 1 2 3))                                               
       (ctr 0 (+ ctr 1))                                                        
       (a 0 (+ a (list-ref cir (modulo ctr 7))))                                
       (sum 0 (+ sum a)))                                                       
      ((>= a 1000) sum)))  
于 2012-12-21T19:09:20.283 回答
1

这是我的(特定于球拍的)解决方案,它不涉及大量(或就此而言,任何)modulo调用,并且是完全通用的(因此您不需要构建(3 2 1 3 1 2 3)OP 拥有的列表):

(define (sum-of-multiples a b limit)
  (define (sum-of-multiple x)
    (for/fold ((sum 0))
              ((i (in-range 0 limit x)))
      (+ sum i)))
  (- (+ (sum-of-multiple a) (sum-of-multiple b))
     (sum-of-multiple (lcm a b))))

测试运行:

> (sum-of-multiples 3 5 1000)
233168
于 2012-12-21T17:00:36.863 回答
1

如果您使用的是 Racket,那么有一种非常紧凑的方法可以满足您的要求,使用循环结构:

(for/fold ([sum 0])
  ([i (in-range 1 1000)]
   #:when (or (zero? (modulo i 3)) (zero? (modulo i 5))))
  (+ sum i))

=> 233168
于 2012-12-21T17:01:43.660 回答
0

如果您绝对想使用“重复模式”方法,您可以这样做。

这在间隔列表上使用递归,而不是依赖list-ref和显式索引。

(define (mults limit)
  (define steps '(3 2 1 3 1 2 3))
  (define (mults-help a sum ls)
    (cond ((>= a limit) sum)
          ((null? ls) (mults-help a sum steps))
          (else (mults-help (+ a (car ls)) 
                            (+ a sum)  
                            (cdr ls)))))
  (mults-help 0 0 steps))
于 2012-12-28T14:22:45.343 回答
0
(require-extension (srfi 1))
(define (sum-mod-3-5 upto)
  (define (%sum-mod-3-5 so-far generator-position steps)
    (let ((next (car generator-position)))
      (if (> (+ steps next) upto)
          so-far
          (%sum-mod-3-5 (+ so-far steps)
                        (cdr generator-position)
                        (+ steps next)))))
  (%sum-mod-3-5 0 (circular-list 3 2 1 3 1 2 3) 0)) ; 233168

对于这个特定的任务,它平均会执行一半的操作,然后如果将计数器增加一,那么如果要检查的条件,它也会减少一。

此外,模(可能是变相除法)比求和更昂贵。

编辑:我不是 Scheme 不同方言中模块化系统的专家。此处的 SRFI-1 扩展只是为了更容易创建循环列表而需要。我找不到与 Common Lisp 类似的(#0=(3 2 1 3 1 2 3) . #0#)东西,但也许,更有知识的人会纠正这个问题。

于 2012-12-21T17:47:41.033 回答