使用地图或折叠将权重转换为范围的更好方法?

时间:2013-09-07 04:56:14

标签: scheme racket fold higher-order-functions map-function

是否有更短/更好的方式来编写以下内容? 可能有一些库进行转换,但我想知道是否有某种 地图折叠可行吗?

(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))))

2 个答案:

答案 0 :(得分: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 - xsprev提供两个累积值 - for/fold形式将返回两个值。所以我们需要使用define-values将两者都塞进临时变量中,然后再将我们关注的变量从xs - 传递到reverse。 (prev的var名为_。这只是一个含义为“忽略”的约定,因为我们不需要它。)


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

(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)

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

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

答案 1 :(得分: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))