公司内部编程游戏,我给了一个 ruby 一行版,但是自己也觉得看起来很 sb,求不土鳖的做法
Dir['*'].each{|dir| print "#{dir}: "; Dir.chdir(dir){ puts Dir['*.txt'].map{|x| File.read(x) =~ /max:(.+) min:(.+)/ ;; [$1.to_i - $2.to_i, x]}.max.to_s}}
多行版是这样的
Dir['*'].each do |dir|
print "#{dir}: "
Dir.chdir(dir) do
puts Dir['*.txt']
.map{ |x|
File.read(x) =~ /max:(.+) min:(.+)/
[$1.to_i - $2.to_i, x]
}.max.to_s
end
end
统计出全国所有城市在 1996 年 01 月 01 日 -2012 年 9 月 31 日。平均昼夜温差 (最高温度减最低温度) 最大的一个月
一个目录给出了各城市的每日昼夜温差,形如这样:
HANGZHOU
|
|---1996-01-01.txt
|.........
----2012-09-31.txt
QIANNAN
SIPING
WUXI
YINGKOU
1996-01-01.txt 这样的文件内容只有一行
max:-1 min:-10
Dir['**/*.txt'].map{|file| File.read(file) =~ /max:(.+) min:(.+)/;[$1.to_i - $2.to_i, file]}.max_by{|x| x[0]}.to_s
#14 楼 @fsword 不能这么说。。和 sort&sort_by 一样,影响因素比较多。像你这种简单的比较还是 max 快..
max: https://gist.github.com/3916880 sort: https://gist.github.com/3916850
一行版本
p Dir['**/*.txt'].map { |file| File.read(file) =~ /max:(.+) min:(.+)/; [file.split('-')[0..-2].join('-'), $1.to_i - $2.to_i] }.group_by {|x| x[0]}.map { |k, v| [k, v.map(&:last).inject(:+).to_f/v.size] }.group_by {|x| x[0].split('/')[0]}.map {|k, v| [k, v.max_by(&:last).last] }
多行版本
p Dir['**/*.txt'].map { |file|
File.read(file) =~ /max:(.+) min:(.+)/
[file.split('-')[0..-2].join('-'), $1.to_i - $2.to_i]
}.group_by {|x| x[0]}.map { |k, v|
[k, v.map(&:last).inject(:+).to_f/v.size]
}.group_by {|x| x[0].split('/')[0]}.map {|k, v|
[k, v.max_by(&:last).last]
}
修正版:
Dir['*'].each do |dir|
puts "%15s: %.4f %s" % [dir, Dir["#{dir}/*.txt"]
.map{|x| File.read(x) =~ /max:(.+) min:(.+)/ ; [$1.to_i - $2.to_i, x.split('/')[1]] }
.group_by{|a| a[1][0..6]}
.map{|k,v| [v.map{|a| a[0]}.reduce(:+).to_f/v.size, k]}
.max].flatten
end
OO 版,未测试
class City
attr_reader :name
def initialize(dir)
@files = Dir["#{dir}/*.txt"]
@name = dir.chomp('//')
@temperatures = files.map do |file|
[max, min] = Temperature.new(file)
end
end
def maximum_deference
@temperatures.max_by { |m,n| m-n}
end
end
class Temperature
def initialize(file)
parser.parse(File.read(file))
end
def parser
@parser ||= new DateParser
end
end
class DateParser
def parse(data)
if m = /max:(.+) min:(.+)/.match(data)
[m[1].to_i, m[2].to_i]
else
[0,0]
end
end
end
# main
cities = Dir["**/"].map do |dir|
City.new(dir)
end
cities.echo do |city|
puts "#{city.name} maximum temperature defference #{city.maximum_difference)}"
end
简单文本解析用 scanf 最方便了
require 'scanf'
'max:-1 min:-10'.scanf 'max:%d min:%d' #=> [-1, -10]
OO 是工具不是目的,不要为 OO 而 OO。另外写个 class 也不是 OO,只是写了个 class 而已...
Dir.glob("*/**/*.txt").group_by{|f| f =~ /(.+)\//;$1}.map{|k,v| { k => v.max_by{open(k).read =~ /max:(.+) min:(.+)/;-[$1.to_i - $2.to_i].abs}}}
#22 楼 @luikore 完全赞同啊。。 不过你这么写又成了“Ruby 黑魔多”的证据,然后你就成了外国人, 最后把你挂到Rubydramas上面..
#22 楼 @luikore 恩,这个不错,多一行 require,不过可以简化最长的那句
require 'scanf'
Dir['*'].each do |dir|
puts "%15s: %.4f %s" % [dir, Dir["#{dir}/*.txt"]
.map{|x| [File.read(x).scanf('max:%d min:%d/').reduce(:-), x] }
.group_by{|a| a[1][-14..-8]}
.map{|k,v| [v.map{|a| a[0]}.reduce(:+).to_f/v.size, k]}
.max].flatten
end
性能暂且不论,可读性有提高,可惜还是做不到 one-liner
我也来个 OO 版,甚至有点为了过分追求 OO 以及软件的重构而 OO.
不过代码还算清晰,基本上都是不可再分的小方法,也没有太多的参数传递,在几个月后再看代码,比单行脚本易懂一些。
(好吧,我承认,使用单行的方式来解决这么复杂的应用,我还真写不出来呢)
class City
def initialize(city_path)
@city_path = city_path
end
def average_arrays
month_days_hash.map {|month, days| [name, month, Parser.parse_temperature(days)/days.length] }
end
def month_days_hash
date_files.group_by do |date_file|
File.basename(date_file, ".txt").slice(/(\d{4}-\d{2})-\d{2}/, 1)
end
end
def date_files
Dir["#{@city_path}????-??-??.txt"]
end
def name
File.basename(@city_path)
end
class Parser
def self.parse_temperature(arrays)
arrays.inject(0) do |sum, file|
string = File.read(file)
sum += parse(string)
end
end
def self.parse(string)
string =~ /max:(.+) min:(.+)/
($1.to_f - $2.to_f).abs
end
end
end
city_directorys = Dir['city/*/']
report = city_directorys.map do |city_directory|
city = City.new(city_directory)
# 一个二维数组, 每个元素是 ["城市名", "年-月", 月平均温差(浮点数)] 形式
city.average_arrays.max_by {|e| e[2] }
end
report.each {|city| printf "%-10s在 %5s 拥有最高温差: %0.1f", *city }
下面是补上的测试,使用 Minitest.
require 'minitest/autorun'
require 'awesome_print'
require 'minitest/pride'
require_relative "test3"
describe City do
before do
@city = City.new("city/hangzhou/")
end
it "must return city name" do
@city.name.must_equal "hangzhou"
end
it "must return a month_days_hash" do
def @city.date_files
["city/hangzhou/1996-01-03.txt",
"city/hangzhou/1996-01-02.txt",
"city/hangzhou/1996-02-02.txt",
"city/hangzhou/1996-02-03.txt",
"city/hangzhou/1999-12-25.txt",
"city/hangzhou/1996-01-01.txt"]
end
@city.month_days_hash.must_equal({"1996-01"=>["city/hangzhou/1996-01-03.txt", "city/hangzhou/1996-01-02.txt", "city/hangzhou/1996-01-01.txt"], "1996-02"=>["city/hangzhou/1996-02-02.txt", "city/hangzhou/1996-02-03.txt"], "1999-12"=>["city/hangzhou/1999-12-25.txt"]})
end
specify { City::Parser.parse("max:20 min: -5").must_equal 25 }
end