Решение на Пета задача от Никола Ненков

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

Към профила на Никола Ненков

Резултати

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

Код

REPOSITORY = 'https://github.com/ndnenkov/ruby-retrospective-3'
# Двадесет неща, които научих:
#
# 1. Първоначалните идеи винаги трябва да се поставят под въпрос, особено когато не познаваме проблема / технологията (когато правих prime? имах mindset-a 'функционалният подход е по-красив и всичко се свежда до 3-те функции от висок ред', съответно използвах `reduce` по странен начин без да погледна и да видя, че all? съществува)
# 2. Ако искаме да получим range, който да обхождаме е по-добре да ползваме `x.upto(y)`, отколкото `(x..y)`. Също `(x...y)` съществува...
# 3. Невъзможността да се викат private методи с експлицитен получател не е ограничение, а по-скоро feature на езика, който подтиква към по-добър дизайн.
# 4. "ОО вариациите" често са по-четими, понеже предлагат възможност за chaining без пренареждане на операциите / операндите (пример: `remainder` vs %). Все пак не бива да се прекалява (дълга математическа формула няма да изглежда добре описана с думи).
# 5. Недообмисленото започване на имплементация с чисто интуитивна идея води до нагаждане на полу-работещ код към очаквания резултат. Крайният ефект е грозен и грешен код (пример: старите `prime_factors` и `combine_with`).
# 6. Не трябва да се избягват по-бавни решения или такива, които изискват повече ресурси, ако те са по-ясни и практиката не е показала, че конкретната оптимизация е нужна (пример: рекурсията в новия `prime_factors`). Забележка: имам предвид микро оптимизации, все още смятам неща като асимптотична сложност за такива, които трябва да се вземат под внимание от самото начало.
# 7. Когато намерим нещото, което изглежда като перфектното решение на нашия проблем, не трябва да се въодушевляваме до степен на заслепяване: възможно да е същестува "по-перфектно нещо" (пример: `split('')` vs `chars`).
# 8. Имена на локални променливи от типа на result са code smell, дори и контекста (името на метода) повече от ясно да казва какво ще съдържат. Почти винаги използването им може да се избегне. В случаите когато не може е важно да се отдели нужното време за измисляне на подходящо име.
# 9. Минаването на тестове не е доказателство за коректност, дори когато те са били писани от зли хора с цел да ти намалят броя точки (пример: старият `drop_every`).
# 10. В повечето от случаите директното превеждане от чист английски на Ruby води до кратък и ясен код (пример: новият `combine_with`).
# 11. Ако колелото е достатъчно малко е лесно да не усетим, че го преоткриване (пример: старият `TodoList#each`).
# 12. Противно на старите ми убеждения, вътрешните класове не са зло. Те предоставят лесен начин за представяне на по-добра абстракция и по-висока кохезия (пример: `Parser` класът в новия `TodoList`).
# 13. Стандартен начин за имплементиране на `hash` е `[@field1, @field2, ...].hash`. Той не само е много четим, но и работи по-добре от повечето имплементации, които бихме измислили сами.
# 14. Ако дълго време сме мислили за имена на локални променливи най-вероятно е имало добър начин да се заобиколи нуждата им изобщо (пример: множеството методи във втора задача, които вече са one-liner-и).
# 15. Мъдър програмист някога е казал "добър код е този, който очакваш да видиш". Това понякога е в разрез с #10: когато става дума за известен алгоритъм, просто казано очакваме да видим преведен псевдокода от статията в Wikipedia, а не обяснението на английски, което бихме дали на човек, който преди не е чувал за алгоритъма (сори, това вероятно е повече лично мнение).
# 16. Bresenham за права може да се реализира на един ред. MINDBLOW!
# 17. Модулите не са ОО и трябва да се избягват в полза на наследяването, освен ако не сме много наясно защо искаме да ползваме модул в конкретната ситуация (старият `Renderers`).
# 18. Родителските класове трябва да съдържат общата функционалност за своите деца, а не да се опитват да имплементират функционалността на всичките си деца. Най-лесно може да се усетим, че правим тази грешка, ако докато създаваме родителския клас искаме да знаем кои ще са всичките му деца. (Ако не се разбра какво имам предвид - говоря за старите си `Renderers`. Ако за момент си представим, че не ползвах модул, а наследявах, родителският клас щеше да имплементира `render` като `prefix + canvas_to_string + suffix` и децата ще трябва да си кажат какви са им `prefix` и `suffix`. По-добре е родителят само да превръща `canvas_to_string` и конкретните деца да си добавят `prefix` и `suffix`, ако имат).
# 19. Нещата, които са по-четими в езика не рядко са и по-оптимални (`attr_reader`-и на един ред, докато множественото присвояване се избягва).
# 20. Трябва много да поработя над commit навиците си. Някои от commit-ите ми не решават изцяло проблемите, които твърдят че адресират. Други не са достатъчно насочени към конкретно нещо и междувременно правят малки неща, които дори не споменават в съобщението си. Дори имаше един момент, в който цял час не commit-вах и накрая направих един голям commit с много generic съобщение, което в действителност не казва нищо.

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

Никола обнови решението на 22.01.2014 13:23 (преди над 10 години)

