Герасим обнови решението на 03.11.2014 17:23 (преди около 10 години)
В имплементацията липсва само метода parse на RBFS::Directory. Но останалите методи трябва да работят коректно. Използвам ли лоши практики? И относно парсирането на стринг за директория:
- Трябва да връщам нов обект от тип RBFS::Directory ?
- Как трябва да се пази името на файл/директория, подаден на директорията - през add_file/add_directory ?
- Нали мога да изнеса част от сериализациите в модул, тъй като няма да ми стигне само един метод за парсирането (а в момента имам 7)?
Здравей :)
Първо да отговоря на въпросите ти:
- Да, трябва да връщаш нов обект от тип
RBFS::Directory
. - Начинът, по който пазиш имената (като ключове на хешове) ми изглежда добре. Притеснява ли те нещо в него?
- Освен в модул, може да ги изнесеш и в клас. :) Все пак гледай класът/модулът да има някакъв смисъл - да има своята добре отделена задача, не просто място, където да си сложиш останалите методи.
Сега малко други коментари:
- Когато дефинираш клас, който се намира в модул - няма нужда да пишеш името на модула пред класа. Той ще бъде дефиниран в модула и в двата случая.
-
::Class
се използва само ако имаш клас със същото име в модула, в който се намираш (или някой от "родителите" му). - Името на метода
retrieve_data
не съответства на съдържанието му. Помисли за по-добро име. Освен това,when
-а в него плаче да изнесеш присвояването извън него. Напомням, че всички подобни конструкции в Ruby са изрази и връщат стойност. И последно за този метод - защо му е аргументътdata
, след като може да използва@data
? - Във
File.parse
правиш сплит два пъти. Можеш да си запазиш резултата, най-добре чрез "разопаковане" на масива, и да го използваш на двете места. Освен това можеш да елиминираш повторенията наFile.new
. - Не е нужно да проверяваш дали в името няма
:
. Обещахме, че няма да подаваме невалидни данни. :) Освен това, ако искахме такава проверка, би било добре да се хвърли изключение, не да се "премълчава".
Малко ми е трудно да парсна (pun intended) това за сериализирането:
- В
serialize_files
можеш да преизползвашFile#serialize
, няма нужда директорията да знае за точния формат на файловете. Променливатаcontent
няма ли да е по-добре да се казваfile
? - Сега осъзнавам, че
serialize_files
не го използваш. - Има нещо объркано при сериализирането.
serialize
викаserialize_directories
, който всеки път обхожда едни и същи файлове и директории. - Защо са ти случаите за 0 файлове и/или 0 директории? Ако направиш сериализирането чрез обхождане/map-ване (опа, подсказах :) ) то естествено ще се получи, без тези проверки.
- Ето малко препоръки как може да изчистиш частта със сериализацията:
- Не използвай конкатенация на низове - направи го с
map
иjoin
. - Това, което прави
serialize_directories
може да го прави самияserialize
. - Форматът за файловете и директориите е един и същ.
"#{entity_count}:#{entities}"
, къдетоentities
е#{name}:#{serialized_string}:#{serialized_string}
. Използвай рекурсивно резултата отFile#serialize
иDirectory#serialize
.
- Не използвай конкатенация на низове - направи го с
Ако нещо не ти е ясно по цялата книга, която изписах - питай. :)
Благодаря за отговорите!
Относно отговорите на въпросите ми:
- Ок.
- Объркваща е концепцията, но мисля, че сега ще се справя.
- Да, ще измисля елегантен начин за подреждането на функциите, а не просто „тъпчене“.
- Относно when-овете в retrieve_data - не се сещам как мога да изнеса присвояването извън тях. Мога да напиша when-овете постфиксно, но не знам дали ще има особена разлика. Освен това, подредбата не е най-добра, защото съм ги писал когато ограничението за редове на метод бе 6. Ще рефакторирам.
- Ъъъъ, wut. Ще ми трябва време да възприема всичката информация. Рекурсията в Ruby не изглежда толкова ясна, колкото в други езици. От началото предполагах, че мога да направя цялата сериализация в serialize, но стана прекалено дълго.
За case
-a - линк към слайд. Този е за if
, но предполагам вече схващаш какво имам предвид.
Друг вариант е да си направиш private метод, в който да имаш само case
-a и да присвояваш резултата от метода на @data_type
.
Аз не виждам разлика между рекурсията в Ruby и рекурсията в други езици.
Кажи кое от нещата не ти е ясно и ще се опитам да обясня по-добре. :)
Aхаааааааааааааааааааааа, сега ми се изясни. Хитро! :)
Рекурсивният подход си е един и същ в повечето езици - факт. Но концепцията не ми изглежда изчистена и удобна за ползване, като в други (чисто функционални) езици. След като реша задачата, ще ти дам мнение отново.
Благодаря за вниманието и ще питам, когато си поблъскам главата над някой проблем час-два, но ще рефакторирам в следващите 2-3 дни и ще се опитам да изчистя цялата концепция в главата си. : -)
def serialize_files
@files.map do|file|
serialized_file = file[1].serialize
file[0].to_s + ':' + serialized_file.length.to_s + ':' + serialized_file
end
end
def serialize
return '0:0:' if @directories.empty? and @files.empty?
files_format = serialize_files.join
files_format << '0:' if @directories.empty?
"#{@files.size}:#{files_format}" + \
@directories.map do |directory|
new_dir = directory[1].serialize
"#{@directories.size}:#{directory[0]}:#{new_dir.length}:#{new_dir}"
end
.join
end
Определено сега е по-добре. :) Map & join are a bliss!
Имам проблем с парсирането. Дефинирам метода без имплементация:
def parse(dir_data)
end
и при пускане на примерните тестове получавам грешка:
1) RBFS Directory serialization ::parse can parse
Failure/Error: parsed_directory = RBFS::Directory.parse(simple_serialized_string)
NoMethodError: undefined method 'parse' for RBFS::Directory:Class
# ./sample_spec.rb:44:in block (5 levels) in <top (required)>'
Kaкво пропускам? Ако искаш, мога да кача останалата част от кода, ако е нужно.
Трябва да е метод на класа..
гррррр
Като оправиш грешката направо си обнови решението :)
-
@files.map do|file|
защо не е@files.map do |file_name, file|
? -
file[0].to_s + ':' + serialized_file.length.to_s + ':' + serialized_file
--?-->"#{file_name}:#{serialized_file.length}:#{serialized_file}"
- Все още не разбирам защо са ти тези проверки
return '0:0:' if @directories.empty? and @files.empty?
иfiles_format << '0:' if @directories.empty?
. Нулата ще се яви естествено ако нямаш файлове или директории, защото масива ти ще е празен. - Прегледай пак примера в условието - броя на директориите трябва да го има веднъж преди да са изредени.
-
Това
@files.map do|file| serialized_file = file[1].serialize file[0].to_s + ':' + serialized_file.length.to_s + ':' + serialized_file end
и това
@directories.map do |directory| new_dir = directory[1].serialize "#{@directories.size}:#{directory[0]}:#{new_dir.length}:#{new_dir}" end
като си оправиш грешката във второто ще ти излязат едни и същи. Обедини си ги в някакъв метод, който да викаш два пъти - за файловете и за директориите.
- Toчка за теб.
- За 0:1:directory1:40:0:1:directory2:22:1:README:9:number:420: ми се чупи - не се добавя '0:' в края. А проверката за наличие на файлове и директории е за да не се счупи при
"#{@files.size}:#{files_format}"
. Не се сещам за по-добра интерпретация в момента. - Тъкмо приключвам с „по-добрата имплементация“. Чак се ядосвам колко приятно и удобно изглежда..
def serialize_files_or_directories(object)
object.map do|object_name, object_content|
serialized_object = object_content.serialize
"#{object_name}:#{serialized_object.length}:#{serialized_object}"
end
.join
end
def serialize
"#{@files.size}:#{serialize_files_or_directories @files}" \
"#{@directories.size}:#{serialize_files_or_directories @directories}"
end
Добър пример за duck typing
?