Решение на Трета задача от Станислав Венков

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

Към профила на Станислав Венков

Резултати

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

Код

module RBFS
class File
attr_accessor :data
def initialize(file = nil)
@data = file
end
def data_type
case @data
when String then :string
when Symbol then :symbol
when Numeric then :number
when TrueClass || FalseClass then :boolean
else :nil
end
end
def serialize
@serialized_file = "#{self.data_type.to_s}:#{@data.to_s}" if !@serialized_file
@serialized_file
end
def File.parse(data_string)
object_info = data_string.partition(":")
case object_info[0]
when 'string' then File.new(object_info[2])
when 'symbol' then File.new(object_info[2].to_sym)
when 'number' then File.new(object_info[2].to_enum)
when 'boolean' then File.new(object_info[2] == 'true')
else File.new(nil)
end
end
def size
self.serialize.size.to_s
end
end
class Directory
attr_reader :directories
attr_reader :files
def add_file(name, file)
@files[name] = file
end
def initialize
@directories = {}
@files = {}
end
def add_directory(name, directory = nil)
@directories[name] = directory || RBFS::Directory.new
end
def [](name)
if @directories[name]
@directories[name]
else
@files[name]
end
end
def serialize
string = "#{@files.size.to_s}:"
@files.each do |name, file|
string = string << name << ':' << file.size << ':' << file.serialize
end
string = string << @directories.size.to_s << ':'
@directories.each do |name, dir|
string << name << ':' << dir.serialize.size.to_s << ':' << dir.serialize
end
string
end
def Directory.parse(serialized_string)
data_array = serialized_string.split(':')
parent_directory = RBFS::Directory.new
last_file_data = RBFS::Parser::parse_files_string(data_array, parent_directory)
if last_file_data
directories_array = serialized_string.partition(last_file_data)[2].split(':')
RBFS::Parser::parse_directories_string(directories_array, parent_directory)
end
parent_directory
end
end
class Parser
def Parser.parse_files_string(data_a, directory_obj)
files_number, i, file = data_a[0].to_i, 3
files_number.times do i-=1
i1, i2, i3, i4, i5 = i+=1, i+=1, i-=2, i-=1, i+=3
file_data = (data_a[i1] << ':' << data_a[i2]).slice!(0..(data_a[i3].to_i-1))
directory_obj.add_file(data_a[i4], file = RBFS::File::parse(file_data))
data_a[i5], i = data_a[i5].gsub(file.data, ''), i+2
end
file ? file.data : nil
end
def Parser.parse_directories_string(data_a, directory_obj)
directories_number, i = data_a[0].to_i, 0
directories_number.times do
i1, i2, i3 = i+=1, i+=1, i+=1
directory_obj.add_directory(data_a[i1], directory_content(data_a, i2)[0])
i = directory_content(data_a, i3)[1]
end
end
def Parser.directory_content(data_a, position)
directory_string, step = "", position
while directory_string.size <= data_a[position].to_i
step+=1
directory_string << data_a[step].to_s << ":"
end
[RBFS::Directory::parse(directory_string), step]
end
end
end

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

............FF....................F..F...FFF