+REPOSITORY = 'https://github.com/ndnenkov/ruby-retrospective-3'
+
+# Двадесет неща, които научих:
+#
+# 1. Първоначалните идеи винаги трябва да се поставят под въпрос, особено когато не познаваме проблема / технологията (когато правих prime? имах mindset-a 'функционалният подход е по-красив и всичко се свежда до 3-те функции от висок ред', съответно използвах `reduce` по странен начин без да погледна и да видя, че all? съществува)
+# 2. Ако искаме да получим range, който да обхождаме е по-добре да ползваме `x.upto(y)`, отколкото `(x..y)`. Също `(x...y)` съществува...
+# 3. Невъзможността да се викат private методи с експлицитен получател не е ограничение, а по-скоро feature на езика, който подтиква към по-добър дизайн.
+# 4. "ОО вариациите" често са по-четими, понеже предлагат възможност за chaining без пренареждане на операциите / операндите (пример: `remainder` vs %). Все пак не бива да се прекалява (дълга математическа формула няма да изглежда добре описана с думи).
+# 5. Недообмисленото започване на имплементация с чисто интуитивна идея води до нагаждане на полу-работещ код към очаквания резултат. Крайният ефект е грозен и грешен код (пример: старите `prime_factors` и `combine_with`).
+# 6. Не трябва да се избягват по-бавни решения или такива, които изискват повече ресурси, ако те са по-ясни и практиката не е показала, че конкретната оптимизация е нужна (пример: рекурсията в новия `prime_factors`). Забележка: имам предвид микро оптимизации, все още смятам неща като асимптотична сложност за такива, които трябва да се вземат под внимание от самото начало.
+# 7. Когато намерим нещото, което изглежда като перфектното решение на нашия проблем, не трябва да се въодушевляваме до степен на заслепяване: възможно да е същестува "по-перфектно нещо" (пример: `split('')` vs `chars`).
+# 8. Имена на локални променливи от типа на result са code smell, дори и контекста (името на метода) повече от ясно да казва какво ще съдържат. Почти винаги използването им може да се избегне. В случаите когато не може е важно да се отдели нужното време за измисляне на подходящо име.
+# 9. Минаването на тестове не е доказателство за коректност, дори когато те са били писани от зли хора с цел да ти намалят броя точки (пример: старият `drop_every`).
+# 10. В повечето от случаите директното превеждане от чист английски на Ruby води до кратък и ясен код (пример: новият `combine_with`).
+# 11. Ако колелото е достатъчно малко е лесно да не усетим, че го преоткриване (пример: старият `TodoList#each`).
+# 12. Противно на старите ми убеждения, вътрешните класове не са зло. Те предоставят лесен начин за представяне на по-добра абстракция и по-висока кохезия (пример: `Parser` класът в новия `TodoList`).
+# 13. Стандартен начин за имплементиране на `hash` е `[@field1, @field2, ...].hash`. Той не само е много четим, но и работи по-добре от повечето имплементации, които бихме измислили сами.
+# 14. Ако дълго време сме мислили за имена на локални променливи най-вероятно е имало добър начин да се заобиколи нуждата им изобщо (пример: множеството методи във втора задача, които вече са one-liner-и).
+# 15. Мъдър програмист някога е казал "добър код е този, който очакваш да видиш". Това понякога е в разрез с #10: когато става дума за известен алгоритъм, просто казано очакваме да видим преведен псевдокода от статията в Wikipedia, а не обяснението на английски, което бихме дали на човек, който преди не е чувал за алгоритъма (сори, това вероятно е повече лично мнение).
+# 16. Bresenham за права може да се реализира на един ред. MINDBLOW!
+# 17. Модулите не са ОО и трябва да се избягват в полза на наследяването, освен ако не сме много наясно защо искаме да ползваме модул в конкретната ситуация (старият `Renderers`).
+# 18. Родителските класове трябва да съдържат общата функционалност за своите деца, а не да се опитват да имплементират функционалността на всичките си деца. Най-лесно може да се усетим, че правим тази грешка, ако докато създаваме родителския клас искаме да знаем кои ще са всичките му деца. (Ако не се разбра какво имам предвид - говоря за старите си `Renderers`. Ако за момент си представим, че не ползвах модул, а наследявах, родителският клас щеше да имплементира `render` като `prefix + canvas_to_string + suffix` и децата ще трябва да си кажат какви са им `prefix` и `suffix`. По-добре е родителят само да превръща `canvas_to_string` и конкретните деца да си добавят `prefix` и `suffix`, ако имат).
+# 19. Нещата, които са по-четими в езика не рядко са и по-оптимални (`attr_reader`-и на един ред, докато множественото присвояване се избягва).
+# 20. Трябва много да поработя над commit навиците си. Някои от commit-ите ми не решават изцяло проблемите, които твърдят че адресират. Други не са достатъчно насочени към конкретно нещо и междувременно правят малки неща, които дори не споменават в съобщението си. Дори имаше един момент, в който цял час не commit-вах и накрая направих един голям commit с много generic съобщение, което в действителност не казва нищо.

Много добри изводи. Заслужи си бонус точките :)

Бележка по точка 13 е, че този начин на имплементиране на hash не е особено оптимален. На места, където производителността е от критично значение, по-добре да се направи друга имплементация, или поне да се мемоизира (кешира) резултата.

По т. 15 може да се спори, защото псевдокодът в Уикипедия не трябва да е референция :)

Решенията ти са прилични. Дребна бележка относно употребата на Struct - аз не бих го ползвал така. Смятам, че attr_accessor е не по-дълъг, но по-четим вариант. Надявам се да си доволен от наученото :)

Само едно допълнение - skeptic ти гърмеше на няколко места с английски думи, но предполагам не си успял да го подкараш с Aspell. Единствената реална грешка беше в думата "divisable", която се пише "divisible". Всички други ограничения ти бяха окей, включително и тестовете, затова не си с нула точки като някои твои колеги.