Лучший способ преобразовать вес в диапазон, используя карту или сгиб?

Есть ли более короткий лучший способ написать следующее? Вероятно, есть какая-то библиотека, которая выполняет преобразование, но мне интересно, может ли работать какая-то карта или fold?

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

person vipjun    schedule 07.09.2013    source источник
comment
Я предполагаю, что начальный 0 в выводе подразумевается?   -  person Adam Burry    schedule 07.09.2013


Ответы (2)


В 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. (Переменная для 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.

p.s. Вполне возможно, что есть какой-то SRFI, который делает что-то подобное, или более элегантный способ сделать это в Racket, чего я не знаю.

person Greg Hendershott    schedule 07.09.2013
comment
Я попробовал, и я придумал это точное решение. - person stchang; 07.09.2013

Совершенно нормально иметь аккумулятор, пока вы все еще строите свой ответ напрямую (то есть вместо того, чтобы накапливать обратный ответ, а затем переворачивать его в конце).

;; 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))
person Ryan Culpepper    schedule 07.09.2013