Скорее всего, от Вас очень хотели увидеть что-то типа небольшой пирамидки классов систем
счисления и языков, которые, возможно, конструировались бы фабриками.
А задание выполнено в чисто процедурном ключе, со структурами и некоторым количеством magic number.
SLanguage привносится extern-ом как глобальный объект и некоторые консультанты, ручаюсь, не преминут
отметить, что в многопоточной среде возможны проблемы.
Нет комментариев. Это не смертельно, на мой взгляд, но и код не настолько ясный, чтобы его можно
было сразу прочитать "с листа". Про пробелы вместо TAB-ов я сам, например, до недавнего времени
не знал, и только когда один коллега просматривал мой код "Блокнотом", я ужаснулся.
Хотя в целом — нормальный рабочий код, хорошо расставленные фигурные и прочие скобки и осмысленные имена.
Есть любители лепить большую кашу из всего этого.
Вообще предположу, что люди из "инспекции" особо не разбирались, просто открыли первый попавшийся
файл, увидели там "char pchResult[ 1024 ];" или точку с запятой после конструкции for, и решили не
продолжать с Вами.
Если ООП — не самая сильная Ваша составляющая как программиста, то положили бы рядом с Source и
Include папку с юнит-тестами. Это могло бы поправить ситуацию.
Здравствуйте, okman, Вы писали:
O>Здравствуйте, YaTL.
O>Скорее всего, от Вас очень хотели увидеть что-то типа небольшой пирамидки классов систем O>счисления и языков, которые, возможно, конструировались бы фабриками.
Дак ведь система счисления при данной реализации вообще задается одним числом... Ну подставьте вместо 8 например 7 или 6 в конструктор — уверен сработает.
O>А задание выполнено в чисто процедурном ключе, со структурами и некоторым количеством magic number. O>SLanguage привносится extern-ом как глобальный объект и некоторые консультанты, ручаюсь, не преминут O>отметить, что в многопоточной среде возможны проблемы.
Господи, да где вы все mfgic numbers видите? Там их просто НЕТ!!! И что, блокировку мьютексом данных объекта SLanguage можно сравнить по сложности с реализацией самого алгоритма?
O>Нет комментариев. Это не смертельно, на мой взгляд, но и код не настолько ясный, чтобы его можно O>было сразу прочитать "с листа". Про пробелы вместо TAB-ов я сам, например, до недавнего времени O>не знал, и только когда один коллега просматривал мой код "Блокнотом", я ужаснулся.
Каюсь. Писал в 4 часа ночи на ноутбуке. А вообще там гуру вроде, им комментарии разьве нужны? Это если для коллег пишешь, думаю, необходимо... Хотя как знать.
O>Хотя в целом — нормальный рабочий код, хорошо расставленные фигурные и прочие скобки и осмысленные имена. O>Есть любители лепить большую кашу из всего этого.
O>Вообще предположу, что люди из "инспекции" особо не разбирались, просто открыли первый попавшийся O>файл, увидели там "char pchResult[ 1024 ];" или точку с запятой после конструкции for, и решили не O>продолжать с Вами.
Ну это же ведь файл ИСПОЛЬЗОВАНИЯ (main.cpp), а не реализации... Или нет?
O>Если ООП — не самая сильная Ваша составляющая как программиста, то положили бы рядом с Source и O>Include папку с юнит-тестами. Это могло бы поправить ситуацию.
Здравствуйте, Олег К., Вы писали:
ОК>Наоборот, нормальные компании не хотят видеть ни синглтоны, ни абстрактные фабрики ни "труЪ С++ и все такое."
Зато они обожают на собеседованиях спрашивать про визиторы, фабрики и проакторы, чтобы скрыть всю
ту муть, в которую придется окунуться "счастливчику", устроившемуся к ним на работу.
Здравствуйте, YaTL.
Вы писали:
YTL>Дак ведь система счисления при данной реализации вообще задается одним числом... Ну подставьте вместо 8 например 7 или 6 в конструктор — уверен сработает.
Ну хорошо. Представьте, что я поддерживаю Ваш код и передо мной поставили задачу
приспособить его к шестнадцатеричной системе счисления. То есть, чтобы при входе "0B6FF2" на
выходе было "ноль би шесть эф эф два".
Куда мне копать ? Какие структуры писать ? Я не могу понять это из кода сразу — мне нужно будет
потратить хотя бы несколько часов, чтобы разобраться. В случае с хорошо написанными классами мне,
скорее всего, предстояло бы написать два-три наследника и переопределить кое-какие методы.
Чувствуете разницу ?
Далее. Предположим, я закончил код и хочу проверить его работу. Где гарантия работоспособности парсера ?
Я не могу прогнать тесты (их нет), чтобы поймать какую-то скрытую ошибку, если вдруг что-то сделаю не так и
напортачу со структурами. Что если все скомпилировалось и вроде бы сделано как надо, а при запуске
вываливается access violation ? Правильно, я узнаю у директора Ваш номер телефона и буду трезвонить
Вам по ночам, спрашивая, что писать в gauWordPartsEn. И Вы проклянете тот день, когда сочли всех
работающих в этой компании гуру, не приняв меня в расчет.
O>>А задание выполнено в чисто процедурном ключе, со структурами и некоторым количеством magic number. O>>SLanguage привносится extern-ом как глобальный объект и некоторые консультанты, ручаюсь, не преминут O>>отметить, что в многопоточной среде возможны проблемы.
YTL>Господи, да где вы все mfgic numbers видите? Там их просто НЕТ!!!
Где magic numbers ? Вот:
lang_en.cpp, начиная со строки 43 и дальше. При беглом просмотре назначение этих чисел не ясно.
Что такое number suffix и почему hundred там встречается трижды, причем первый раз ему сопоставлено
число 2, второй — 5, а третий — 8 ? В num_parser.cpp постоянно мелькает десятка, и это наряду с
такими именами, как uPartDegree10. А Вы точно уверены, что когда-нибудь эту десятку не понадобится
заменить числом 16 и придется с лупой бегать по всему коду ? Тоже самое с '\n' вместо endl.
А если надо будет на юникод портировать ? Вдруг wcout проглотит этот знак, а потом в японской
локализации это всплывет отсутствием конца строки ? Ну это я "придираюсь", по большому счету.
Все-таки это реализация, а не интерфейс.
Но код визуально должен быть безупречным. Даже если это супералгоритм, превосходящий аналоги в N раз —
если там повсюду будут знаки разыменования, кавычки, амперсанд и прочий "мусор", инспектирующий
интуитивно отвернется от такого кода. То же самое с magic numbers — числа должны стоять только там,
где их назначение абсолютно ясно из контекста, не может измениться в будущем и совершенно не может
трактоваться иначе. Например, if (Size == 0).
YTL>И что, блокировку мьютексом данных объекта SLanguage можно сравнить по сложности с реализацией самого алгоритма?
Если бы SLanguage был, скажем, локальным объектом, в блокировке вообще не было бы необходимости.
O>>Если ООП — не самая сильная Ваша составляющая как программиста, то положили бы рядом с Source и O>>Include папку с юнит-тестами. Это могло бы поправить ситуацию.
YTL>Ошибаетесь.
Вы вообще зря заняли позицию обороняющегося.
Я уверен, что любой из здесь присутствующих, выложи он свой код на обозрение (я за это плюсик и поставил),
получил бы нарекания точно в таком же количестве. Ну что если у человека на 1000 строк кода 20
использований friend или mutable ? Правильно ведь не молчать, а указать ему на то, что он, возможно,
что-то делает неверно и ему нужно пересмотреть свой подход к решению задачи или даже к языку вообще.
Здравствуйте, YaTL, Вы писали:
YTL>Насчет двоичной системы вопрос не совсем понятный, 8 и 10 задаются в конструкторе параметрами. А Вы не заметили что все эти const wchar_t* позволили выделить данные о языках в отдельные файлы с этими структурами, отделить их от функции/алгоритма? Других языков не знаю, но над переводом "я подумал", согласитесь.
Про двоичную понял, почитал алгоритм. А про wchar_t* не понял. Как они помогут, если, к примеру, нам захочется вынести инфу для каждого языка в xml-файлы? Согласитесь, удобно: добавил xml'ку и получил плюс один язык без всякого допиливания кода. И в main.cpp вот это
Здравствуйте, YaTL, Вы писали:
YTL>Если есть у кого время... История такая. Заинтересовался вакансией, сказали что рассмотрят меня как потенциального сотрудника только после выполненного задания. Вот текст задачи:
YTL>Задание: необходимо вывести введенное 10-тичное число словами на русском языке в 8-ми и 10-ной системах. Например. YTL>Вводим: 131 YTL>Выводим: YTL>сто тридцать один в десятичной системе; YTL>двести три в восьмеричной системе. YTL>При проектировании подумайте о том, как ваше решение можно было бы локализовать на английский язык (тут обратите внимание, например, на цифру "двести", которая на английском будет состоять из двух слов).
Попробую и я в Акронис устроиться. Прошу пинать.
Идея простая, как три копейки — у нас есть язык, состоящий из чисел прописью. Причем задан порядок для строк нашего языка. Опишем наш язык порождающей грамматикой, и сгенерим i-тый элемент. Если удастся задать грамматику в таком виде, что она будет порождать элементы в заданном порядке, то все срастется.
Опишем примитивы для определения грамматики:
class Grammar a where
e :: a -- empty string
t :: String -> a -- terminal
(<|>),(<*>) :: a -> a -> a -- Alternative, Sequence
count :: a -> Int -- cardinality of language
infixr 4 <|>
class (Grammar a) => OrderedGrammar a where
nth :: a -> Int -> [String] -- n-th element of language ordered set
и конкретную реализацию:
data G = Empty
| T String
| Alt G G
| Seq G G
deriving (Show)
instance Grammar G where
e = Empty
t = T
(<|>) = Alt
(<*>) = Seq
count (Empty) = 1
count (T _) = 1
count (Alt t1 t2) = let n1 = count t1; n2 = count t2 in n1 + n2
count (Seq t1 t2) = let n1 = count t1; n2 = count t2 in n1 * n2
instance OrderedGrammar G where
nth (Empty) 0 = []
nth (Empty) _ = error "invalid index"
nth (T s) 0 = [s]
nth (T _) _ = error "invalid index"
nth (Alt t1 t2) n | n < count t1 = nth t1 n
| otherwise = nth t2 (n - count t1)
nth (Seq t1 t2) n = let n2 = count t2; i1 = n `div` n2; i2 = n `mod` n2
in (nth t1 i1) ++ (nth t2 i2)
и вспомогательные функции:
n2words :: (OrderedGrammar a) => a -> Int -> String
n2words g n = unwords $ nth g n
strings :: (Grammar a) => [String] -> a
strings = foldr1 (<|>) . map t
Теперь все готово чтоб определить грамматику для английских чисел прописью:
-- english 10-ary grammar
en10 :: G
en10 = t "zero"
<|> en10_1e9
en10_1e9 = en10_1e6
<|> en10_1e3 <*> t "million" <*> t "and" <*> (e <|> en10_1e6)
en10_1e6 = en10_1e3
<|> en10_1e3 <*> t "thousand" <*> t "and" <*> (e <|> en10_1e3)
en10_1e3 = en10_nn
<|> en10_n00 <*> (e <|> en10_nn)
en10_n00 = en10_0 <*> t "hundred"
en10_nn = en10_0
<|> en10_1n
<|> en10_n0 <*> (e <|> en10_0)
en10_0 = strings ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
en10_1n = strings ["ten", "eleven", "twelve", "thirteen", "fourteen", "fiveteen"
, "sixteen", "seventeen", "eightteen", "nineteen"]
en10_n0 = strings ["twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety"]
Проверим:
*Main> count en10
1000000000
*Main> map (n2words en10) [0, 1, 131, 14563, 2000006]
["zero","one","one hundred thirty one","fourteen thousand and five hundred sixty three","two million and six"]
Вроде работает!
Определим грамматику для восьмеричных чисел:
-- english 8-ary grammar
en8 :: G
en8 = t "zero"
<|> en8_1e9
en8_1e9 = en8_1e6
<|> en8_1e3 <*> t "million" <*> t "and" <*> (e <|> en8_1e6)
en8_1e6 = en8_1e3
<|> en8_1e3 <*> t "thousand" <*> t "and" <*> (e <|> en8_1e3)
en8_1e3 = en8_nn
<|> en8_n00 <*> (e <|> en8_nn)
en8_n00 = en8_0 <*> t "hundred"
en8_nn = en8_0
<|> en8_1n
<|> en8_n0 <*> (e <|> en8_0)
en8_0 = strings ["one", "two", "three", "four", "five", "six", "seven"]
en8_1n = strings ["ten", "eleven", "twelve", "thirteen", "fourteen", "fiveteen"
, "sixteen", "seventeen"]
en8_n0 = strings ["twenty", "thirty", "fourty", "fifty", "sixty", "seventy"]
Проверим:
*Main> count en8
134217728
*Main> 8^9
134217728
*Main> map (n2words en8) [0, 1, 131, 0o14563, 0o2000006]
["zero","one","two hundred three","fourteen thousand and five hundred sixty three","two million and six"]
Грамматику для русского определим в следующей серии (из за согласования она посложнее английского, "одна тысяча" но "один миллион", однако нет ничего невозможного).
Абстракция достаточно мощна, чтоб выражать числа прописью для разных языков, без изменения кода, только задавая грамматики. Ограничения на грамматику сходу сформулировать не смогу, понятно что она должна выдавать строки по порядку. Сами грамматики можно достаточно легко задавать в виде отдельных ресурсных файлов (в том же XML). В данном решении не совсем обоснованно ввел класс Grammar, OrderedGrammar, ведь у меня только один инстанс.
Здравствуйте, ettcat, Вы писали:
E> Идея простая, как три копейки — у нас есть язык, состоящий из чисел прописью. Причем задан порядок для строк нашего языка. Опишем наш язык порождающей грамматикой, и сгенерим i-тый элемент. Если удастся задать грамматику в таком виде, что она будет порождать элементы в заданном порядке, то все срастется.
E> Опишем примитивы для определения грамматики:
E> Грамматику для русского определим в следующей серии (из за согласования она посложнее английского, "одна тысяча" но "один миллион", однако нет ничего невозможного).
Следующая серия — грамматика для русских чисел прописью:
-- russian 10-ary grammar
-- suffixes: f - feminine gender, m - masculine gender
-- s - singular, p - plural
-- g - genitive
ru10 :: G
ru10 = t "ноль"
<|> ru10_1e9
ru10_1e9 = ru10_1e6
<|> ru10_1e9_nnn <*> (e <|> ru10_1e6)
ru10_1e9_nnn = ru10_1e9_nn
<|> ru10_n00 <*> (t "миллионов" <|> ru10_1e9_nn)
ru10_1e9_nn = ru10_1e9_n
<|> ru10_1n <*> t "миллионов"
<|> ru10_n0 <*> (t "миллионов" <|> ru10_1e9_n)
ru10_1e9_n = ru10_1m <*> t "миллион"
<|> ru10_n_sgm <*> t "миллиона"
<|> ru10_n_pg <*> t "миллионов"
ru10_1e6 = ru10_1e3
<|> ru10_1e6_nnn <*> (e <|> ru10_1e3)
ru10_1e6_nnn = ru10_1e6_nn
<|> ru10_n00 <*> (t "тысяч" <|> ru10_1e6_nn)
ru10_1e6_nn = ru10_1e6_n
<|> ru10_1n <*> t "тысяч"
<|> ru10_n0 <*> (t "тысяч" <|> ru10_1e6_n)
ru10_1e6_n = ru10_1f <*> t "тысяча"
<|> ru10_n_sgf <*> t "тысячи"
<|> ru10_n_pg <*> t "тысяч"
ru10_1e3 = ru10_1e3_nn
<|> ru10_n00 <*> (e <|> ru10_1e3_nn)
ru10_1e3_nn = ru10_1e3_n
<|> ru10_1n
<|> ru10_n0 <*> (e <|> ru10_1e3_n)
ru10_1e3_n = ru10_1m
<|> ru10_n_sgm
<|> ru10_n_pg
ru10_1f, ru10_1m :: G
ru10_1f = t "одна"; ru10_1m = t "один"
ru10_2f = t "две"; ru10_2m = t "два"
ru10_n_sg = strings [ "три", "четыре" ]
ru10_n_sgf = ru10_2f <|> ru10_n_sg
ru10_n_sgm = ru10_2m <|> ru10_n_sg
ru10_n_pg = strings [ "пять", "шесть", "семь", "восемь", "девять" ]
Из за согласования, и из-за требования упорядоченности грамматики, приходится разворачивать каждую ветвь (тысяча|тысячи|тысяч, миллион|миллиона|миллионов) из-за чего грамматика разбухает.
Вообще, если убрать требование упорядоченности, то грамматика получится компактней, но при этом придется как-то задавать порядок генерации (например что-то типа sematic actions). Или для согласования можно как-то использовать аттрибутную грамматику (это так, на вскидку). Ушел думать...
T>P.S. На Haskell-то кто угодно напишет. Интересней сделать на C, скажем с YACC. Надо как-нибудь попробовать, как время будет.
Так тут не бог весть какой Haskell, причем в итоге строится дерево. Думаю на C++ получилось бы не сильно сложнее.
Если честно, не понял как тут переиспользовать YACC. Только если написать парсер нашей грамматики, заданной в BNF с построением нужного дерева.
PS Данное решение я привел исключительно как пример применения микроскопа для заколачивания гвоздей В зиллионах примеров кода "число прописью" ни разу не видел, чтоб использовались порождающие грамматики.
Здравствуйте, ettcat, Вы писали:
E>PS Данное решение я привел исключительно как пример применения микроскопа для заколачивания гвоздей В зиллионах примеров кода "число прописью" ни разу не видел, чтоб использовались порождающие грамматики.
Ну вообще-то я полностью согласен именно с вашим подходом к решению этой задачи. Идея писать жёсткий транслятор под конкретную задачу руками, как это сделал топикстартер мне кажется однозначно означающей провал задания, безотносительно к тому насколько там хорошо проведена декомпозиция, как соблюдаются отступы и сколько классов написано. В мире и так написаны тонны такого кода. Зачем писать ещё?
Здравствуйте, Олег К., Вы писали:
ОК>Наоборот, нормальные компании не хотят видеть ни синглтоны, ни абстрактные фабрики ни "труЪ С++ и все такое."
Синглетоны — конечно штука вредная, но нормальный С++ стиль — великая штука для сколько бы то ни было больших проектов. Я пока лично не видел хороших компаний использующих С или С стиль для проектов больше нескольких человеко-месяцев.
Здравствуйте, ettcat, Вы писали:
E>Кстати, получится расширить решение на немецкий? E>58 -> восемь-и-пятьдесят E>А вот французкий: E>77 -> шестьдесят семнадцать
Здравствуйте, ettcat, Вы писали:
E>Кстати, получится расширить решение на немецкий? E>58 -> восемь-и-пятьдесят
E>А вот французкий: E>77 -> шестьдесят семнадцать
Зачем устоявшиеся немецкие и французские названия чисел писать по-русски?
LGB>Зачем устоявшиеся немецкие и французские названия чисел писать по-русски?
Если английские написания перевести дословно на русский, то получится примерно так же, как и на русском. Правила порождения одинаковы (сотни, десятки, единицы, до 20 — специальные названия).
Внезапно (по крайне мере для меня) оказалось, что в немецком и французком правила несколько другие. А следовательно подход с зашиванием порядка следования (сначала сотни, потом десятки потом единицы), и числительных (..., "шестьдесят", "семьдесят", "восемьдесят", ...) в код может не сработать с французким и немецким языком. А на русский я перевел просто для наглядности, чтоб показать правила образования числительных.