【问题标题】:Scheme/Racket - Count all positive vals in a listScheme/Racket - 计算列表中的所有正值
【发布时间】:2019-10-19 21:29:05
【问题描述】:

我对 Scheme/Racket 没有太多经验,但我正在尝试创建一个函数来计算列表中有多少正值。到目前为止我有这个:

(define num_pos 0)
(define num_neg 0)
(define (numpos lst)
 (cond
  ((null? lst) 0)
  ((>= (car lst) 0) (+ num_pos 1) (numpos (cdr lst)))
  (else (+ num_neg 1) (numpos (cdr lst)))
  )
)

但是当我在 DrRacket 中运行它并对其进行测试时,lst 始终为空,因此如果我调用 (numpos '(1 -1 2)) 它会通过 null 检查返回 0,但如果我删除 null 检查它在(car lst)上崩溃,说它需要一对但得到'()。我花了一点时间没有运气。有任何想法吗?

【问题讨论】:

    标签: scheme racket


    【解决方案1】:

    (+ num_pos 1) 不会改变num_pos 的值。尝试将其替换为 (set! num_pos (+ num_pos 1)) 并为 num_neg 执行相同操作。

    调用(numpos '(1 -1 2))后,num_pos的值变成了2。

    编辑: 我完全同意奥斯卡的评论,他已经添加了一个更好的答案。这是一个额外的例子,额外的 num_neg 支持:

    (define (numpos lst num_pos num_neg)
     (cond
      ((null? lst)
       (list num_pos num_neg))
      ((>= (car lst) 0)
       (numpos (cdr lst) (+ num_pos 1) num_neg))
      (else
       (numpos (cdr lst) num_pos      (+ num_neg 1)))))
    

    如果您调用(numpos '(1 -1 2) 0 0),则它会返回一个列表,其中包含正值和负值的数量,例如(2 1)

    请注意,新版本需要额外的 2 个参数,每个参数都需要为 0。这些参数的工作方式类似于原始代码中的 num_posnum_neg。这是tail recursive

    使用尾递归,您可以轻松消除副作用(全局变量访问)。

    如果您不喜欢这些额外的参数,您可以使用内部define 重写,如下所示:

    (define (numpos lst)
      (define (iter lst num_pos num_neg)
        (cond
         ((null? lst)
          (list num_pos num_neg))
         ((>= (car lst) 0)
          (iter (cdr lst) (+ num_pos 1) num_neg))
         (else
          (iter (cdr lst) num_pos      (+ num_neg 1)))))
      (iter lst 0 0))
    

    【讨论】:

    • 即使这样会给出正确的答案,但不建议以这种方式使用Scheme。 set! 直到稍后,他们学会了以函数式风格编写解决方案,这是使用该语言的惯用方式。
    • @ÓscarLópez,感谢您的评论。我同意你的观点,但为了帮助最初的提问者理解代码不起作用的原因,我尝试尽量减少对代码的更改。
    • 感谢@torus 的回复,因为它也帮助我理解了!! ?
    【解决方案2】:

    在Scheme 中,我们尝试使用functional programming 范式编写程序。在您的示例中,在过程外部定义一个计数器不是一个好主意,要更改它的值,您需要使用set!指令在内部变异它,并且我们应该避免这样做。

    通常的解决方案(如果我们要“手动​​”解决这个问题)是递归遍历列表并在每次递归调用时递增值,请注意我们甚至不需要在过程中递增变量,像这样:

    (define (numpos lst)
      (cond
        ((null? lst) 0)
        ((>= (car lst) 0) (+ 1 (numpos (cdr lst))))
        (else (numpos (cdr lst)))))
    

    了解其工作原理的关键在这里:

    (+ 1 (numpos (cdr lst)))
    

    我们在递归的结果上加一,并且对每个正数继续这样做,直到到达列表的末尾,并在末尾添加一个零。

    在你的代码中,你写了这个:(+ num_pos 1),但是那个表达式并没有改变num_pos的值,它只是简单地加一到零,但从来没有存储 加法的结果!

    现在我们可以用合适的值来定义变量,我们只需要用程序计算num_posnum_neg的值就可以很容易地推断出来:

    (define num_pos (numpos lst))
    (define num_neg (- (length lst) num_pos))
    

    有很多方法可以解决这个问题。熟悉递归过程后,您会发现有大量内置过程可让您快速找到常见问题的解决方案。事实上,回答您问题的惯用方式是使用count

    (define (numpos lst)
      (count (lambda (n) (>= n 0))
             lst))
    

    【讨论】:

    • 哇,谢谢!!这清楚了很多。我不知道我可以那样做一个增量计数器,我看到我的错误在哪里,因为它每次都会返回 1 甚至不存储一个值!
    猜你喜欢
    • 2015-01-08
    • 1970-01-01
    • 2020-07-17
    • 2011-07-10
    • 2017-07-07
    • 2019-04-13
    • 1970-01-01
    • 2019-10-11
    • 1970-01-01
    相关资源
    最近更新 更多