使用散列来区分正数、奇数、偶数和负数

Using hash to tell positive, odd, even, and negative numbers

我有一个数组:

    ak = [10, 20, 3, 4, 5, -5, 28, 27]

我想要这样的解决方案:

    #even:4
    #odd:3
    #positive:7
    #negative:1

如何使用哈希来做到这一点?

您可以遍历数组并像这样测试每个值:

def evaluate(array)
  response = { even: 0, odd: 0, positive: 0, negative: 0 }
  array.each do |item|
    response[:even] += 1 if item.even?
    response[:odd] += 1 if item.odd?
    ...
  end
  response
end

或者类似的东西。之后可以优化。

def calculate(arr)
   even = arr.select {|e| e.even?}.size
   odd = arr.select {|e| e.odd?}.size
   pos = arr.select {|e| e >= 0}.size
   neg = arr.select {|e| e < 0}.size

   hash = {even: even, odd: odd, positive: pos, negative:neg}
end

假设(基于您期望的输出)您只需要正偶数或奇数:

h = Hash.new
h["even"] = ak.select {|x| x.even? && x > 0}.count
h["odd"] = ak.select {|x| x.odd? && x > 0}.count
h["positive"] = ak.select {|x| x > 0}.count
h["negative"] = ak.select {|x| x < 0}.count

puts h

另一个解决方案:

ak = [10, 20, 3, 4, 5, -5, 28, 27]
akp = ak.select{ |n| n > 0 }
h = {
  even: akp.count(&:even?),
  odd: akp.count(&:odd?),
  positive: akp.count,
  negative: ak.count{ |n| n < 0 }
}
puts ak, h

您可以按如下所示以相当通用(可重用)的方式执行此操作。

代码

def analyze_array(ak, ops)
  ops.each_with_object({}) { |(k,m),h| h.update(k=>ak.count(&m)) }
end

例子

ak = [10, 20, 3, 4, 5, -5, 28, 27]

ops = [[:even,     :even?       ],
       [:odd,      :odd?        ],
       [:positive, ->(n) { n>0 }],
       [:negative, ->(n) { n<0 }]]

analyze_array(ak, ops)
  #=> {:even=>4, :odd=>4, :positive=>7, :negative=>1} 

说明

对于上面的例子:

enum = ops.each_with_object({})
  #=> #<Enumerator: [[:even, :even?], [:odd, :odd?],
  #    [:positive, #<Proc:0x007fe90395aaf8@(irb):9 (lambda)>],
  #    [:negative, #<Proc:0x007fe90395aaa8@(irb):10 (lambda)>]]
  #     :each_with_object({})> 

注意 :even?:odd? 是符号(不要与方法混淆)。

enum 的元素由 Enumerator#each, which calls Array#each 传递到块中。我们可以把enum转成数组看看里面的元素是什么:

enum.to_a
  #=> [[[:even, :even?], {}], [[:odd, :odd?], {}],
  #    [[:positive, #<Proc:0x007fe90395aaf8@(irb):9 (lambda)>], {}],
  #    [[:negative, #<Proc:0x007fe90395aaa8@(irb):10 (lambda)>], {}]] 

并模拟将 enum 的 (4) 个元素传递到带有 Enumerator#next 的块中。 enum[[:even, :even?], {}])的第一个元素传递给块并赋值给块变量:

(k,m),h = enum.next
  #=> [[:even, :even?], {}] 
k #=> :even 
m #=> :even? 
h #=> {} 

接下来,我们使用Hash#update(又名merge!)将一键散列合并到h和returnh的新值中:

h.update(k=>ak.count(&m))
  #=> {}.update(:even=>[10, 20, 3, 4, 5, -5, 28, 27].count(&:even?))
  #=> {:even=>4}

(Ruby 允许我们将 (k=>ak.count(&m)) 写成 shorthand for ({k=>ak.count(&m)})).

像往常一样,& 调用 Symbol#to_proc 将符号 :even? 转换为过程,然后将过程转换为 count.[=45= 的块]

然后将enum的下一个值传递给块("odd"),执行类似的计算并将散列{ :odd=>4 }合并到h中,结果在:

h #=> { :even=>4, :odd=>4 }

然后 enum 的第三个和第四个值被传递给块。唯一的区别是 ak.count(&m) 中的 m 已经是一个过程(实际上是一个 lambda),所以 & 只是将它转换为 count.[=45= 的块]

   h = Hash.new
   h["even"] = ak.select {|x| x.even? && x > 0}.count
   h["odd"] = ak.select {|x| x.odd? && x > 0}.count
   h["positive"] = ak.select {|x| x > 0}.count
   h["negative"] = ak.select {|x| x < 0}.count

并添加

 put h