Failures:

  1) RBFS Directory serialization ::parse can parse directory trees without files
     Failure/Error: expect(parsed_directory['dir1']        ).to be_an RBFS::Directory
       expected nil to be a kind of RBFS::Directory
     # /tmp/d20141111-26053-xqwx53/spec.rb:119:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  2) RBFS Directory serialization ::parse can parse directories recursively
     Failure/Error: parsed_directory = RBFS::Directory.parse(recursive_serialized_string)
     TypeError:
       wrong argument type Symbol (expected Regexp)
     # /tmp/d20141111-26053-xqwx53/solution.rb:101:in `gsub'
     # /tmp/d20141111-26053-xqwx53/solution.rb:101:in `block in parse_files_string'
     # /tmp/d20141111-26053-xqwx53/solution.rb:97:in `times'
     # /tmp/d20141111-26053-xqwx53/solution.rb:97:in `parse_files_string'
     # /tmp/d20141111-26053-xqwx53/solution.rb:84:in `parse'
     # /tmp/d20141111-26053-xqwx53/solution.rb:121:in `directory_content'
     # /tmp/d20141111-26053-xqwx53/solution.rb:110:in `block in parse_directories_string'
     # /tmp/d20141111-26053-xqwx53/solution.rb:108:in `times'
     # /tmp/d20141111-26053-xqwx53/solution.rb:108:in `parse_directories_string'
     # /tmp/d20141111-26053-xqwx53/solution.rb:87:in `parse'
     # /tmp/d20141111-26053-xqwx53/spec.rb:125:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  3) RBFS File data type number can be parsed
     Failure/Error: expect(file.data     ).to eq 1234
       
       expected: 1234
            got: #<Enumerator: "1234":each>
       
       (compared using ==)
     # /tmp/d20141111-26053-xqwx53/spec.rb:295:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  4) RBFS File data type float number can be parsed
     Failure/Error: expect(file.data     ).to eq 3.14
       
       expected: 3.14
            got: #<Enumerator: "3.14":each>
       
       (compared using ==)
     # /tmp/d20141111-26053-xqwx53/spec.rb:314:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  5) RBFS File data type boolean false can be detected
     Failure/Error: expect(file.data_type).to eq :boolean
       
       expected: :boolean
            got: :nil
       
       (compared using ==)
       
       Diff:
       @@ -1,2 +1,2 @@
       -:boolean
       +:nil
     # /tmp/d20141111-26053-xqwx53/spec.rb:342:in `block (6 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  6) RBFS File data type boolean false can be serialized
     Failure/Error: expect(file.serialize).to eq 'boolean:false'
       
       expected: "boolean:false"
            got: "nil:false"
       
       (compared using ==)
     # /tmp/d20141111-26053-xqwx53/spec.rb:347:in `block (6 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  7) RBFS File data type boolean false can be parsed
     Failure/Error: expect(file.data_type).to eq :boolean
       
       expected: :boolean
            got: :nil
       
       (compared using ==)
       
       Diff:
       @@ -1,2 +1,2 @@
       -:boolean
       +:nil
     # /tmp/d20141111-26053-xqwx53/spec.rb:354:in `block (6 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.04345 seconds
44 examples, 7 failures

Failed examples:

rspec /tmp/d20141111-26053-xqwx53/spec.rb:116 # RBFS Directory serialization ::parse can parse directory trees without files
rspec /tmp/d20141111-26053-xqwx53/spec.rb:124 # RBFS Directory serialization ::parse can parse directories recursively
rspec /tmp/d20141111-26053-xqwx53/spec.rb:292 # RBFS File data type number can be parsed
rspec /tmp/d20141111-26053-xqwx53/spec.rb:311 # RBFS File data type float number can be parsed
rspec /tmp/d20141111-26053-xqwx53/spec.rb:340 # RBFS File data type boolean false can be detected
rspec /tmp/d20141111-26053-xqwx53/spec.rb:345 # RBFS File data type boolean false can be serialized
rspec /tmp/d20141111-26053-xqwx53/spec.rb:350 # RBFS File data type boolean false can be parsed

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

Станислав обнови решението на 02.11.2014 23:06 (преди около 10 години)

