对项目进行分组而不重复逻辑

时间:2016-03-11 20:31:57

标签: algorithm design-patterns imperative-programming

当命令性地编程时,我经常发现自己将代码编写到这样的组中:

function group(items):
    groups <- new Groups
    curGroup <- new Group
    for item in items:
        if item doesn't belong in curGroup:
            if curGroup is good:
                add curGroup to groups
            curGroup <- new Group
        add item to curGroup
    if curGroup is good:
        add curGroup to groups
    return groups

不幸的是,这段代码存在一些不足之处:

  • if curGroup is good: add curGroup to groups代码重复。虽然条件中的条件可以分解为函数,但调用该函数并将curGroups添加到组的逻辑仍会出现两次,并且很容易忘记第二次出现。

  • 创建新组的逻辑出现两次。这个逻辑可能很简单,如果不是那么它可以分解成一个单独的函数,但与第一个项目符号点一样,它表示流程不正确。

  • 第一项可能无法通过归属检查,在这种情况下,我们会在创建新组后立即创建新组。此问题可能看似微不足道,但有时需要明确阻止将初始空组添加到groups。无论如何,它表明了所需逻辑的错误表达。

我想知道是否有一种更清晰的方式来表达这种逻辑。我为这个问题的抽象性质道歉,但这个问题出现在多个上下文中。如果有必要在特定编程语言的上下文中解决此问题,则可以假设Java。

2 个答案:

答案 0 :(得分:1)

解决此问题的一种方法是将is good组过滤器从分组循环中拆分 - 将其视为后处理或需求驱动的过滤。您可以争辩说,将两者结合起来(如您的问题中所示)是过早优化导致尴尬代码的一个例子。

如果使用内部Group循环对while item进行外循环迭代,则可以自然地避免重复new Group代码。它还应该有助于巧妙地处理您的第一项问题,方法是更容易对待组中的第一项与其他项不同:

function group(items):
    groups <- new Groups
    while(items not empty):
        curGroup <- new Group
        using items:
            add current item to curGroup
            advance to next item
        while(items not empty):
            using items:
                if current item belongs in curGroup:
                    add current item to curGroup
                    advance to next item
                else exit inner loop
        if(curGroup is good):
            add curGroup to groups
    return groups

请注意,上面的伪代码使用items作为迭代器。

即使您的问题是关于命令式编程,查看groupBy的Haskell实现也许是有益的。

答案 1 :(得分:1)

我接近这个的方法是在你的第一个if语句中添加一个附加条件,这样它就包含了最终if语句的逻辑。如果该项目不属于当前群组,或者我位于curGroup中的最后group,我会确保将item添加到items

这不是一个大规模的改进(它仍然是八行代码,我不喜欢奇怪的嵌套if语句),但我无法想到更好的解决方案。

它很好地解决了你的三个问题:

  1. if curGroup is good: add curGroup to groups不再重复
  2. Group添加Groups也不再重复
  3. 我的重组并没有直接解决这个问题。但是,您可以通过确保在组空时总是说项目属于组中来轻松避免问题3(这对我来说很有意义,但我不知道您的分组实际在做什么的细节)。
  4. 这可能是这样的:

    function group(items):
        groups <- new Groups
        curGroup <- new Group
        for item in items: 
            if item doesn't belong in curGroup || item is last item:
                if item is last item:
                    add item to curGroup
                if curGroup is good:
                    add curGroup to groups
                curGroup <- new Group
            add item to curGroup
        return groups
    

    我很高兴看到比这更好,更精致的解决方案,但我想我会发布这个至少让事情滚动

    更新

    这是一个不同的方向你可以采取这个(如果它在Java中工作,我更习惯于C#)。不是构建一组组,而是构建一个哈希映射(我将它称为字典,因为它就是C#中的内容),键是你计算的一些值来确定一个项属于哪个组,而值是一组项目。确定项目属于哪个组的功能应该与您当前检查项目是否属于当前gruop的方式非常相似。

    然后你的代码看起来像这样:

    function group(items):
        groups <- new Dictionary<string, Group>
        for item in items:
            groupKey <- item.FindKey()
            if !groups.ContainsKey(groupKey):
                add new group to groups with key of groupKey
            add item to groups[groupKey]
        return groups
    

    这种方法的优点:

    1. 没有重复的代码
    2. 项目的顺序并不重要,而在此之前确实很重要(在某些情况下这可能是一个缺点,如果是这样,请告诉我,有办法解决这个问题。)
    3. 很容易理解项目属于给定组的原因(便于调试)
    4. 缺点

      1. 如前所述,订单可能很重要,以保存
      2. 使用更复杂的数据结构(虽然HashMap / Dictionary并不复杂)
      3. 可能很难为项目编写FindKey函数(虽然我怀疑它在大多数情况下都不会)