【问题标题】:In Ruby, how do I check if at least 80% of elements in an array match a pattern?在 Ruby 中,如何检查数组中至少 80% 的元素是否与模式匹配?
【发布时间】:2017-05-23 23:48:25
【问题描述】:

我使用的是 Ruby 2.4。如何检查数组中至少 80% 的元素是否与某个模式匹配?我想看看每个元素是否与正则表达式匹配

/\d\d?\s*-\s*\d\d?/

【问题讨论】:

  • 数数并做数学?
  • 过滤并比较新数组和旧数组的百分比?

标签: arrays ruby regex matching ruby-2.4


【解决方案1】:

如果性能确实很重要,您无需检查所有元素以了解是否至少 80% 的元素都符合条件

使用 ruby​​ 2.3,这个实现比 count 解决方案快一点,比 grep 解决方案快两倍:

def pareto_match(array, proportion: 0.8)
  min_success= (array.count * proportion).round(4)
  max_failures= (array.count * (1 - proportion)).round(4)
  success= 0
  failure= 0
  array.each do |element|
    if yield(element)
      success+= 1
      return true if success >= min_success
    else
      failure+= 1
      return false if failure > max_failures
    end
  end
end

pareto_match(array){|e| e =~ /\d\d?\s*-\s*\d\d?/}

【讨论】:

  • 小心浮点运算。对于 10 个元素,min_success 将是 8.0,而 max_failures 将是 1.9999999999999996
  • 死了,我已经编辑了答案。我曾经使用 Bignum 来解决这类问题
  • 由于您正在计算数组元素,您可以将数字四舍五入为整数。
  • 使用to_i 而不是round(x) 这种情况不起作用:pareto_match(%w(1 2 3 a)){|e| e =~ /\d/}
  • round(x) 没有多大意义,因为数组不能包含小数元素。我会使用min_success = (array_count * proportion).roundmax_failures = array.count - min_success。您也可以使用ceilfloor 代替round
【解决方案2】:

我会写:

(array.count{|item| item =~ /\d\d?\s*-\s*\d\d?/} / array.size) >= 0.8

【讨论】:

    【解决方案3】:

    您可以将Enumerable#grep 与简单数学结合使用:

    array.grep(/\d\d?\s*-\s*\d\d?/).size / array.size.to_f >= 0.8
    

    要进一步缩短,您可以使用Numeric#quoNumeric#fdiv

    array.grep(/\d\d?\s*-\s*\d\d?/).size.quo(array.size) >= 0.8
    

    【讨论】:

    • 为什么不只是>= .8
    • 如果你写array.grep(...).size >= 0.8 * array.size,你可以保护to_f
    • .size.fdiv(array.size) >= 0.8 甚至可能是.size.quo(array.size) >= 80/100r
    • @akuhn 是真的 :)
    • @Stefan 不错!之前没有看到quo - 直到(谢谢!)!顺便说一句,为什么不用8/10r 而不是80/100r(我想80/100r 的意图更清楚)? :)
    猜你喜欢
    • 2020-08-20
    • 2016-06-26
    • 1970-01-01
    • 1970-01-01
    • 2020-02-06
    • 2017-02-10
    • 1970-01-01
    • 1970-01-01
    • 2019-05-24
    相关资源
    最近更新 更多