Решение на Десета задача от Мартина Тонковска

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

Към профила на Мартина Тонковска

Резултати

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

Код

REPOSITORY = 'https://github.com/misosupu/ruby-retrospective-2015-2'
# Какво научих от Ruby Retrospective, част 2:
#
#1. Влагането на класове, с цел по-доброто разделение на кода.
#
#2. Използване на namespace-ове, за да подобрим организацията на кода и проследяването на неговата логика и структура.
#Например:
#В задача 5, финалната версия на домашното ми включва класове като CommitObject, които са кръстени толкова объркващо, вместо прост да се използва фактът, че CommitObject съществува единствено, за да улесняване на класа ObjectStore.
#
#Очевидно, по-добрият (и красив) подход е да напишем нещо такова: ObjectStore::CommitObject.
#
#3. Използването на case понякога е изкушаващо (особено, ако си свикнал да пишеш множество if-else-ове), но рядко е най-чистото решение.
#Например:
#В 6та задача, методите, управляващи смяната на посоката на костенурката, могат да станат на един ред и далеч по-елегантно по следния начин:
#
# @orientation = DIRECTIONS[(DIRECTIONS.find_index(@orientation) +/- 1) % 4]
#
#Също, с оглед на това, което прочетох като коментари на rubyists - честото използване на case в повечето случай е code smell.
#
#4. Мога да използвам методът instance_of?, за да проверя инстанция на кой клас е даден обект. Въпреки това, използването на такъв тип методи в общия случай е силно непрепоръчително, тъй като противоречат на duck typing идеологията, затова би трябвало да се избягват.
#
#5. Има разлика между dup и clone.
#
#И двете създават shallow копие на обекта, с който са извикани, но...
# 1. clone копира singleton класа, докато dup - не.
# 2. clone копира frozen и tainted state-a на даден обект, за разлика от dup, който копира само tainted state-a.
#
#Bonus: След продължително debug-ване установих, че трябва да внимавам повече с присвояванията на обекти към други. Все пак всичко в Ruby е клас. Некоректно присвояване беше причината за bug в LazyNotes задачата, добавях елементи в daily и weekly agenda, без да ги клонирам и крайният списък съдържаше еднакви елементи.
#
#
#6. Научих какво е null coalescing operator.
#Операторът || може да се използва (освен като булев предикат 'or') и когато дадена променлива не е дефинирана предварително
#дефинирана.
#
#Например:
#
#a ||= b, което е същото като a = a || b и означава: "инициализирай a със стойността на b, ако а.nil? или a == false"
#
#Може да се запише и по този начин: a = b if a == nil || a == false
#
#7. Когато се използва #method_missing е добре да се дефинира и #respond_to_missing?.
#
#8. method.__arity__
#Съществува следната конструкция:
#proc { |a, b, c| a + b + c }.arity # => 3
#
#9. 3a проверка дали низ съдържа даден regex pattern:
# 1. 'Very important note' =~ /Very/
# 2. 'Very important note'[/Very/]
# 3. 'Very important note'.match(/Very/)
#
#Bonus: Benchmarking Ruby code. В случая, разбира се, useless, но интересен екперимент. Също, TIL - Ruby има benchmarking module, като част от стандартната библиотека.
#
#Code:
#require 'benchmark'
#
#'Very important note' =~ /Very/
#puts Benchmark.measure { 1000000.times { "test123" =~ /1/ } }
#
#'Very important note'[/Very/]
#puts Benchmark.measure { 1000000.times { "test123"[/1/] } }
#
#'Very important note'.match(/Very/)
#puts Benchmark.measure { 1000000.times { "test123".match(/1/) } }
#
#Result:
# user system total real
#0.328000 0.000000 0.328000 ( 0.317529)
#0.390000 0.000000 0.390000 ( 0.387297)
#0.718000 0.000000 0.718000 ( 0.725083)
#
#10. Kакво са singleton method и singleton клас.
#
#В Ruby е възможно дефинирането на методи, специфичени за точно определен обект. Такъв тип методи се наричат singleton методи. Когато се дефинира такъв тип метод за даде обект, Ruby автоматично създава клас, който съдържа всички singleton методи за обекта. Въпросният клас е Singleton класа.
#
#
#11. [Концептуално] Какво са т.нар. "explaining variables".
#Explaining variables се използват с цел да се замени сложен израз с по-опростен, като част от неговия резултат се изчислява и записва в подходящо именована променлива.
#Ако даден метод има нужда от коментари, за да се изясни неговата цел, най-вероятно има нужда от рефакториране.
#
#Пример:
# function = OpenStruct.new(name: match[1].to_sym, parameters: parameters)
# validate_function(function)
#
#Вместо:
# validate_function(OpenStruct.new(name: match[1].to_sym, parameters: parameters))
#
#12. Всъщност, self (експлицитно извикан) се използва изключително рядко в Ruby.
#Необходим е, например, в следните случаи:
#
# 1. Искаме да дефинираме class method
# 2. Когато имаме дублиране на имена на методи и променливи (което, принципно, е непрепоръчително):
# def run(miles)
# self.miles = miles
# end
#
#Bonus:
#Извикването на методи в Ruby може да стане и без скоби, заграждащи неговите аргументи.
#
#Например:
# def foo(bar)
# # do something
# end
#
#Може да се запише и като:
# def foo bar
# # do something
# end
#
#13. Използване на require 'pp', за по-ясна видимост на променливите и по-лесно debug-ване.
#
#Bonus: gem awesome_print > Ruby STD pretty_print.
#
#14. Множество if-else-ове могат да се заменят далеч по-елегантно с хеш, където всеки ключ в хеша кореспондира на съответен случай в if/else контекст.
#
#Пример от задача 8:
# ARITHMETIC_FUNCTIONS = { ADD: [2, Float::INFINITY, :+], ..., MOD: [2, 2, :%] }
#
#Bonus: В Ruby няма предефинирана константа за безкрайност, но може да се използва Float::INFINITY като синоним.
#Също:
# 1/0 #=> ZeroDivisionError
# 1.0 / 0 #=> Infinity
# 1 / 0.0 #=> Infinity
# -1.0/0 #=> Negative infinity
#
# 15. Добра идея е регулярните изрази да се изнасят като константи на клас или модул, ако се използват повече от веднъж.
#
# 16. Две или повече променливи може да бъдат инициализирани на 1 ред с помощта на parallel assignemnt. Въпреки това е bad practice, според Ruby Style Guide: "Avoid the use of parallel assignment for defining variables. Parallel assignment is less readable than separate assignment."
#
# 17. Да пиша домашни/всякакъв Ruby код с отворена документация в браузъра. Ruby е език с богат набор от методи и почти винаги съществува готово (и по-оптимизирано)решение от стандартната библиотека, вместо сложно и завъртяно мое.
#
# 18. Array#count приема блок.
#
# 19. Поставянето на повече празни символи с цел нещата да изглеждат по-подредени не е добра идея и е признак за code smell.
#
# Щом методът има нужда от празен ред, в общи линии означава, че ясно можем да разграничим 2 смислово различни части от метода и в повечето случай това е признак за висок branching factor, както и знак, че част от логиката въпросния метод може да се изнесе отделно.
#
# 20. Колкото по-прост и ясен е кода, толкова по-лесен става за рефакториране.

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

