Подтвердить что ты не робот

Как получить все элементы из списка без последнего элемента?

Следующий Groovy код

    lines = ['0','1','2','3','4','5']
    println lines[1..lines.size()-1]
    println lines[1..-1]
    println lines[1..<lines.size()-1]
    println lines[1..<-1]
    println lines[1..<-2]
    println lines[1..-2]

производит этот вывод:

[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4]
[1, 0]
[1, 2, 3, 4, 5]
[1, 2, 3, 4]

Так как -1 - это индекс последнего элемента в списке, то первые два имеют смысл (диапазоны в Groovy включают конечный элемент, а не опуская его, как и везде в Java:-()

Строка № 3 - желаемый результат (список без первого и последнего элемента).

Я беспокоюсь о выходе # 4: Почему я получаю [1, 0] для 1..-1?

Также [1, 2, 3, 4, 5] для диапазона 1..<-2 кажется неправильным.

Почему это происходит?

4b9b3361

Ответ 1

Лучший способ взять все элементы, кроме последнего, на мой взгляд, - использовать метод take:

def list = ['a', 'b', 'c']
assert list.take(list.size() - 1) == ['a', 'b']

Он корректно ведет себя в углу, где size == 1:

def list = ['one']
assert list.take(list.size() - 1) == []

Хотя я бы предпочел, чтобы он выбрал исключение в случае size == 0, но поведение не так уж плохо:

def list = []
assert list.take(list.size() - 1) == []

Вы также можете использовать list[0..<list.size()-1] (ваш третий пример), и он будет вести себя одинаково, за исключением пустого списка, и в этом случае он выкинет ArrayIndexOutOfBoundsException, но я думаю, что это не так читаемо, как take копия.


Другим приемлемым решением является list[0..-2] (ваш последний пример), который, я думаю, выглядит намного более элегантным, но, к сожалению, разрывается, когда size == 1 с ArrayIndexOutOfBoundsException.


В ваших примерах (я предполагаю, что вы хотели использовать 0 в качестве начального индекса вместо 1, если вы хотите включить все элементы, кроме последнего):

lines[0..lines.size()-1] эквивалентен lines[0..-1], потому что метод списков getAt(Range) будет обрабатывать диапазоны с отрицательными индексами так же, как и getAt(Integer), т.е. как доступ к элементу списка (list.size() + negativeIndex) 'th. Следовательно, list[0..-1] - это то же самое, что сказать "от первого элемента к последнему" и это то же самое, что копировать список; и list[-1..0] совпадает с "от последнего к первому" и эквивалентно list.reverse():)

Проблема с другим примером неинклюзивного диапазона заключается в том, что неинклюзивные диапазоны оцениваются до доступа к списку, и они оценивают неправильный инклюзивный диапазон. Например, 0..<-2 оценивается как 0..-1 и поэтому возвращает все элементы. 0..<-1 оценивается до 0..0 и возвращает только первый элемент.

Обратите внимание, что пустой диапазон является частным случаем. Он обозначается как 0..<0 и не имеет инклюзивного эквивалента (поэтому Groovy здесь не будет никакого магического преобразования). И поэтому list[0..<list.size()-1] работает, когда size == 1 (диапазон оценивается до пустого диапазона), а list[0..-2] не:)

Ответ 2

Возможно, это изменилось с тех пор, как эпидемия написала свой ответ, но вы можете получить весь список без последнего элемента с 0..<-1:

assert ["foo"][0..<-1] == []
assert ["foo", "bar"][0..<-1] == ["foo"]
assert ["foo", "bar", "baz"][0..<-1] == ["foo", "bar"]


// blows up if empty, here take is better
assert [][0..<-1] == []  // BOOM

// if you want null safe, use take
assert [].take(-1) == []

Это с groovy 2.2.1.

Ответ 3

Так как Groovy 2.4 вы можете использовать метод init():

lines = ['0','1','2','3','4','5']
assert lines.init() == ['0','1','2','3','4']