3

有没有更短 ⁄ 更好的写法?可能有一些库可以进行转换,但我想知道某种 地图折叠是否可以工作?

(define (weights-to-range lw)
  ; '(1 4 6 6 6 6 6) -> (1 5 11 17 23 29 35)
    (define (f x lw acc)
    (if (null? lw)
        acc
        (let ([y (+ x (car lw))])
          (f y (cdr lw) (append acc (list y))))))
  (f (car lw) (cdr lw) (list (car lw))))
4

2 回答 2

3

在 Racket 中,我可能会使用for/fold列表理解来编写它:

(define (weights-to-range weights)
  (define-values (xs _)
    (for/fold ([xs '()] [prev 0])
              ([weight (in-list weights)])
      (define this (+ prev weight))
      (values (cons this xs) this)))
  (reverse xs))

(require rackunit)
(check-equal? (weights-to-range '(1 4 6 6 6 6 6))
              '(1 5 11 17 23 29 35))

除了这个之外,它会更简单,因为这提供了两个累积值fold/fold- xs并且prev-for/fold表单将返回两个值。因此,我们需要将两者都放入临时变量中define-values,然后再传递我们关心的一个 - 从xs- 到reverse。(varprev被命名为_。这只是一个约定,意思是“忽略”,因为我们不需要它。)


当然,这里的一般想法是使用成对的“滑动窗口”来“折叠”列表,到目前为止,累积结果可用于每个步骤。在您的情况下,函数是+,但可以概括为:

(define (fold-slide f vs)
  (define-values (xs _)
    (for/fold ([xs '()] [prev 0])
              ([v (in-list vs)])
      (define this (f prev v))
      (values (cons this xs) this)))
  (reverse xs))

有了这样一个fold-slide(因为没有更好的名字)函数,你可以简单地写:

(fold-slide + '(1 4 6 6 6 6 6)

fold-slide如果它可以处理任何大小的“窗口”,而不仅仅是 2,那么它可能会更有用。

ps 完全有可能有一些 SRFI 做这样的事情,或者在 Racket 中做这件事的更优雅的方式,我不知道。

于 2013-09-07T06:24:50.917 回答
2

在仍然直接构建答案的同时拥有一个累加器是非常好的(也就是说,而不是累积一个反转的答案然后在最后反转它)。

;; weights-to-range : (listof number) -> (listof number)
;; Returns list of partial sums of input list.
(define (weights-to-range lw0)

  ;; helper : (listof number) number -> (listof number)
  ;; acc is the sum of elements seen so far
  (define (helper lw acc)
    (cond [(null? lw)
           null]
          [else
           (let ([new-acc (+ acc (car lw))])
             (cons new-acc (helper (cdr lw) new-acc)))]))

  (helper lw0 0))
于 2013-09-07T13:55:41.000 回答