在Ruby中,计数、大小和长度有太多的选择?

153

我似乎找不到一个确定的答案,我想确保我在“第n个层次”上理解这个问题 :-)

    a = { "a" => "Hello", "b" => "World" }
    a.count  # 2
    a.size   # 2
    a.length # 2
a = [ 10, 20 ]    a.count # 2    a.size   # 2    a.length # 2

那么应该使用哪个呢?如果我想知道a是否有多于一个元素,那么它似乎并没有关系,但是我想确保我真正理解了它们之间的区别。这也适用于数组。我得到了相同的结果。

此外,我意识到在ActiveRecord中,count/size/length具有不同的含义。我目前主要关注纯Ruby(1.92),但如果有人想谈一下AR的区别,那也将不胜感激。

谢谢!


5
你遇到的现象有时被称为TMTOWTDI:“有多种方法可以做到”。这个口号来自于Perl社区,而Perl是对Ruby产生影响的语言之一。 - Andrew Grimm
这些通常是彼此的别名 - 它们执行相同的操作。还有一个方法也要记在心里:Array # nitems,它返回数组中非NIL项的数量。但是在Ruby 1.9中不再可用。 - Tilo
6个回答

206
对于数组和哈希表,sizelength 的别名。它们是同义词,执行的功能完全相同。 count 更加通用 - 它可以接受元素或断言,并只计算匹配的项。
> [1,2,3].count{|x| x > 2 }
=> 1

如果您不提供计数参数,它与调用长度基本相同。但是可能会有性能差异。

Array的源代码中可以看出,它们几乎完全做了相同的事情。这是array.length实现的C代码:

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))

另一方面,哈希表(source code)似乎没有实现自己优化的count版本,因此使用了来自Enumerablesource code)的实现,该实现会遍历所有元素并逐个计数。

总的来说,如果您想知道总共有多少个元素,请使用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,并且它有许多个 bar,那么在 foo 中会有一个名为 bars_count 的列,每当创建/销毁一个 bar 时,该列就会更新。使用 foo.bars.size 就是检查该列(而不实际查询任何 bars)。foo.bars.count 执行实际查询,这将使缓存失效。 - Dudo

7
在大多数情况下(例如 ArrayString),sizelength 的一个别名

count 通常来自 Enumerable,可以使用可选的谓词块。因此,enumerable.count {cond} 大致等于 (enumerable.select {cond}).length -- 当然,它可以绕过中间结构,因为它只需要匹配谓词的计数。

注意:如果没有指定块,我不确定 count 是否会强制对枚举进行评估,或者是否可能在可能的情况下跳过到 length

编辑(感谢 Mark 的回答!): 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
在Mark Byers的回答上补充一些内容。在Ruby中,方法array.sizeArray#length方法的别名。使用这两种方法没有技术上的区别。可能你在性能方面也看不到任何差别。然而,array.count也可以完成同样的工作,但带有一些额外的功能Array#count 它可以用于基于某些条件获取元素的总数。Count可以通过三种方式调用: Array#count # 返回数组中元素的数量 Array#count n # 返回数组中值为n的元素数量 Array#count{|i| i.even?} 根据对每个元素数组调用的条件返回计数
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方法提供一些信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接