Решение на Трета задача от Дамян Димитров

Обратно към всички решения

Към профила на Дамян Димитров

Резултати

  • 6 точки от тестове
  • 1 бонус точка
  • 7 точки общо
  • 44 успешни тест(а)
  • 0 неуспешни тест(а)

Код

module RBFS
class File
attr_accessor :data
def initialize(file = nil)
@data = file
end
def data_type
case @data
when Fixnum, Float then :number
when NilClass then :nil
when String then :string
when TrueClass, FalseClass then :boolean
when Symbol then :symbol
end
end
def serialize
"#{data_type}:#{data}"
end
def self.parse(serialized_string)
file_type, file_data = serialized_string.split(':', 2)
case file_type
when 'number' then File.new File.to_num(file_data)
when 'nil' then File.new
when 'string' then File.new file_data
when 'symbol' then File.new file_data.to_sym
when 'boolean' then File.new file_data == 'true'
end
end
private
def self.to_num(string)
if string.include? '.'
string.to_f
else
string.to_i
end
end
end
class Directory
attr_accessor :files, :directories
def initialize
@files = {}
@directories = {}
end
def add_file(name, file)
@files[name] = file
end
def add_directory(name, directory = Directory.new)
@directories[name] = directory
end
def [](name)
@directories[name] || @files[name]
end
def serialize
files_data = serialize_data(@files)
dirs_data = serialize_data(@directories)
"#{@files.size}:#{files_data.join ''}#{@directories.size}:#{dirs_data.join ''}"
end
def self.parse(string)
parsing = Parser.new
parsing.parse_directories(string)
parsing.directory
end
private
def serialize_data(hash)
hash.map { |name, data| "#{name}:#{data.serialize.size}:#{data.serialize}" }
end
end
class Parser
attr_reader :directory
def initialize
@directory = Directory.new
end
def parse_files(string, dir)
number, tail = string.split(':', 2)
1.upto(number.to_i).each do
file_name, file_size, tail = tail.split(':', 3)
file_data = tail[0..(file_size.to_i - 1)]
tail = tail[file_size.to_i..(tail.size - 1)]
dir.add_file file_name, File.parse(file_data)
end
tail ? tail : ''
end
def parse_directories(string, dir = @directory)
tail = parse_files(string, dir)
number, tail = tail.split(':', 2)
1.upto(number.to_i).each do
dir_name, dir_size, tail = tail.split(':', 3)
dir_data, tail = tail[0..dir_size.to_i - 1], tail[dir_size.to_i..tail.size]
dir.add_directory dir_name, (new_dir = Directory.new)
parse_directories(dir_data, new_dir)
end
end
end
end

Лог от изпълнението

............................................

Finished in 0.04269 seconds
44 examples, 0 failures

История (4 версии и 4 коментара)

Дамян обнови решението на 06.11.2014 14:20 (преди над 9 години)

+module RBFS
+ class File
+ attr_accessor :data
+
+ def initialize(file = nil)
+ @data = file
+ end
+
+ def data_type
+ return :number if @data.is_a? Numeric
+ return :nil if @data.class == NilClass
+ return :string if @data.class == String
+ return :boolean if @data.class == TrueClass || @data.class == FalseClass
+ return :symbol if @data.class == Symbol
+ end
+
+ def serialize
+ "#{data_type}:#{data}"
+ end
+
+ def self.parse(serialized_string)
+ file_type = serialized_string.split(':')[0]
+ file_data = serialized_string.split(':')[1]
+ return RBFS::File.new file_data.to_num if file_type == 'number'
+ return RBFS::File.new if file_type == 'nil'
+ return RBFS::File.new file_data if file_type == 'string'
+ return RBFS::File.new file_data.to_sym if file_type == 'symbol'
+ return RBFS::File.new file_data == 'true' if file_type == 'boolean'
+ end
+ end
+
+ class Directory
+ attr_accessor :files, :directories
+
+ def initialize
+ @files = {}
+ @directories = {}
+ end
+
+ def add_file(name, file)
+ @files[name] = file
+ end
+
+ def add_directory(name, directory = RBFS::Directory.new)
+ @directories[name] = directory
+ end
+
+ def[](name)
+ if @directories[name].is_a? Directory
+ @directories[name]
+ else
+ @files[name]
+ end
+ end
+
+ def serialize
+ "#{serialized_files}#{serialized_directories}"
+ end
+
+ def serialized_files
+ result = ["#{@files.size}:"]
+ @files.each do |name, file|
+ result << "#{name}:#{file.serialize.size}:#{file.serialize}"
+ end
+
+ result.join ''
+ end
+
+ def serialized_directories
+ result = ["#{@directories.size}:"]
+ @directories.each do |name, directory|
+ result << "#{name}:#{directory.serialize.size}:#{directory.serialize}"
+ end
+
+ result.join ''
+ end
+ end
+end
+
+class String
+ def to_num
+ if include? '.'
+ to_f
+ else
+ to_i
+ end
+ end
+end