+
+module RBFS
+
+ class File
+
+ attr_accessor :data
+
+ def initialize(*data)
+ @data = data[0] ? data[0] : nil
+ end
+
+ def data_type
+ if @data.is_a? String then return :string end
+ if @data.is_a? Symbol then return :symbol end
+ if @data.is_a? Numeric then return :number end
+ if @data === true || @data === false then return :boolean end
+ if @data == nil then return :nil end
+ end
+
+ def serialize
+ self.data_type.to_s + ":" + @data.to_s
+ end
+
+ def File.parse(data_string)
+ object_info = data_string.partition(":")
+ case object_info[0] #=> data type
+ when 'string' then File.new(String.new(object_info[2])) #=> data as object
+ when 'symbol' then File.new(Symbol.new(object_info[2])) #=> data as object
+ when 'number' then File.new(Number.new(object_info[2])) #=> data as object
+ when 'boolean' then File.new(object_info[2] == 'true') #=> data as object
+ else File.new(nil)
+ end
+ end
+
+ def size
+ self.serialize.size.to_s
+ end
+
+ end
+
+ class Directory
+
+ def add_file(name, file)
+ name.gsub!(':', '')
+ @files[name] = file
+ end
+
+ def initialize
+ @directories = {}
+ @files = {}
+ end
+
+ def add_directory(name, *directory)
+ name.gsub!(':', '')
+ @directories[name] = directory[0] || RBFS::Directory.new
+ end
+
+ def [](name)
+ if @directories[name]
+ @directories[name]
+ else
+ @files[name]
+ end
+ end
+
+ def files
+ @files
+ end
+
+ def directories
+ @directories
+ end
+
+ def serialize
+ string = @files.size.to_s + ':'
+ @files.each {|name, file|
+ string = string << name << ':' << file.size << ':' << file.serialize
+ }
+ string = string << @directories.size.to_s << ':'
+ @directories.each {|name, dir|
+ string << name << ':' << dir.serialize.size.to_s << ':' << dir.serialize
+ }
+ string
+ end
+
+ def Directory.parse(serialized_string)
+ data_array = serialized_string.split(':')
+ parent_directory = RBFS::Directory.new
+ last_file_data = RBFS::Additional::parse_files_string(data_array, parent_directory)
+ if last_file_data
+ directories_array = serialized_string.partition(last_file_data)[2].split(':')
+ RBFS::Additional::parse_directories_string(directories_array, parent_directory)
+ end
+ parent_directory
+ end
+
+ end
+
+ class Additional
+ def Additional.parse_files_string(data_a, directory_obj)
+ files_number, i, file = data_a[0].to_i, 3
+ files_number.times { i-=1
+ i1, i2, i3, i4, i5 = i+=1, i+=1, i-=2, i-=1, i+=3
+ file_data = (data_a[i1] << ':' << data_a[i2]).slice!(0..(data_a[i3].to_i-1))
+ directory_obj.add_file(data_a[i4], file = RBFS::File::parse(file_data))
+ data_a[i5], i = data_a[i5].gsub(file.data, ''), i+2
+ }
+ file ? file.data : nil
+ end
+
+ def Additional.parse_directories_string(data_a, directory_obj)
+ directories_number, i = data_a[0].to_i, 0
+ directories_number.times {
+ i1, i2, i3 = i+=1, i+=1, i+=1
+ directory_obj.add_directory(data_a[i1], directory_content(data_a, i2)[0])
+ i = directory_content(data_a, i3)[1]
+ }
+ end
+
+ def Additional.directory_content(data_a, position)
+ directory_string, step = "", position
+ while directory_string.size <= data_a[position].to_i
+ step+=1
+ directory_string << data_a[step].to_s << ":"
+ end
+ [RBFS::Directory::parse(directory_string), step]
+ end
+ end
+
+end

Станислав обнови решението на 02.11.2014 23:08 (преди около 10 години)

-
module RBFS
class File
attr_accessor :data
def initialize(*data)
@data = data[0] ? data[0] : nil
end
def data_type
if @data.is_a? String then return :string end
if @data.is_a? Symbol then return :symbol end
if @data.is_a? Numeric then return :number end
if @data === true || @data === false then return :boolean end
if @data == nil then return :nil end
end
def serialize
self.data_type.to_s + ":" + @data.to_s
end
def File.parse(data_string)
- object_info = data_string.partition(":")
- case object_info[0] #=> data type
+ object_info = data_string.partition(":")
+ case object_info[0] #=> data type
when 'string' then File.new(String.new(object_info[2])) #=> data as object
when 'symbol' then File.new(Symbol.new(object_info[2])) #=> data as object
when 'number' then File.new(Number.new(object_info[2])) #=> data as object
when 'boolean' then File.new(object_info[2] == 'true') #=> data as object
else File.new(nil)
end
end
def size
self.serialize.size.to_s
end
end
class Directory
def add_file(name, file)
- name.gsub!(':', '')
- @files[name] = file
+ name.gsub!(':', '')
+ @files[name] = file
end
def initialize
@directories = {}
@files = {}
end
def add_directory(name, *directory)
- name.gsub!(':', '')
- @directories[name] = directory[0] || RBFS::Directory.new
+ name.gsub!(':', '')
+ @directories[name] = directory[0] || RBFS::Directory.new
end
def [](name)
- if @directories[name]
- @directories[name]
- else
+ if @directories[name]
+ @directories[name]
+ else
@files[name]
- end
+ end
end
def files
- @files
+ @files
end
def directories
@directories
end
def serialize
string = @files.size.to_s + ':'
@files.each {|name, file|
string = string << name << ':' << file.size << ':' << file.serialize
}
string = string << @directories.size.to_s << ':'
@directories.each {|name, dir|
string << name << ':' << dir.serialize.size.to_s << ':' << dir.serialize
}
string
end
def Directory.parse(serialized_string)
data_array = serialized_string.split(':')
parent_directory = RBFS::Directory.new
last_file_data = RBFS::Additional::parse_files_string(data_array, parent_directory)
if last_file_data
directories_array = serialized_string.partition(last_file_data)[2].split(':')
RBFS::Additional::parse_directories_string(directories_array, parent_directory)
end
parent_directory
end
end
class Additional
def Additional.parse_files_string(data_a, directory_obj)
files_number, i, file = data_a[0].to_i, 3
files_number.times { i-=1
i1, i2, i3, i4, i5 = i+=1, i+=1, i-=2, i-=1, i+=3
file_data = (data_a[i1] << ':' << data_a[i2]).slice!(0..(data_a[i3].to_i-1))
directory_obj.add_file(data_a[i4], file = RBFS::File::parse(file_data))
data_a[i5], i = data_a[i5].gsub(file.data, ''), i+2
}
file ? file.data : nil
end
def Additional.parse_directories_string(data_a, directory_obj)
directories_number, i = data_a[0].to_i, 0
directories_number.times {
i1, i2, i3 = i+=1, i+=1, i+=1
directory_obj.add_directory(data_a[i1], directory_content(data_a, i2)[0])
i = directory_content(data_a, i3)[1]
}
end
def Additional.directory_content(data_a, position)
directory_string, step = "", position
while directory_string.size <= data_a[position].to_i
step+=1
directory_string << data_a[step].to_s << ":"
end
[RBFS::Directory::parse(directory_string), step]
end
end
end

