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

编写此代码的更“ruby”是什么?

发布时间:2020-12-17 02:51:09 所属栏目:百科 来源:网络整理
导读:这是我的学生(我是一名助教)的家庭作业,我正在努力学习 Ruby,所以我想我会编写代码.目标是从重定向文件中读取整数并打印一些简单信息.文件中的第一行是元素的数量,然后每个整数都驻留在它自己的行上. 这段代码可以工作(尽管效率可能不高),但是如何使代码更像
这是我的学生(我是一名助教)的家庭作业,我正在努力学习 Ruby,所以我想我会编写代码.目标是从重定向文件中读取整数并打印一些简单信息.文件中的第一行是元素的数量,然后每个整数都驻留在它自己的行上.

这段代码可以工作(尽管效率可能不高),但是如何使代码更像Ruby呢?

#!/usr/bin/ruby -w

# first line is number of inputs (Don't need it)
num_inputs = STDIN.gets.to_i

# read inputs as ints
h = Hash.new
STDIN.each do |n|
  n = n.to_i
  h[n] = 1 unless h[n] and h[n] += 1      
end

# find smallest mode
h.sort.each do |k,v|
  break puts "Mode is: #{k}","n" if v == h.values.max
end

# mode unique?
v = h.values.sort
print "Mode is unique: "
puts v.pop == v.pop,"n"

# print number of singleton odds,#       odd elems repeated odd number times in desc order
#       even singletons in desc order
odd_once = 0
odd = Array.new
even = Array.new
h.each_pair do |k,v|
  odd_once += 1 if v == 1 and k.odd?
  odd << k if v.odd?
  even << k if v == 1 and k.even?
end
puts "Number of elements with an odd value that appear only once: #{odd_once}","n"
puts "Elements repeated an odd number of times:"
puts odd.sort.reverse,"n"
puts "Elements with an even value that appear exactly once:"
puts even.sort.reverse,"n"

# print fib numbers in the hash
class Fixnum
  def is_fib?
    l,h = 0,1
    while h <= self
      return true if h == self
      l,h = h,l+h
    end
  end
end
puts "Fibonacci numbers:"
h.keys.sort.each do |n|
  puts n if n.is_fib?
end

解决方法

我不知道这是否是“更多Ruby方式”. FWIW至少在更“高阶”的方式.

# first line is number of inputs (Don't need it),thus drop the first line
# read inputs as ints
h = ARGF.drop(1).reduce(Hash.new(0)) {|h,n| h.tap {|h| h[n.to_i] += 1 }}

这里不多说.我们使用reduce来让它为我们完成工作,而不是简单地循环遍历ARGF并设置哈希键.我们使用默认值为0的哈希,而不是手动检查密钥是否存在.

我们使用Enumerable#drop简单地删除第一行.

ARGF是一个很酷的功能(像大多数脚本功能一样)来自Perl:如果您只是将脚本称为script.rb而没有参数,那么ARGF就是标准输入.但是,如果你将脚本称为script.rb a.txt b.txt,则Ruby会将所有参数解释为文件名,打开所有文件进行读取,ARGF将是其内容的串联.这使您可以非常快速地编写可以通过标准输入或文件获取输入的脚本.

# find smallest mode
modes = h.group_by(&:last).sort.last.last.map(&:first).sort
puts "Mode is: #{modes.first}"

Ruby没有明确的键值对类型,而是哈希上的大多数循环操作都使用双元素数组.这允许我们使用Array#first和Array#last来引用键和值.

在这种特殊情况下,我们使用Enumerable#group_by将散列分组到不同的桶中,我们使用的分组标准是最后一种方法,即我们的散列中的值是频率.换句话说,我们按频率分组.

如果我们现在对得到的散列进行排序,则最后一个元素是具有最高频率的元素(即模式).我们取最后一个元素(键值对的值),然后是最后一个元素,它是一个键值对(数字=>频率)的数组,我们从中提取键(数字)并对它们进行排序.

[注意:只需在每个中间阶段打印出结果,就会更容易理解.只需用以下内容替换上面的modes = …行:

p modes = h.tap(&method(:p)).
  group_by(&:last).tap(&method(:p)).
  sort.tap(&method(:p)).
  last.tap(&method(:p)).
  last.tap(&method(:p)).
  map(&:first).tap(&method(:p)).
  sort

]

模式现在是一个排序数组,其中包含具有该特定频率的所有数字.如果我们采用第一个元素,我们有最小的模式.

# mode unique?
puts "Mode is #{unless modes.size == 1 then '*not* ' end}unique."

如果数组的大小不是1,那么模式不是唯一的.

# print number of singleton odds,#       odd elems repeated odd number times in desc order
#       even singletons in desc order
odds,evens = h.select {|_,f|f==1}.map(&:first).sort.reverse.partition(&:odd?)

看起来这里有很多东西,但它实际上很简单.您在等号后开始阅读,只需从左向右阅读.

>我们选择哈希值中的所有项目(即频率)为1.我们选择所有单例.
>我们将所有得到的键值对映射到它们的第一个元素,即数字 – 我们扔掉频率.
>我们对列表进行排序
>然后反转它(对于较大的列表,我们应该反过来排序,因为这是浪费CPU周期和内存)
>最后,我们将数组分为两个数组,一个包含所有奇数,另一个包含所有偶数
>现在我们终于查看等号的左侧:Enumerable#partition返回一个包含带有分区元素的两个数组的双元素数组,我们使用Ruby的解构赋值将两个数组分配给两个变量

puts“具有奇数值但仅出现一次的元素数量:#{odds.size}”

现在我们有一个奇怪的单例列表,它们的数量就是列表的大小.

puts "Elements repeated an odd number of times: #{
  h.select {|_,f| f.odd?}.map(&:first).sort.reverse.join(',')
}"

这与上面的非常类似:选择所有具有奇数频率的数字,映射出键(即数字),排序,反向,然后通过将它们连接在一起,并用逗号和空格将它们转换为字符串.

puts "Elements with an even value that appear exactly once: #{evens.join(',')}"

再说一遍,既然我们有一个甚至单身的列表,打印它们只是用逗号连接列表元素的问题.

# print fib numbers in the hash

我不想重构这个算法更有效,特别是memoize.我做了一些小调整.

class Integer

算法中没有任何东西依赖于特定大小的数字,所以我将方法拉到了Integer类中.

def fib?

我删除了is_前缀.这是一个布尔方法的事实在问号中已经明确.

l,l+h
    end
  end
end
puts "Fibonacci numbers: #{h.keys.sort.select(&:fib?).join(',')}"

这可能不需要太多解释:拿起钥匙,对它们进行排序,选择所有斐波那契数字并用逗号连接起来.

以下是如何重构此算法的想法.有一个使用Hash的a very interesting implementation of Fibonacci,默认值为memoizing:

fibs = {0 => 0,1 => 1}.tap do |fibs|
  fibs.default_proc = ->(fibs,n) { fibs[n] = fibs[n-1] + fibs[n-2] }
end

它看起来有点像这样:

class Integer
  @@fibs = {0 => 0,1 => 1}.tap do |fibs|
    fibs.default_proc = ->(fibs,n) { fibs[n] = fibs[n-1] + fibs[n-2] }
  end

  def fib?
    i = 0
    until @@fibs[i += 1] > self
      break true if @@fibs[i] == self
    end
  end
end
puts "Fibonacci numbers: #{h.keys.sort.select(&:fib?).join(',')}"

如果有人能想出一个优雅的方法摆脱i = 0,i = 1和整个直到循环,我会很感激.

(编辑:李大同)

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

    推荐文章
      热点阅读