Количество, размер, длина ... слишком много вариантов в Ruby?


141

Кажется, я не могу найти окончательного ответа по этому поводу, и я хочу убедиться, что понимаю это до «n-го уровня» :-)

    a = {"a" => "Привет", "b" => "Мир"}
    a.count # 2
    а. размер # 2
    а. длина # 2

    а = [10, 20]
    a.count # 2
    а. размер # 2
    а. длина # 2

Итак, что использовать? Если я хочу знать, имеет ли a более одного элемента, то это не имеет значения, но я хочу убедиться, что понимаю реальную разницу. Это относится и к массивам. Я получаю такие же результаты.

Кроме того, я понимаю, что количество / размер / длина имеют разные значения в ActiveRecord. Сейчас меня больше всего интересует чистый Ruby (1.92), но если кто-то захочет узнать о разнице, которую делает AR, это тоже будет признательно.

Благодарность!


5
Явление, с которым вы столкнулись, иногда называют TMTOWTDI : Есть несколько способов сделать это. Этот слоган принадлежит сообществу Perl, и Perl оказал влияние на Ruby.
Эндрю Гримм

обычно это псевдонимы друг для друга - они делают то же самое. Вы также должны помнить об одном методе:, Array#nitemsкоторый возвращает количество элементов, отличных от NIL, в массиве. Но это больше не доступно в Ruby 1.9
Тило

Ответы:


195

Для массивов и хэшей size- это псевдоним length. Они синонимы и делают одно и то же.

count более универсален - он может принимать элемент или предикат и считать только те элементы, которые совпадают.

> [1,2,3].count{|x| x > 2 }
=> 1

В случае, когда вы не предоставляете параметр для подсчета, он имеет тот же эффект, что и длина вызова. Однако может быть разница в производительности.

Из исходного кода для Array видно, что они делают почти то же самое. Вот код C для реализации array.length:

static VALUE
rb_ary_length(VALUE ary)
{
    long len = RARRAY_LEN(ary);
    return LONG2NUM(len);
}

А вот соответствующая часть из реализации array.count:

static VALUE
rb_ary_count(int argc, VALUE *argv, VALUE ary)
{
    long n = 0;

    if (argc == 0) {
        VALUE *p, *pend;

        if (!rb_block_given_p())
            return LONG2NUM(RARRAY_LEN(ary));

        // etc..
    }
}

Код array.countделает несколько дополнительных проверок , но в конце концов вызывает тот же код LONG2NUM(RARRAY_LEN(ary)).

С другой стороны, хэши ( исходный код ), похоже, не реализуют свою собственную оптимизированную версию, countпоэтому используется реализация из Enumerable( исходного кода ), которая выполняет итерацию по всем элементам и считает их один за другим.

В общем, я бы посоветовал использовать length(или его псевдоним size), а не countесли вы хотите знать, сколько всего элементов всего.


Что касается ActiveRecord, с другой стороны, есть существенные различия. посмотрите этот пост:


10

Существует критическая разница для приложений, использующих соединения с базой данных.

Когда вы используете много ORM (ActiveRecord, DataMapper и т. Д.), Общее понимание состоит в том, что .size будет генерировать запрос, который запрашивает все элементы из базы данных ('select * from mytable'), а затем дает вам количество элементов в результате, тогда как .count будет генерировать один запрос ('select count (*) from mytable'), что значительно быстрее.

Поскольку эти ORM настолько распространены, я следую принципу наименьшего удивления. В общем, если у меня что-то уже есть в памяти, я использую .size, а если мой код генерирует запрос к базе данных (или внешней службе через API), я использую .count.


1
Есть кое-что, что нужно учитывать при этом counter_cache. Если есть таблица, fooи has_many bar, у вас будет столбец с fooименем, bars_countкоторый обновляется каждый раз, когда barсоздается / уничтожается. Использование foo.bars.size- это то, что проверяет этот столбец (без фактического запроса bars). foo.bars.countвыполняет фактический запрос, который нарушил бы цель кеша.
Dudo

7

В большинстве случаев (например, Array или String ) size- это псевдоним для length.

countобычно поступает из Enumerable и может принимать необязательный блок предикатов. Таким образом enumerable.count {cond}, [примерно] (enumerable.select {cond}).length- он, конечно, может обойти промежуточную структуру, поскольку ему просто нужен подсчет совпадающих предикатов.

Примечание: я не уверен, count заставляет ли выполнять оценку перечисления, если блок не указан, или если он замыкается на, lengthесли это возможно.

Изменить (и спасибо за ответ Марка!): count Без блока (по крайней мере, для массивов) не вызывает оценку. Я полагаю, что без формального поведения он «открыт» для других реализаций, если принудительное вычисление без предиката вообще имеет смысл.


5

Я нашел хорошее программное обеспечение на http://blog.hasmanythrough.com/2008/2/27/count-length-size

В ActiveRecord есть несколько способов узнать, сколько записей находится в ассоциации, и есть некоторые тонкие различия в том, как они работают.

post.comments.count - Определите количество элементов с помощью запроса SQL COUNT. Вы также можете указать условия для подсчета только подмножества связанных элементов (например: conditions => {: author_name => "josh"}). Если вы настроили кеш счетчика для ассоциации, #count вернет это кешированное значение вместо выполнения нового запроса.

post.comments.length - всегда загружает содержимое ассоциации в память, а затем возвращает количество загруженных элементов. Обратите внимание, что это не приведет к принудительному обновлению, если ассоциация была ранее загружена, а затем новые комментарии были созданы другим способом (например, Comment.create (...) вместо post.comments.create (...)).

post.comments.size - работает как комбинация двух предыдущих вариантов. Если коллекция уже загружена, она вернет ее длину, как при вызове #length. Если он еще не загружен, это похоже на вызов #count.

Также имею личный опыт:

<%= h(params.size.to_s) %> # works_like_that !
<%= h(params.count.to_s) %> # does_not_work_like_that !

2

У нас есть несколько способов узнать, сколько элементов в массиве, например .length, .countи .size. Однако лучше использовать array.size, чем array.count. Потому .sizeчто лучше по производительности.


1

Добавляем еще к ответу Марка Байерса. В Ruby этот метод array.sizeявляется псевдонимом метода Array # length . Нет никакой технической разницы в использовании любого из этих двух методов. Возможно, вы не заметите и разницы в производительности. Тем не менее, он array.countтакже выполняет ту же работу, но с некоторыми дополнительными функциями. Array # count

Его можно использовать для получения общего количества элементов на основе некоторого условия. Счетчик можно вызвать тремя способами:

Array # count # Возвращает количество элементов в массиве

Array # count n # Возвращает количество элементов, имеющих значение n в массиве

Массив # count {| i | ieven?} Возвращает количество на основе условия, установленного для каждого массива элементов.

array = [1,2,3,4,5,6,7,4,3,2,4,5,6,7,1,2,4]

array.size     # => 17
array.length   # => 17
array.count    # => 17

Здесь все три метода работают одинаково. Однако здесь начинается самое countинтересное.

Скажем, я хочу узнать, сколько элементов массива содержит массив со значением 2

array.count 2    # => 3

В массиве всего три элемента со значением 2.

Теперь я хочу найти все элементы массива больше 4

array.count{|i| i > 4}   # =>6

Всего в массиве 6 элементов, превышающих 4.

Я надеюсь, что это дает некоторую информацию о countметоде.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.