From https://github.com/fmi/ruby-retrospective-2015-2
 * branch            master     -> FETCH_HEAD
HEAD is now at 512294b Modify a test in task 8 to not include empty cells
Cloning into 'submission'...
HEAD is now at 9e8ab73 Problem 8 handles Errors correctly now
From /tmp/ruby-retrospective-2015-2/checker
 * branch            master     -> FETCH_HEAD
 * [new branch]      master     -> upstream/master

Changes URL:
https://github.com/misosupu/ruby-retrospective-2015-2/compare/512294b6dde...9e8ab734d91

‘solutions/08.rb’ -> ‘/tmp/ruby-retrospective-2015-2/checker/solutions/08.rb’
‘solutions/05.rb’ -> ‘/tmp/ruby-retrospective-2015-2/checker/solutions/05.rb’
‘solutions/07.rb’ -> ‘/tmp/ruby-retrospective-2015-2/checker/solutions/07.rb’
‘solutions/06.rb’ -> ‘/tmp/ruby-retrospective-2015-2/checker/solutions/06.rb’
OK
..............................

Finished in 0.02512 seconds
30 examples, 0 failures
OK
..............

Finished in 0.00994 seconds
14 examples, 0 failures
OK
...............................

Finished in 0.01226 seconds
31 examples, 0 failures
OK
........................................

