1

作为我正在进行的项目的一部分,我必须构建各种报告。为了构建这些报告,我从非常大的数据集中收集数据。因此,我试图优化我的循环结构,以尽量减少内部循环内的检查。

我经常使用的其中一个全局变量(数据集的键值类型)大致具有以下结构:

dataStatus->inputDate->state->region->street->data

我必须遍历前面的每个部分才能获得我想要的数据。问题是虽然我总是有状态(它是输入屏幕中的必填字段),但可能会省略区域街道。如果这条街道是空白的,我必须遍历该地区的所有街道。同样,如果该区域留空,我必须遍历该州内的所有区域,并再次遍历每个区域中的所有街道。

简单的解决方案是这样的:

loop through dataStatuses {
    loop through dates {
        if street is set {
            get data
        } else if region is set {
            loop through streets {
                get data
            }
        } else {
            loop through regions {
                loop through streets {
                    get data
                }
            }
        }
    }
}

但是,我真的很想要一种跳过内部检查的方法。到目前为止,我能想到的最好的解决方案是这样的:

if street is set {
    loop through dataStatuses {
        loop through dates {
            get data
        }
    }
} else if region is set {
    loop through dataStatuses {
        loop through dates {
            loop through streets {
                get data
            }
        }
    }
} else {
    loop through dataStatuses {
        loop through dates {
            loop through regions {
                loop through streets {
                    get data
                }
            }
        }
    }
}

有没有比这更优雅的解决方案,也许可以在达到数据之前满足 n 级的需求?

任何帮助都感激不尽。

4

3 回答 3

1

唔。嗯,首先,这段代码很可能是 I/O 密集型的,以至于优化除了磁盘访问之外的任何东西都可能是无关紧要的。您可以尝试在每次 if-test 之前将一个毫无意义的循环扔到 1000 以查看这有多大的不同。

看起来索引在这里对您没有帮助,因为您必须在未指定值时访问所有数据,但如果您正在计算聚合,那么缓存这些可能会有所帮助。 Intersystem 的 DeepSee产品就是为此而设计的。

但听起来你可能只是在运行一个打印所有数据的大报告,并且想要优化你可以做的事情。在这种情况下,我认为您的解决方案可以做到这一点。就优雅而言,它通常不会与重度优化结合使用。泛化解决方案的问题在于它通常比定制解决方案慢。

我认为可能使用标签和 goto 语句虽然更难阅读,但可能比您的解决方案运行得快一点 - 如果您认为这是值得的。Intersystems 在其编译的 SQL 例程中执行标记和 goto 的事实使我认为这可能是最快的选择。我没有测量差异,但我想 Intersystems 可能有。如果您真的需要速度,那么您应该尝试不同的方法并测量速度。请务必使用冷暖例程和全局缓存进行测试。

至于通用解决方案,如果您真的愿意,您可以编写一些生成类似于您手动编码的代码的代码,但可以为通用的 n 级解决方案生成它。你告诉它级别,它会生成解决方案。这既通用又快速——但我非常怀疑对于这个例子来说,编写通用解决方案是否会很好地利用你的时间,因为手工编码非常简单,而且很可能并不常见。

于 2013-08-29T16:47:47.767 回答
0

我想出了一个适合我的解决方案。这可能不是我最初寻找的完美,但它对我有用,原因如下:

  1. 当我完成这项工作后,其他程序员将定期处理报告,我希望在这方面让他们的生活尽可能轻松。
  2. 报告生成器会很好,但不是目前最有成效的事情。

我相信我的解决方案在不牺牲太多性能的情况下相当容易阅读和维护:

GatherData
    set command="do LoopThroughRegions(status,date,state,.dataCollection)"
    if ($length(street)>0){
        set command="do LoopThroughData(status,date,state,region,street,.dataCollection)"
    } elseif ($length(region)>0){
        set command="do LoopThroughStreets(status,date,state,region,.dataCollection)"
    }

    set date=fromDate-1,status=""
    for{
        set status=$order(^GLOBAL(status)) quit:status=""
        for{
            set date=$order(^GLOBAL(status,date)) quit:((date>toDate)||(date=""))
            xecute (command)
        }
    }
 quit

LoopThroughRegions(status,date,state,dataCollection)
    set currentRegion="" 
    for{
        set currentRegion=$order(^GLOBAL(status,date,region,currentRegion)) quit:currentRegion=""
        do LoopThroughStreets(status,date,state,currentRegion,.dataCollection)
    }
 quit

LoopThroughStreets(status,date,state,region,dataCollection)
    set currentStreet=""
    for{
        set currentStreet=$order(^GLOBAL(status,date,state,region,currentStreet)) quit:currentStreet=""
        do LoopThroughData(status,date,state,region,currentStreet,.dataCollection)
    }
 quit

LoopThroughData(status,date,state,region,street,dataCollection)
    set dataItem="" 
    for{
        set dataItem=$order(^GLOBAL(status,date,state,region,street,dataItem)) quit:dataItem=""
        // Do stuff
    }
 quit

除非提供更好的解决方案,否则我将选择自己的答案以供将来参考。希望它甚至可以帮助其他人。

于 2013-08-30T12:47:40.087 回答
0

您提出的答案正朝着正确的方向前进,但我会稍微改变它以避免使用 xecute (执行和性能很少发挥良好)并使整体架构更好一点。

与其决定在主程序中做什么并只传入需要的参数,不如传入所有参数并决定在子程序本身中需要做什么。

所以你的代码将变成:

GatherData(...)
    set date=fromDate-1,status=""
    for {
        set status=$order(^GLOBAL(status)) quit:status=""
        for {
            set date=$order(^GLOBAL(status,date)) quit:((date>toDate)||(date=""))
            do LoopThroughRegions(status,date,state,region,street,.dataCollection)
        }
    }
 quit

LoopThroughRegions(status,date,state,region,street,dataCollection)
    if region'="" {
        do LoopThroughStreets(status,date,state,region,street,.dataCollection) 
        quit
    }
    set currentRegion="" 
    for {
        set currentRegion=$order(^GLOBAL(status,date,region,currentRegion)) quit:currentRegion=""
        do LoopThroughStreets(status,date,state,currentRegion,street,.dataCollection)
    }
 quit

LoopThroughStreets(status,date,state,region,street,dataCollection)
    if street'="" {
        do LoopThroughData(status,date,state,region,street,dataCollection) 
        quit
    }
    set currentStreet=""
    for{
        set currentStreet=$order(^GLOBAL(status,date,state,region,currentStreet)) quit:currentStreet=""
        do LoopThroughData(status,date,state,region,currentStreet,.dataCollection)
    }
 quit

LoopThroughData(status,date,state,region,street,dataCollection)
    set dataItem="" 
    for{
        set dataItem=$order(^GLOBAL(status,date,state,region,street,dataItem)) quit:dataItem=""
        // Do stuff
    }
 quit

我还建议将这些小子程序更改为私有程序。

于 2013-09-01T11:44:46.320 回答