Дамян обнови решението на 07.11.2014 15:21 (преди над 9 години)

module RBFS
class File
attr_accessor :data
def initialize(file = nil)
@data = file
end
def data_type
- return :number if @data.is_a? Numeric
- return :nil if @data.class == NilClass
- return :string if @data.class == String
+ return :number if @data.is_a? Numeric
+ return :nil if @data.class == NilClass
+ return :string if @data.class == String
return :boolean if @data.class == TrueClass || @data.class == FalseClass
- return :symbol if @data.class == Symbol
+ return :symbol if @data.class == Symbol
end
def serialize
"#{data_type}:#{data}"
end
def self.parse(serialized_string)
- file_type = serialized_string.split(':')[0]
- file_data = serialized_string.split(':')[1]
- return RBFS::File.new file_data.to_num if file_type == 'number'
- return RBFS::File.new if file_type == 'nil'
- return RBFS::File.new file_data if file_type == 'string'
- return RBFS::File.new file_data.to_sym if file_type == 'symbol'
+ file_type, file_data = serialized_string.split(':')
+
+ return RBFS::File.new file_data.to_num if file_type == 'number'
+ return RBFS::File.new if file_type == 'nil'
+ return RBFS::File.new file_data if file_type == 'string'
+ return RBFS::File.new file_data.to_sym if file_type == 'symbol'
return RBFS::File.new file_data == 'true' if file_type == 'boolean'
end
end
+ class ParseSerializedString
+ attr_reader :directory
+
+ def initialize
+ @directory = RBFS::Directory.new
+ end
+
+ def parse_files(string, dir)
+ head, number_of_files, tail = string.partition(/\d+/)
+
+ 1.upto(number_of_files.to_i).each do
+ head, file_name, tail = tail.partition(/[^:]+/)
+ head, file_size, tail = tail.partition(/\d+/)
+ head, file_data, tail = tail.partition(/:.{#{file_size}}/)
+
+ file_data = file_data[1..file_data.size]
+
+ dir.add_file file_name, RBFS::File.parse(file_data)
+ end
+
+ tail
+ end
+
+ def parse_directories(string, dir = @directory)
+ tail = parse_files(string, dir)
+
+ head, number_of_dirs, tail = tail.partition(/\d+/)
+
+ 1.upto(number_of_dirs.to_i).each do
+ head, dir_name, tail = tail.partition(/[^:]+/)
+ head, dir_size, tail = tail.partition(/\d+/)
+ head, dir_data, tail = tail.partition(/:.{#{dir_size}}/)
+
+ dir.add_directory dir_name, (new_dir = RBFS::Directory.new)
+ parse_directories(dir_data, new_dir)
+ end
+ end
+end
+
class Directory
attr_accessor :files, :directories
def initialize
@files = {}
@directories = {}
end
def add_file(name, file)
@files[name] = file
end
def add_directory(name, directory = RBFS::Directory.new)
@directories[name] = directory
end
def[](name)
if @directories[name].is_a? Directory
@directories[name]
else
@files[name]
end
end
def serialize
"#{serialized_files}#{serialized_directories}"
end
def serialized_files
result = ["#{@files.size}:"]
- @files.each do |name, file|
- result << "#{name}:#{file.serialize.size}:#{file.serialize}"
+
+ @files.each do |file_name, file|
+ result << "#{file_name}:#{file.serialize.size}:#{file.serialize}"
end
result.join ''
end
def serialized_directories
result = ["#{@directories.size}:"]
- @directories.each do |name, directory|
- result << "#{name}:#{directory.serialize.size}:#{directory.serialize}"
+
+ @directories.each do |dir_name, directory|
+ result << "#{dir_name}:#{directory.serialize.size}:#{directory.serialize}"
end
result.join ''
+ end
+
+ def self.parse(string)
+ parsing = RBFS::ParseSerializedString.new
+ parsing.parse_directories(string)
+ parsing.directory
end
end
end
class String
def to_num
if include? '.'
to_f
else
to_i
end
end
end

Дамян обнови решението на 07.11.2014 15:24 (преди над 9 години)

+class String
+ def to_num
+ if include? '.'
+ to_f
+ else
+ to_i
+ end
+ end
+end
+
module RBFS
class File
attr_accessor :data
def initialize(file = nil)
@data = file
end
def data_type
return :number if @data.is_a? Numeric
return :nil if @data.class == NilClass
return :string if @data.class == String
return :boolean if @data.class == TrueClass || @data.class == FalseClass
return :symbol if @data.class == Symbol
end
def serialize
"#{data_type}:#{data}"
end
def self.parse(serialized_string)
file_type, file_data = serialized_string.split(':')
return RBFS::File.new file_data.to_num if file_type == 'number'
return RBFS::File.new if file_type == 'nil'
return RBFS::File.new file_data if file_type == 'string'
return RBFS::File.new file_data.to_sym if file_type == 'symbol'
return RBFS::File.new file_data == 'true' if file_type == 'boolean'
end
end
- class ParseSerializedString
- attr_reader :directory
-
- def initialize
- @directory = RBFS::Directory.new
- end
-
- def parse_files(string, dir)
- head, number_of_files, tail = string.partition(/\d+/)
-
- 1.upto(number_of_files.to_i).each do
- head, file_name, tail = tail.partition(/[^:]+/)
- head, file_size, tail = tail.partition(/\d+/)
- head, file_data, tail = tail.partition(/:.{#{file_size}}/)
-
- file_data = file_data[1..file_data.size]
-
- dir.add_file file_name, RBFS::File.parse(file_data)
- end
-
- tail
- end
-
- def parse_directories(string, dir = @directory)
- tail = parse_files(string, dir)
-
- head, number_of_dirs, tail = tail.partition(/\d+/)
-
- 1.upto(number_of_dirs.to_i).each do
- head, dir_name, tail = tail.partition(/[^:]+/)
- head, dir_size, tail = tail.partition(/\d+/)
- head, dir_data, tail = tail.partition(/:.{#{dir_size}}/)
-
- dir.add_directory dir_name, (new_dir = RBFS::Directory.new)
- parse_directories(dir_data, new_dir)
- end
- end
-end
-
class Directory
attr_accessor :files, :directories
def initialize
@files = {}
@directories = {}
end
def add_file(name, file)
@files[name] = file
end
def add_directory(name, directory = RBFS::Directory.new)
@directories[name] = directory
end
def[](name)
if @directories[name].is_a? Directory
@directories[name]
else
@files[name]
end
end
def serialize
"#{serialized_files}#{serialized_directories}"
end
def serialized_files
result = ["#{@files.size}:"]
@files.each do |file_name, file|
result << "#{file_name}:#{file.serialize.size}:#{file.serialize}"
end
result.join ''
end
def serialized_directories
result = ["#{@directories.size}:"]
@directories.each do |dir_name, directory|
result << "#{dir_name}:#{directory.serialize.size}:#{directory.serialize}"
end
result.join ''
end
def self.parse(string)
parsing = RBFS::ParseSerializedString.new
parsing.parse_directories(string)
parsing.directory
end
end
-end
-class String
- def to_num
- if include? '.'
- to_f
- else
- to_i
+ class ParseSerializedString
+ attr_reader :directory
+
+ def initialize
+ @directory = RBFS::Directory.new
+ end
+
+ def parse_files(string, dir)
+ head, number_of_files, tail = string.partition(/\d+/)
+
+ 1.upto(number_of_files.to_i).each do
+ head, file_name, tail = tail.partition(/[^:]+/)
+ head, file_size, tail = tail.partition(/\d+/)
+ head, file_data, tail = tail.partition(/:.{#{file_size}}/)
+
+ file_data = file_data[1..file_data.size]
+
+ dir.add_file file_name, RBFS::File.parse(file_data)
+ end
+
+ tail
+ end
+
+ def parse_directories(string, dir = @directory)
+ tail = parse_files(string, dir)
+
+ head, number_of_dirs, tail = tail.partition(/\d+/)
+
+ 1.upto(number_of_dirs.to_i).each do
+ head, dir_name, tail = tail.partition(/[^:]+/)
+ head, dir_size, tail = tail.partition(/\d+/)
+ head, dir_data, tail = tail.partition(/:.{#{dir_size}}/)
+
+ dir.add_directory dir_name, (new_dir = RBFS::Directory.new)
+ parse_directories(dir_data, new_dir)
+ end
end
end
end

Не можах да измисля друг начин за реализиране на RBFS.Directory.parse. Използвам класа RBFS::ParseSerializedString, които може би е малко неясен на пръв поглед. Ще се опитам по-късно да добавя кратък коментар каква ми е логиката на алгоритъма.

Здравей :)

Ето малко коментари:

  • Не съм сигурен, че monkey-patch-a на String е добра идея. Сигурно ще има спорни мнения по въпроса, но аз по-скоро бих го сложил като private метод на File.
  • Когато си в модула RBFS не е нужно да пишеш RBFS::File или RBFS::Directory. И без това RBFS:: ще ги намери.
  • За data_type и File.parse ще е много по-добре да се използва case. Освен прегледност, ще се оттървеш и от проблема на сравнението на class, а именно, че ако някой ти подаде наследник на някой от тези класове - няма да го разпознаеш.
  • Помисли какво ще се случи, ако имаш : в съдържанието на файл.
  • def[](name) тук трябва да има един спейс :)
  • if @directories[name].is_a? Directory може ли да не е директория? Проверката е подвеждаща, защото на пръв поглед човек би си помислил, че може да има и други неща вътре. А то реално проверява дали не е nil. Методът #[] може да се напише и на един ред като използваш, че hash[key] връща nil, ако го няма този ключ + определена логическа операция.
  • Това е симулация на map :)

    @files.each do |file_name, file|
      result << "#{file_name}:#{file.serialize.size}:#{file.serialize}"
    end
    

    Направи си го с map и залепи после бройката чрез интерполация (на последния ред).

  • serialized_files и serialized_directories не ти ли се струват еднакви? Разликата е минимална, защо не ги обединиш в един метод, който да работи и за двете? Освен това, направи ги private, защото се използват само вътре в класа.
  • parse_files и parse_directories също са почти еднакви. За parse_directories е хубаво да използваш рекурсивно Directory.parse, вместо parse_directories - става по-ясно какво се случва.
  • Регулярните изрази усложняват нещата. Чувал ли си това за регулярните изрази и двата проблема? :) Използвай split за да вземеш броя, името и дължината и просто String#[], за да отрежеш данните за конкретния файл или директория. Ако мислиш, че няма да стане - разгледай втория параметър на split.
  • Как ти се струва името на класа да е нещо като Parser? ParseSerializedString ми звучи като име на метод, не на клас.

Дамян обнови решението на 08.11.2014 10:58 (преди над 9 години)

-class String
- def to_num
- if include? '.'
- to_f
- else
- to_i
- end
- end
-end
-
module RBFS
class File
attr_accessor :data
def initialize(file = nil)
@data = file
end
def data_type
- return :number if @data.is_a? Numeric
- return :nil if @data.class == NilClass
- return :string if @data.class == String
- return :boolean if @data.class == TrueClass || @data.class == FalseClass
- return :symbol if @data.class == Symbol
+ case @data
+ when Fixnum, Float then :number
+ when NilClass then :nil
+ when String then :string
+ when TrueClass, FalseClass then :boolean
+ when Symbol then :symbol
+ end
end
def serialize
"#{data_type}:#{data}"
end
def self.parse(serialized_string)
- file_type, file_data = serialized_string.split(':')
+ file_type, file_data = serialized_string.split(':', 2)
- return RBFS::File.new file_data.to_num if file_type == 'number'
- return RBFS::File.new if file_type == 'nil'
- return RBFS::File.new file_data if file_type == 'string'
- return RBFS::File.new file_data.to_sym if file_type == 'symbol'
- return RBFS::File.new file_data == 'true' if file_type == 'boolean'
+ case file_type
+ when 'number' then File.new File.to_num(file_data)
+ when 'nil' then File.new
+ when 'string' then File.new file_data
+ when 'symbol' then File.new file_data.to_sym
+ when 'boolean' then File.new file_data == 'true'
+ end
end
+
+ private
+
+ def self.to_num(string)
+ if string.include? '.'
+ string.to_f
+ else
+ string.to_i
+ end
+ end
end
class Directory
attr_accessor :files, :directories
def initialize
@files = {}
@directories = {}
end
def add_file(name, file)
@files[name] = file
end
- def add_directory(name, directory = RBFS::Directory.new)
+ def add_directory(name, directory = Directory.new)
@directories[name] = directory
end
- def[](name)
- if @directories[name].is_a? Directory
- @directories[name]
- else
- @files[name]
- end
+ def [](name)
+ @directories[name] || @files[name]
end
def serialize
- "#{serialized_files}#{serialized_directories}"
+ files_data = serialize_data(@files)
+ dirs_data = serialize_data(@directories)
+ "#{@files.size}:#{files_data.join ''}#{@directories.size}:#{dirs_data.join ''}"
end
- def serialized_files
- result = ["#{@files.size}:"]
-
- @files.each do |file_name, file|
- result << "#{file_name}:#{file.serialize.size}:#{file.serialize}"
- end
-
- result.join ''
- end
-
- def serialized_directories
- result = ["#{@directories.size}:"]
-
- @directories.each do |dir_name, directory|
- result << "#{dir_name}:#{directory.serialize.size}:#{directory.serialize}"
- end
-
- result.join ''
- end
-
def self.parse(string)
- parsing = RBFS::ParseSerializedString.new
+ parsing = Parser.new
parsing.parse_directories(string)
parsing.directory
end
+
+ private
+
+ def serialize_data(hash)
+ hash.map { |name, data| "#{name}:#{data.serialize.size}:#{data.serialize}" }
+ end
end
- class ParseSerializedString
+ class Parser
attr_reader :directory
def initialize
- @directory = RBFS::Directory.new
+ @directory = Directory.new
end
def parse_files(string, dir)
- head, number_of_files, tail = string.partition(/\d+/)
+ number, tail = string.split(':', 2)
- 1.upto(number_of_files.to_i).each do
- head, file_name, tail = tail.partition(/[^:]+/)
- head, file_size, tail = tail.partition(/\d+/)
- head, file_data, tail = tail.partition(/:.{#{file_size}}/)
+ 1.upto(number.to_i).each do
+ file_name, file_size, tail = tail.split(':', 3)
+ file_data = tail[0..(file_size.to_i - 1)]
+ tail = tail[file_size.to_i..(tail.size - 1)]
- file_data = file_data[1..file_data.size]
-
- dir.add_file file_name, RBFS::File.parse(file_data)
+ dir.add_file file_name, File.parse(file_data)
end
- tail
+ tail ? tail : ''
end
def parse_directories(string, dir = @directory)
tail = parse_files(string, dir)
- head, number_of_dirs, tail = tail.partition(/\d+/)
+ number, tail = tail.split(':', 2)
- 1.upto(number_of_dirs.to_i).each do
- head, dir_name, tail = tail.partition(/[^:]+/)
- head, dir_size, tail = tail.partition(/\d+/)
- head, dir_data, tail = tail.partition(/:.{#{dir_size}}/)
+ 1.upto(number.to_i).each do
+ dir_name, dir_size, tail = tail.split(':', 3)
+ dir_data, tail = tail[0..dir_size.to_i - 1], tail[dir_size.to_i..tail.size]
- dir.add_directory dir_name, (new_dir = RBFS::Directory.new)
+ dir.add_directory dir_name, (new_dir = Directory.new)
+
parse_directories(dir_data, new_dir)
end
end
end
end

Благодаря за коментара. Направих доста подобрения :) Но има още какво да се желае.

Все още не съм измислил как да обединя методите parse_files и parse_directories, но както се казва работя по въпроса :)