加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby – 在数组上使用累加器映射

发布时间:2020-12-17 03:42:02 所属栏目:百科 来源:网络整理
导读:我正在寻找为Enumerable创建一个同时映射和注入的方法.例如,将其称为map_with_accumulator, [1,2,3,4].map_with_accumulator(:+)# = [1,6,10] 或者用于字符串 ['a','b','c','d'].map_with_accumulator {|acc,el| acc + '_' + el}# = ['a','a_b','a_b_c','a_b
我正在寻找为Enumerable创建一个同时映射和注入的方法.例如,将其称为map_with_accumulator,

[1,2,3,4].map_with_accumulator(:+)
# => [1,6,10]

或者用于字符串

['a','b','c','d'].map_with_accumulator {|acc,el| acc + '_' + el}
# => ['a','a_b','a_b_c','a_b_c_d']

我找不到解决方案.我想我可以用reduce来做.我正在沿着这样的道路前进:

arr.reduce([]) {|acc,e| ..... }

初始值是一个空数组,但我无法正确.

编辑:请参阅下面的J?rg答案以获得正确的解决方案.另一种(有点粗略)的方法我在阅读他的答案之后意识到使用instance_eval,它将给定块的上下文更改为执行它的对象的上下文.因此self设置为引用数组而不是调用上下文(这意味着它不再是闭包!)并且数组调用注入和移位.令人费解,不必要的简洁,令人困惑的阅读,但它教会了我一些新的东西.

['a','d'].instance_eval do
  inject([shift]) {|acc,el| acc << acc.last+el}
end
#=> ['a','ab','abc','abcd']

解决方法

此操作称为 scan or prefix_sum,但遗憾的是,Ruby核心库或标准库中没有实现.

但是,你的直觉是正确的:你可以使用Enumerable#inject实现它. (实际上,Enumerable#inject是通用的,每次迭代操作都可以使用inject实现!)

module Enumerable
  def scan(initial)
    inject([initial]) {|acc,el| acc << yield(acc.last,el) }
  end
end

[1,4].scan(0,&:+)
# => [0,1,10]

%w[a b c d].scan('') {|acc,el| acc + '_' + el }
# => ["","_a","_a_b","_a_b_c","_a_b_c_d"]

理想情况下,行为应该与inject的4次重载相匹配(在这种情况下它会给你指定的结果),但不幸的是,在Ruby中实现了这些重载,没有对VM内部的特权访问(特别是在发送网站)是后部的主要痛苦.

它是这样的:

module Enumerable
  # Trying to match the signature of `inject` without access to the VM internals
  # is a PITA :-(
  def scan(initial=(initial_not_given = true; first),meth=nil)
    raise ArgumentError,'You can pass either a block or a method,not both.' if block_given? && meth
    return enum_for(__method__) if initial_not_given && !meth && !block_given?
    return enum_for(__method__,initial) unless initial.is_a?(Symbol) || meth || block_given?
    meth,initial,initial_not_given = initial,first,true unless initial_not_given || meth || block_given?
    raise ArgumentError,"Method #{meth.inspect} is not a Symbol." unless meth.is_a?(Symbol) || block_given?

    this = if initial_not_given then drop(1) else self end

    return this.inject([initial]) {|acc,el| acc << acc.last.__send__(meth,el) } unless block_given?
    this.inject([initial]) {|acc,4].scan(:+)
# => [1,10]

%w[a b c d].scan {|acc,el| acc + '_' + el }
# => ["a","a_b","a_b_c","a_b_c_d"]

正如您所看到的,注入本身的实现相当优雅,丑陋仅仅是因为在没有超载的情况下实现了语言的重载.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读