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



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

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

在 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的变量名为 _ 。这只是一个约定,意思是"忽略",因为我们不需要它。


当然,这里的一般思路是使用货币对的"滑动窗口"折叠"列表,到目前为止,每一步的累积结果都可用。在您的情况下,该函数是 + ,但可以概括:

(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 个,它可能更有用。

附言完全有可能有一些SRFI可以做这样的事情,或者在Racket中用更优雅的方式做到这一点,我不知道。

拥有一个累加器,同时仍然直接构建你的答案是完全可以的(也就是说,而不是积累一个反向的答案,然后在最后反转它)。

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

相关内容

最新更新