设计模式,用于并行使用两个列表,并返回其中一个列表的其余部分

时间:2014-02-19 19:58:29

标签: scheme idioms

Absract:抽象问题是:

  • 值列表
  • 修饰符列表,作用于值以返回新值的事物 (对于示例代码,我只是将值乘以修饰符值)
  • 修饰符列表不限于与值列表相同的大小。
  • 将修饰符应用于值,并返回任何未使用的修饰符

这是一个使用两个独立函数的版本:一个实际应用修饰符,一个用于获取剩余修饰符

;; Return the modified list
(define (apply-mods vals mods) 
    (if (or (null? vals) (null? mods)) vals
      (cons (* (car vals) (car mods)) (apply-mod (cdr vals) (cdr mods)))
    )
)
;; trim the modifiers 
(define (trim-mods vals mods)
    (if (or (null? vals) (null? mods)) mods
    (trim-mods (cdr vals) (cdr mods))
)

我的想法是,在我应用修饰符列表(apply-mods vals mods)后,我可能想要使用其余的 后续操作中的修饰符(trim-mods vals mods)

目前,我提出的最好的方法是两种函数方法,但迭代列表两次似乎很浪费。

是否有一种干净的方法可以返回修改后的值和未使用的修饰符?

具体具体问题是:

  • 我的价值观是音符;每个都有一个音量和一个持续时间。就像是: (vol: 1, dur: 1 beat)(vol: 1 dur: 2 beats)(vol: 1 dur: 1 beat)...
  • 我的修饰符是“音量的变化”,每个都有音量变化和持续时间 (dvol: +1 dur: 4 beats)(dvol: -2 dur: 4 beats)...
  • 当我通过列表进行递归时,我会跟踪净累计时间,以确定哪个修饰符对给定音符有效。

因此,在真正的问题中,修饰符与值之间没有简单的1-1映射,因此我希望遇到一些情况,我会将修饰符列表应用于更短的音符列表(就术语而言)持续时间)比票据清单;然后我想申请 剩下的修饰符到下一个音符列表(我计划将整个音乐分成几个块)。

2 个答案:

答案 0 :(得分:2)

假设这些是预期的结果:

> (apply-mods '((1 . 10)) '((1 . 4) (2 . 4) (3 . 4)))
'((2 . 4) (3 . 4) (4 . 2))
'((3 . 2))

> (apply-mods '((1 . 1) (1 . 2) (1 . 1)) '((+1 . 4) (-2 . 4)))
'((2 . 1) (2 . 2) (2 . 1))
'((-2 . 4))

这是一个简单的循环处理2并行列表:

(define (apply-mods vals mods)
  (let loop ((vals vals) (mods mods) (res null))
    (cond
      ((null? vals) (values (reverse res) mods))
      ((null? mods) (error "not enough mods"))
      (else
       (let ((val (car vals)) (mod (car mods)))
         (let ((vol (car val)) (dur (cdr val)) (dvol (car mod)) (ddur (cdr mod)))
           (cond
             ; case 1. duration of note = duration of mod => consume note and mod
             ((= dur ddur)
              (loop (cdr vals) 
                    (cdr mods) 
                    (cons (cons (+ vol dvol) dur) res)))
             ; case 2. duration of note < duration of mod => consume note, push back shorter mod
             ((< dur ddur) 
              (loop (cdr vals) 
                    (cons (cons dvol (- ddur dur)) (cdr mods)) 
                    (cons (cons (+ vol dvol) dur) res)))
             ; case 3. duration of note > duration of mod => push back part of note, consume mod
             (else         
              (loop (cons (cons vol (- dur ddur)) (cdr vals)) 
                    (cdr mods) 
                    (cons (cons (+ vol dvol) ddur) res))))))))))

您的要求似乎更简单,您可能只需要涵盖案例1,但我只能在等待示例时进行推测。无论如何,您将能够非常轻松地根据您的特定需求调整此代码。

答案 1 :(得分:1)

听起来你可能想要一个可变数据结构,比如队列。

(make-mod-queue '(dvol: +1 dur: 4 beats)(dvol: -2 dur: 4 beats)...))

#queue((4 (dvol: +1)) (4 (dvol: -2)) ...)

(make-note-queue '(vol: 1, dur: 1 beat)(vol: 1 dur: 2 beats)(vol: 1 dur: 1 beat))

#queue((1 (vol" 1)) (1 (vol: 1)) (2 (vol: 1))

然后是一个组合它们的功能

(define (apply-mods note-queue mod-queue)
 (let ((new-queue make-empty-queue))
       (get-note-dur (lambda () 
                      (if (emtpy-queue? note-queue)
                          #f  
                          (car (front-queue note-queue)))))
       (get-mod-dur (lambda () 
                      (if (empty-queue? mod-queue)
                          #f
                          (car (front-queue mod-queue)))))
       (get-vol 
          (lambda () 
            (if (or (empty-queue? mod-queue) (empty-queue? mod-queue))
                 #f        
                 (+ (note-vol (front-queue note-queue)) 
                    (mod-vol  (front-queue mod-queue)))))))
   (let loop ((d1 (get-note-dur)) ;;should return #f is note-queue is empty
              (d2 (get-mod-dur))  ;;ditto for mod-queue
              (vol (get-volume))) 
    (cond ((not vol) 
           (cond ((and d2 (not (= d2 (get-mod-dur))))
                  (set-car! (front-queue mod-queue) d2) new-queue)
                  new-queue)
                 ((and d1 (not (= d1 (get-note-dur))))
                  (set-car! (front-queue note-queue) d1) new-queue)
                  new-queue)
                 (else new-queue)))
          ((= d1 d2)
           (insert-queue! new-queue (cons d1 (list 'vol: vol)))
           (delete-queue! note-queue)
           (delete-queue! mod-queue)
           (loop (get-note-dur) (get-mod-dur) (get-volume)
          ((< d1 d2)
           (insert-queue! new-queue (cons d1 (list 'vol: vol)))
           (delete-queue! note-queue)
           (loop (get-note-dur) (- d2 d1) (get-volume)))
          ((> d1 d2)
           (insert-queue! new-queue (cons d2 (list 'vol: vol)))
           (delete-queue! mod-queue)
           (loop (- d1 d2) (get-mod-dur) (get-volume)))))))

会回来     #queue(1(vol“2))(1(vol:2))(2(vol:2) 和你的mod队列(无论你传递的是什么,现在都会变异为     #queue(4(​​dvol:-2))...), 原始的音符队列现在是一个空队列

如SICP

中所述的队列

http://mitpress.mit.edu/sicp/full-text/sicp/book/node62.html