2011-05-03 213 views
12

我有一个对象集合,我想将其分解为集合集合,其中每个连续的3个元素集合在一个集合中。 举例来说,如果我有如何在Groovy中切片集合

def l = [1,4,2,4,5,9] 

我希望把它变成:

def r = [[1,4,2], [4,5,9]] 

我现在做它通过循环收集和破坏它。但后来我需要通过这些团体'成并行功能对其进行处理..这将是很好的(N)消除此操作系统前处理工作,只是说像

l.slice(3).collectParallel { subC -> process(subC) } 

我发现步法上Range类,但它看起来像只对索引起作用。任何聪明的想法?

更新: 我不认为这是引用链接的重复,尽管它非常接近。如下所示,它更像是我正在寻找的迭代器类型的东西。子集合然后将被传递到GPars collectParallel中。理想情况下,我不需要分配一个全新的集合。

+1

可能重复的[Groovy的内置分割的阵列划分为相等大小的子阵列?](http://stackoverflow.com/questions/2924395/groovy-built-in-to-split-an-array -into-equal-sized-subarrays) – 2011-05-03 18:24:18

+0

我同意这不是一个确切的重复,因为你寻找的是懒惰的本质。 – 2011-05-04 02:34:35

+0

我不会称之为片,而是整理。我认为切片是更像这样的:http://www.webquills.net/web-development/perl/perl-5-hash-slices-can-replace.html – 2017-09-11 17:20:01

回答

20

退房常规1.8.6。 List上有一个新的整理方法。

def list = [1, 2, 3, 4] 
assert list.collate(4) == [[1, 2, 3, 4]] // gets you everything 
assert list.collate(2) == [[1, 2], [3, 4]] //splits evenly 
assert list.collate(3) == [[1, 2, 3], [4]] // won't split evenly, remainder in last list. 

看看在Groovy List documentation更多的信息,因为有一对夫妇,让你一些其他的选择,包括丢弃其余为其他则params的。

就您的并行处理而言,您可以使用gpars巡览整个列表。

def list = [1, 2, 3, 4, 5] 
GParsPool.withPool { 
    list.collate(2).eachParallel { 
    println it 
    } 
} 
2

如果我的理解正确,那么您当前正在将原始集合中的元素复制到子集合中。要了解更多关于这些问题的建议,请查看以下问题的答案:Split collection into sub collections in Groovy

这听起来像是你正在寻找的是子集合有效地成为原始集合视图的一种方式。如果是这种情况,请查看List.subList() method。你可以循环索引从0到size(),以3为增量(或者你选择的任何切片大小),或者你可以更好地创建一个Iterable/List来隐藏来自调用者的细节。这是后者的实现,受Ted's answer的启发。

class Slicer implements Iterator { 
    private List backingList 
    private int sliceSize 
    private int index 

    Slicer(List backingList, int sliceSize) { 
    this.backingList = backingList 
    this.sliceSize = sliceSize 
    } 

    Object next() { 
    if (!hasNext()) { 
     throw new NoSuchElementException() 
    } 

    def ret 
    if (index + sliceSize <= backingList.size()) { 
     ret = backingList.subList(index, index+sliceSize) 
    } else if (hasNext()) { 
     ret = backingList.subList(index, backingList.size()) 
    } 
    index += sliceSize 
    return ret 
    } 

    boolean hasNext() { 
    return index < backingList.size() 
    } 

    void remove() { 
    throw new UnsupportedOperationException() //I'm lazy ;) 
    } 
} 
+0

感谢您的信息 - 你是对的,它更多的是我想创建的'视图'。不需要重新分配任何新的对象。迈克尔的链接上面的方法很好,就像我现有的代码一样。这种“可迭代”实现听起来是正确的 - 想知道需要多少代码 - 需要定义一个新的Iterable实现?子类迭代器? – Bobby 2011-05-03 19:01:21

+0

我想你可能在你的hasNext()的实现中有一个错误的错误。尝试使用包含0,1,2,3,4,5个元素的列表,并将sliceSize设为4. – jabley 2011-07-08 14:28:42

+0

@jabley您说得对!我不确定是什么让我把它放在那里。我会去除它。 – 2011-07-09 17:04:23

0

没有内置在做的正是你想要的任何东西,但如果我们@Delegate调用本地列表的迭代器,我们可以写我们自己的类,它的工作原理就像一个返回块你”一个Iterator重新寻找:

class Slicer { 
    protected Integer sliceSize 
    @Delegate Iterator iterator 

    Slicer(objectWithIterator, Integer sliceSize) { 
     this.iterator = objectWithIterator.iterator() 
     this.sliceSize = sliceSize 
    } 

    Object next() { 
     List currentSlice = [] 
     while(hasNext() && currentSlice.size() < sliceSize) { 
      currentSlice << this.iterator.next() 
     } 
     return currentSlice 
    } 
} 

assert [[1,4,2], [4,5,9]] == new Slicer([1,4,2,4,5,9], 3).collect { it } 

因为它拥有所有的,正常的迭代器的方法,你会得到免费的懒评价上的任何Groovy的语法糖的方法是有一个iterator()方法,就像一个范围:

assert [5,6] == new Slicer(1..100, 2).find { slice -> slice.first() == 5 } 

assert [[9, 10], [19, 20], [29, 30]] == new Slicer(1..30, 2).findAll { slice -> !(slice[1] % 10) } 
+0

我喜欢你的解决方案,但它仍然像制作副本而不是拥有原始列表视图的子列表。我试图找出是否有某种方法将您的方法与List.subList()结合起来。 – 2011-05-04 15:15:16

+0

我可能不理解你的评论,但上面的代码没有做副本,它创建了一个惰性列表迭代器,它将委托给原始列表的本地迭代器。没有列表复制正在发生。我不知道如何创建像collectParallel可以使用的列表的懒惰版本。很多非懒惰的版本(像注入,但不懒惰,如果你想出点什么,我想看看:)。 – 2011-05-04 23:33:57

+0

我的想法终于合并了。当你知道支持对象的所有信息都是Iterable时,你的解决方案是完美的。然而,如果你知道它是一个RandomAccessList(例如一个ArrayList),你可以在Slicer中维护一个正在运行的索引,并让你的下一个方法返回'backingList.subList(index,index + sliceSize)'。当sliceSize为3时,它不会有太大区别,但是对于1000,您将避免为每个片分配空间以及将项添加到片中的时间。更有意义? – 2011-05-05 01:36:17

1

我喜欢这两个解决方案,但在这里是第一个解决的轻微改善的版本,我很喜欢:

class Slicer implements Iterator { 
private List backingList 
private int sliceSize 
private int index 

Slicer(List backingList, int sliceSize) { 
    this.backingList = backingList; 
    int ss = sliceSize; 

    // negitive sliceSize = -N means, split the list into N equal (or near equal) pieces 
    if(sliceSize < 0) { 
     ss = -sliceSize; 
     ss = (int)((backingList.size()+ss-1)/ss); 
    } 
    this.sliceSize = ss 
} 

Object next() { 
    if (!hasNext()) { 
    throw new NoSuchElementException() 
    } 

    def ret = backingList.subList(index, Math.min(index+sliceSize , backingList.size())); 
    index += sliceSize 
    return ret 
    } 

    boolean hasNext() { 
    return index < backingList.size() - 1 
    } 

    void remove() { 
    throw new UnsupportedOperationException() //I'm lazy ;) 
    } 

    List asList() { 
    this.collect { new ArrayList(it) } 
    } 

    List flatten() { 
    backingList.asImmutable() 
    } 

} 

// ======== TESTS 

    def a = [1,2,3,4,5,6,7,8]; 
    assert [1,2,3,4,5,6,7,8] == a; 
    assert [[1, 2], [3, 4], [5, 6], [7, 8]] == new Slicer(a,2).asList(); 
    assert [[1,2,3], [4,5,6], [7,8]] == (new Slicer(a,3)).collect { it } // alternative to asList but inner items are subList 
    assert [3, 2, 1, 6, 5, 4, 8, 7] == ((new Slicer(a,3)).collect { it.reverse() }).flatten() 

    // show flatten iterator 
    //new Slicer(a,2).flattenEach { print it } 
    //println "" 

    // negetive slice into N pieces, in this example we split it into 2 pieces 
    assert [[1, 2, 3, 4], [5, 6, 7, 8]] == new Slicer(a,-2).collect { it as List } // same asList 
    assert [[1, 2, 3], [4, 5, 6], [7, 8]] == new Slicer(a,-3).asList() 
    //assert a == (new Slicer(a,3)).flattenCollect { it } 
    assert [9..10, 19..20, 29..30] == ((new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) }) 
    assert [[9, 10], [19, 20], [29, 30]] == ((new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) }.collect { it.flatten() }) 

    println((new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) }) 
    println((new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) }.collect { it.flatten() })