我可以自动计算 tcl 中某些数据的移动平均值吗?

问题描述

我编写了一个程序来计算某些数据的移动平均值。问题是我不能自动化,我的意思是如果我想通过 10 步或更多的步骤来完成这个过程,用交换条款写 10 行是不明智的。

按 4 步计算移动平均值的部分是:

     set aux  [lindex $line 4]
     set T [lindex $line 1]
     set aux1 [lrange $valores 1 1]
       set valores [lreplace $valores 0 0 $aux1] 
       
     set aux1 [lrange $valores 2 2]
       set valores [lreplace $valores 1 1 $aux1]
     
     set aux1 [lrange $valores 3 3]
       set valores [lreplace $valores 2 2 $aux1]
       
     set aux1 [lrange $valores 4 4]
       set valores [lreplace $valores 3 3 $aux1]
     
       set valores [lreplace $valores 4 4 $aux]
       set promP [avg $valores]

我知道我必须使用 for 循环,但我所做的尝试没有奏效。

解决方法

假设您对数据保持一个窗口,这并不太难。诀窍是制定一个程序来完成关键工作。

set WINDOW_SIZE 10
set storedData {}

proc updateMovingAverage {value} {
    global storedData WINDOW_SIZE

    set storedData [lreplace [list {*}$storedData $value] 0 end-$WINDOW_SIZE]
    return [expr {[tcl::mathop::+ {*}$storedData] / double([llength $storedData])}]
}

或者你可以创建一个类:

oo::class create MovingAverage {
    variable window size

    constructor {{windowSize 10}} {
        set window {}
        set size $windowSize
    }

    method item {value} {
        set window [lreplace [list {*}$window $value] 0 end-$size]
        return
    }

    method average {} {
        return [expr {[tcl::mathop::+ {*}$window] / double([llength $window])}]
    }
}

该类将添加项目和计算平均值分开。后者现在是 Tcl 中的标准模式。添加项目的技巧是将项目附加到列表中,如果列表的前面大于所需的窗口,则修剪掉它; list {*}$thing $value 执行 append-an-item,而 lreplace THING 0 end-$wantedLength 执行前缀修剪(用空的项目序列替换它们)。


这是一个更高效的版本。

oo::class create MovingAverage {
    variable window size index

    constructor {{windowSize 10}} {
        set window {}
        set size $windowSize
        set index 0
    }

    method item {value} {
        lset window $index $value
        set index [expr {($index + 1) % $size}]
        return
    }

    method average {} {
        return [expr {[tcl::mathop::+ {*}$window] / double([llength $window])}]
    }
}

这使用的事实是,从 8.6 开始(完全巧合的是,当集成类时),lset 命令可以将项目附加到列表中。

,

一个协程版本,比如评论中提到的Donal:

[ServiceContract]
public interface IDiscoveryService
{
  ValueTask<SaveDiscoveryResultResponse> SaveDiscoveryResultAsync(IAsyncEnumerable<SaveDiscoveryResultRequest> request);
}

它和类版本比标准 proc 版本(如 Donal 的第一个示例)有优势,因为它不依赖于任何全局变量,因此您可以同时计算具有不同窗口大小的多个不同数据集不同协程/对象中的时间。