Здравей :)

Имам няколко забележки и препоръки:

  • Защо приемаш splat аргумент във File#initialize и Directory#add_directory? Не знам дали си забелязал този слайд, но в Ruby аргументите могат да имат стойности по подразбиране. :)
  • В data_type ще е по-прегледно, ако използваш case. Така няма да е нужно и да употребяваш is_a?. Също така, return-ите ще са напълно излишни.
  • Използвай интерполация вместо конкатенация - string1 + string2 => "#{string1}#{string2}".
  • Има по-подходящ метод, който може да използваш на мястото на partition. Разгледай документацията на String. Освен това, помисли какво ще се случи, ако имаш : в съдържанието на файл.
  • String.new, Symbol.new и Number.new са кофти начини да преобразуваш нещо в съответния тип. Има подходящи методи в String за тази цел. Плюс това, няма нужда да превръщаш String в String. :)
  • Използвайки метода File#size ти се налага да сериализираш един и същ файл по няколко пъти. По-добре е просто да си вземеш сериализирания низ в променлива и директно да му викнеш size.
  • Не е хубаво да премахваш : от имената на файлове и директории. Обещахме, че няма да тестваме с невалидни аргументи. Ако все пак се налагаше да работите с този случай - това не би бил правилният вариант да се справим с него, защото правим нещо, което използващият класа няма да очаква.
  • Вместо методите files и directories сложи по един attr_reader.
  • Directory#serialize ми прилича на C код. Като за начало използвай do ... end за многоредови блокове, както е написано в style guide-а. Опитай се да напишеш метода без постоянните конкатенации на низове. Подсказвам - map, join и интерполация.

Не ми достига IQ-то, за да разшифровам всичките parse методи за време по-малко от един час. :) Като за начало - това, че ти се е наложило да създадеш един абсолютно безцелен клас, само за да заобиколиш ограничението, би трябвало да ти подскаже, че нещо не е както трябва. :)

Ето някои насоки:

  • Не мисли с индекси. Не се налага да се използва нито един индекс.
  • Прави операциите една по една. Например, за да парснеш един файл (в директория) имаш следните стъпки:
    • Прочиташ името и дължината му в променливи, като ги махаш от низа.
    • Прочиташ текст, колкото си прочел в горната стъпка
    • Даваш го на File#parse и си го добавяш
  • Парсването на файл и поддиректория (от директория) са аналогични. Тоест може да имаш един метод, който да върши и двете, като се извика два пъти.
  • Може да си направиш отделен клас, който да се занимава само с парсване на неща. Ще е удобно, ако той има вътрешно състояние - низа, който парсваш.

Ако имаш въпроси по нещата, които написах - питай. :) Имаш почти цяла седмица още, за да подобриш решението си. :)

Станислав обнови решението на 09.11.2014 15:24 (преди около 10 години)