Finished in 0.02302 seconds
40 examples, 0 failures
From https://github.com/fmi/ruby-homework
 * branch            master     -> FETCH_HEAD
HEAD is now at 9dd040c Modify a test in task 8 to not include empty cells
.

Finished in 0.00297 seconds
1 example, 0 failures

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

Мартина обнови решението на 27.01.2016 20:14 (преди почти 9 години)

+REPOSITORY = 'https://github.com/misosupu/ruby-retrospective-2015-2'
+
+# Какво научих от Ruby Retrospective, част 2:
+#
+#1. Влагането на класове, с цел по-доброто разделение на кода.
+#
+#2. Използване на namespace-ове, за да подобрим организацията на кода и проследяването на неговата логика и структура.
+#Например:
+#В задача 5, финалната версия на домашното ми включва класове като CommitObject, които са кръстени толкова объркващо, вместо прост да се използва фактът, че CommitObject съществува единствено, за да улесняване на класа ObjectStore.
+#
+#Очевидно, по-добрият (и красив) подход е да напишем нещо такова: ObjectStore::CommitObject.
+#
+#3. Използването на case понякога е изкушаващо (особено, ако си свикнал да пишеш множество if-else-ове), но рядко е най-чистото решение.
+#Например:
+#В 6та задача, методите, управляващи смяната на посоката на костенурката, могат да станат на един ред и далеч по-елегантно по следния начин:
+#
+# @orientation = DIRECTIONS[(DIRECTIONS.find_index(@orientation) +/- 1) % 4]
+#
+#Също, с оглед на това, което прочетох като коментари на rubyists - честото използване на case в повечето случай е code smell.
+#
+#4. Мога да използвам методът instance_of?, за да проверя инстанция на кой клас е даден обект. Въпреки това, използването на такъв тип методи в общия случай е силно непрепоръчително, тъй като противоречат на duck typing идеологията, затова би трябвало да се избягват.
+#
+#5. Има разлика между dup и clone.
+#
+#И двете създават shallow копие на обекта, с който са извикани, но...
+# 1. clone копира singleton класа, докато dup - не.
+# 2. clone копира frozen и tainted state-a на даден обект, за разлика от dup, който копира само tainted state-a.
+#
+#Bonus: След продължително debug-ване установих, че трябва да внимавам повече с присвояванията на обекти към други. Все пак всичко в Ruby е клас. Некоректно присвояване беше причината за bug в LazyNotes задачата, добавях елементи в daily и weekly agenda, без да ги клонирам и крайният списък съдържаше еднакви елементи.
+#
+#
+#6. Научих какво е null coalescing operator.
+#Операторът || може да се използва (освен като булев предикат 'or') и когато дадена променлива не е дефинирана предварително
+#дефинирана.
+#
+#Например:
+#
+#a ||= b, което е същото като a = a || b и означава: "инициализирай a със стойността на b, ако а.nil? или a == false"
+#
+#Може да се запише и по този начин: a = b if a == nil || a == false
+#
+#7. Когато се използва #method_missing е добре да се дефинира и #respond_to_missing?.
+#
+#8. method.__arity__
+#Съществува следната конструкция:
+#proc { |a, b, c| a + b + c }.arity # => 3
+#
+#9. 3a проверка дали низ съдържа даден regex pattern:
+# 1. 'Very important note' =~ /Very/
+# 2. 'Very important note'[/Very/]
+# 3. 'Very important note'.match(/Very/)
+#
+#Bonus: Benchmarking Ruby code. В случая, разбира се, useless, но интересен екперимент. Също, TIL - Ruby има benchmarking module, като част от стандартната библиотека.
+#
+#Code:
+#require 'benchmark'
+#
+#'Very important note' =~ /Very/
+#puts Benchmark.measure { 1000000.times { "test123" =~ /1/ } }
+#
+#'Very important note'[/Very/]
+#puts Benchmark.measure { 1000000.times { "test123"[/1/] } }
+#
+#'Very important note'.match(/Very/)
+#puts Benchmark.measure { 1000000.times { "test123".match(/1/) } }
+#
+#Result:
+# user system total real
+#0.328000 0.000000 0.328000 ( 0.317529)
+#0.390000 0.000000 0.390000 ( 0.387297)
+#0.718000 0.000000 0.718000 ( 0.725083)
+#
+#10. Kакво са singleton method и singleton клас.
+#
+#В Ruby е възможно дефинирането на методи, специфичени за точно определен обект. Такъв тип методи се наричат singleton методи. Когато се дефинира такъв тип метод за даде обект, Ruby автоматично създава клас, който съдържа всички singleton методи за обекта. Въпросният клас е Singleton класа.
+#
+#
+#11. [Концептуално] Какво са т.нар. "explaining variables".
+#Explaining variables се използват с цел да се замени сложен израз с по-опростен, като част от неговия резултат се изчислява и записва в подходящо именована променлива.
+#Ако даден метод има нужда от коментари, за да се изясни неговата цел, най-вероятно има нужда от рефакториране.
+#
+#Пример:
+# function = OpenStruct.new(name: match[1].to_sym, parameters: parameters)
+# validate_function(function)
+#
+#Вместо:
+# validate_function(OpenStruct.new(name: match[1].to_sym, parameters: parameters))
+#
+#12. Всъщност, self (експлицитно извикан) се използва изключително рядко в Ruby.
+#Необходим е, например, в следните случаи:
+#
+# 1. Искаме да дефинираме class method
+# 2. Когато имаме дублиране на имена на методи и променливи (което, принципно, е непрепоръчително):
+# def run(miles)
+# self.miles = miles
+# end
+#
+#Bonus:
+#Извикването на методи в Ruby може да стане и без скоби, заграждащи неговите аргументи.
+#
+#Например:
+# def foo(bar)
+# # do something
+# end
+#
+#Може да се запише и като:
+# def foo bar
+# # do something
+# end
+#
+#13. Използване на require 'pp', за по-ясна видимост на променливите и по-лесно debug-ване.
+#
+#Bonus: gem awesome_print > Ruby STD pretty_print.
+#
+#14. Множество if-else-ове могат да се заменят далеч по-елегантно с хеш, където всеки ключ в хеша кореспондира на съответен случай в if/else контекст.
+#
+#Пример от задача 8:
+# ARITHMETIC_FUNCTIONS = { ADD: [2, Float::INFINITY, :+], ..., MOD: [2, 2, :%] }
+#
+#Bonus: В Ruby няма предефинирана константа за безкрайност, но може да се използва Float::INFINITY като синоним.
+#Също:
+# 1/0 #=> ZeroDivisionError
+# 1.0 / 0 #=> Infinity
+# 1 / 0.0 #=> Infinity
+# -1.0/0 #=> Negative infinity
+#
+# 15. Добра идея е регулярните изрази да се изнасят като константи на клас или модул, ако се използват повече от веднъж.
+#
+# 16. Две или повече променливи може да бъдат инициализирани на 1 ред с помощта на parallel assignemnt. Въпреки това е bad practice, според Ruby Style Guide: "Avoid the use of parallel assignment for defining variables. Parallel assignment is less readable than separate assignment."
+#
+# 17. Да пиша домашни/всякакъв Ruby код с отворена документация в браузъра. Ruby е език с богат набор от методи и почти винаги съществува готово (и по-оптимизирано)решение от стандартната библиотека, вместо сложно и завъртяно мое.
+#
+# 18. Array#count приема блок.
+#
+# 19. Поставянето на повече празни символи с цел нещата да изглеждат по-подредени не е добра идея и е признак за code smell.
+#
+# Щом методът има нужда от празен ред, в общи линии означава, че ясно можем да разграничим 2 смислово различни части от метода и в повечето случай това е признак за висок branching factor, както и знак, че част от логиката въпросния метод може да се изнесе отделно.
+#
+# 20. Колкото по-прост и ясен е кода, толкова по-лесен става за рефакториране.