代码之家  ›  专栏  ›  技术社区  ›  Misha Moroshko Amr Mostafa

为什么Ruby Inject方法不能在没有初始值的情况下求和字符串长度?

  •  5
  • Misha Moroshko Amr Mostafa  · 技术社区  · 15 年前

    为什么下面的代码会发出错误?

    ['hello','stack','overflow'].inject{|memo,s|memo+s.length}
    
    TypeError: can't convert Fixnum into String
            from (irb):2:in `+'
            from (irb):2:in `block in irb_binding'
            from (irb):2:in `each'
            from (irb):2:in `inject'
            from (irb):2
    

    如果通过初始值,则工作正常:

    ['hello','stack','overflow'].inject(0){|memo,s|memo+s.length}
    => 18
    
    3 回复  |  直到 9 年前
        1
  •  16
  •   Saurabh    9 年前

    你有答案了 apidock :

    如果没有显式指定备忘录的初始值,则使用collection的第一个元素作为备忘录的初始值。

    也就是说,如果没有初始值,您将尝试 'hello' + 'stack'.length

        2
  •  6
  •   Jörg W Mittag    15 年前

    正如错误消息已经告诉您的那样,问题在于 TypeError

    类型 Enumerable#inject 没有显式的累加器(通常称为 reduce )是不是有点像

    reduce :: [a] → (a → a → a) → a
    

    或者用一个更为鲁莽的符号,我刚刚编好

    Enumerable[A]#inject {|A, A| A } → A
    

    您会注意到所有类型都是相同的。的元素类型 Enumerable ,块的两个参数类型、块的返回类型和整体方法的返回类型。

    在您的例子中,块的类型只是不相加。这个街区过了两个街区 String 它应该返回一个 弦 . 但是你打电话给 + 第一个参数的方法(它是 弦 )有一个论点是 Integer . 但是 String#+ 不采取 整数 它只需要一个 弦 或者更确切地说是 转换 到A 弦 也就是说对 #to_str . 这就是为什么你得到 类型错误 对于 字符串α+ .

    类型 可枚举注入 具有 显式累加器(通常称为 fold )是不是有点像

    fold :: [b] → a → (a → b → a) → a
    

    或

    Enumerable[B]#inject(A) {|A, B| A } → A
    

    在这里你看到蓄能器可以有一个 不同的 类型,而不是集合的元素类型。这正是你需要的。

    这两条规则通常能让你通过所有 可枚举注入 -相关问题:

    1. 收集器的类型和块的返回类型必须相同
    2. 当不传递显式累加器时,累加器的类型与元素类型相同

    规则1通常会在你做类似的事情时咬你。

    acc[key] = value
    

    在您的块中,因为赋值的值是赋值的,而不是赋值的接收者。你得把这个换成

    acc.tap { acc[key] = value }
    

    在您的特定案例中,已经提到了两种解决方案。使用显式累加器

    ary.reduce(0){|acc, e| acc + e.length }
    

    或转换为整数 第一

    ary.map(&:length).reduce(:+)
    
        3
  •  3
  •   Saurabh    9 年前

    没有初始值, inject 使用集合中的第一个元素作为初始值。

    看见 ruby-doc .

    推荐文章