module RBFS
class File
attr_accessor :data
- def initialize(*data)
- @data = data[0] ? data[0] : nil
+ def initialize(file = nil)
+ @data = file
end
def data_type
- if @data.is_a? String then return :string end
- if @data.is_a? Symbol then return :symbol end
- if @data.is_a? Numeric then return :number end
- if @data === true || @data === false then return :boolean end
- if @data == nil then return :nil end
+ case @data
+ when String then :string
+ when Symbol then :symbol
+ when Numeric then :number
+ when TrueClass || FalseClass then :boolean
+ else :nil
+ end
end
def serialize
- self.data_type.to_s + ":" + @data.to_s
+ @serialized_file = "#{self.data_type.to_s}:#{@data.to_s}" if !@serialized_file
+ @serialized_file
end
def File.parse(data_string)
object_info = data_string.partition(":")
- case object_info[0] #=> data type
- when 'string' then File.new(String.new(object_info[2])) #=> data as object
- when 'symbol' then File.new(Symbol.new(object_info[2])) #=> data as object
- when 'number' then File.new(Number.new(object_info[2])) #=> data as object
- when 'boolean' then File.new(object_info[2] == 'true') #=> data as object
+ case object_info[0]
+ when 'string' then File.new(object_info[2])
+ when 'symbol' then File.new(object_info[2].to_sym)
+ when 'number' then File.new(object_info[2].to_enum)
+ when 'boolean' then File.new(object_info[2] == 'true')
else File.new(nil)
end
end
def size
self.serialize.size.to_s
end
end
class Directory
+ attr_reader :directories
+ attr_reader :files
+
def add_file(name, file)
- name.gsub!(':', '')
@files[name] = file
end
def initialize
@directories = {}
@files = {}
end
- def add_directory(name, *directory)
- name.gsub!(':', '')
- @directories[name] = directory[0] || RBFS::Directory.new
+ def add_directory(name, directory = nil)
+ @directories[name] = directory || RBFS::Directory.new
end
def [](name)
if @directories[name]
@directories[name]
else
@files[name]
end
end
- def files
- @files
- end
-
- def directories
- @directories
- end
-
def serialize
- string = @files.size.to_s + ':'
- @files.each {|name, file|
+ string = "#{@files.size.to_s}:"
+ @files.each do |name, file|
string = string << name << ':' << file.size << ':' << file.serialize
- }
+ end
string = string << @directories.size.to_s << ':'
- @directories.each {|name, dir|
+ @directories.each do |name, dir|
string << name << ':' << dir.serialize.size.to_s << ':' << dir.serialize
- }
+ end
string
end
def Directory.parse(serialized_string)
data_array = serialized_string.split(':')
parent_directory = RBFS::Directory.new
- last_file_data = RBFS::Additional::parse_files_string(data_array, parent_directory)
+ last_file_data = RBFS::Parser::parse_files_string(data_array, parent_directory)
if last_file_data
directories_array = serialized_string.partition(last_file_data)[2].split(':')
- RBFS::Additional::parse_directories_string(directories_array, parent_directory)
+ RBFS::Parser::parse_directories_string(directories_array, parent_directory)
end
parent_directory
end
end
- class Additional
- def Additional.parse_files_string(data_a, directory_obj)
+ class Parser
+ def Parser.parse_files_string(data_a, directory_obj)
files_number, i, file = data_a[0].to_i, 3
- files_number.times { i-=1
+ files_number.times do i-=1
i1, i2, i3, i4, i5 = i+=1, i+=1, i-=2, i-=1, i+=3
file_data = (data_a[i1] << ':' << data_a[i2]).slice!(0..(data_a[i3].to_i-1))
directory_obj.add_file(data_a[i4], file = RBFS::File::parse(file_data))
data_a[i5], i = data_a[i5].gsub(file.data, ''), i+2
- }
+ end
file ? file.data : nil
end
- def Additional.parse_directories_string(data_a, directory_obj)
+ def Parser.parse_directories_string(data_a, directory_obj)
directories_number, i = data_a[0].to_i, 0
- directories_number.times {
+ directories_number.times do
i1, i2, i3 = i+=1, i+=1, i+=1
directory_obj.add_directory(data_a[i1], directory_content(data_a, i2)[0])
i = directory_content(data_a, i3)[1]
- }
+ end
end
- def Additional.directory_content(data_a, position)
+ def Parser.directory_content(data_a, position)
directory_string, step = "", position
while directory_string.size <= data_a[position].to_i
step+=1
directory_string << data_a[step].to_s << ":"
end
[RBFS::Directory::parse(directory_string), step]
end
end
end