Если есть у кого время... История такая. Заинтересовался вакансией, сказали что рассмотрят меня как потенциального сотрудника только после выполненного задания. Вот текст задачи:
Задание: необходимо вывести введенное 10-тичное число словами на русском языке в 8-ми и 10-ной системах. Например.
Вводим: 131
Выводим:
сто тридцать один в десятичной системе;
двести три в восьмеричной системе.
При проектировании подумайте о том, как ваше решение можно было бы локализовать на английский язык (тут обратите внимание, например, на цифру "двести", которая на английском будет состоять из двух слов).
Суть не в том, что мне отказали, а что я потратил некоторое количество личного времени на написание кода, и очень хочу знать на чем "срубился". Заранее благодарен за любые комментарии.
Не знаю на чем вы срубились. Думаю, дело не в отрицательных, или слишком больших числах, кривом интерфейсе командной строки — все это мелочи.
Лично я бы не взял за использование С++ на пустом месте.
imho, проще надо быть, проще.
Детально не вчитывался в алгоритм, просмотрел практически по-диагонали. Сразу бросилось в глаза, что написано в сишном стиле. Возможно, люди хотели увидеть синглтон, абстрактную фабрику или что-то подобное, труЪ C++ и все такое. Структуры, содержащие const wchar_t* режут глаз. Где конструктор копирования, оператор присваивания? Понятно, что оно, м.б. и не надо в данном случае, но... И почему const wchar_t* вместо std::string? И почему вообще wchar_t вместо TCHAR (ну, это мой личный пунктик, м.б. не все согласятся)? Легко ли расширить твой алгоритм для двоичной системы?
Здравствуйте, YaTL, Вы писали:
YTL>Если есть у кого время... История такая. Заинтересовался вакансией, сказали что рассмотрят меня как потенциального сотрудника только после выполненного задания. Вот текст задачи: YTL>Суть не в том, что мне отказали, а что я потратил некоторое количество личного времени на написание кода, YTL>и очень хочу знать на чем "срубился". Заранее благодарен за любые комментарии.
бросилось в глаза отсутствие "нуля" (про отрицательные уже сказали), проверки на не-число, а так же несоответствие заданию. там несколько другой вывод просили сделать. на код и алгоритм не смотрел. а еще бросилось в глаза отсутствие комментариев. но имхо это не смертельно. я бы вас взял. коллеги и не такие ляпы допускают. дело ж не в ляпах, а чтобы пнули -> исправил, а не начинал оправдываться. может быть, вы не туда трудоустраиваетесь. может быть я не прав. не знаю...
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Здравствуйте, 0xC0DE, Вы писали:
CDE>Здравствуйте, DimaX, Вы писали:
DX>>Лично я бы не взял за использование С++ на пустом месте. CDE>Э-э-э, а если вакансия была программиста C++?
а что в этом коде от плюсов кроме расширения?
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Здравствуйте, мыщъх, Вы писали:
М>Здравствуйте, 0xC0DE, Вы писали:
CDE>>Здравствуйте, DimaX, Вы писали:
DX>>>Лично я бы не взял за использование С++ на пустом месте. CDE>>Э-э-э, а если вакансия была программиста C++? М>а что в этом коде от плюсов кроме расширения?
Посмотрел код еще раз и не понял в чем собственно вопрос? По вашему это С-шный код?
Здравствуйте, DimaX, Вы писали:
DX>Здравствуйте, мыщъх, Вы писали:
М>>Здравствуйте, 0xC0DE, Вы писали:
CDE>>>Здравствуйте, DimaX, Вы писали:
DX>>>>Лично я бы не взял за использование С++ на пустом месте. CDE>>>Э-э-э, а если вакансия была программиста C++? М>>а что в этом коде от плюсов кроме расширения?
DX>Посмотрел код еще раз и не понял в чем собственно вопрос? По вашему это С-шный код?
боюсь обидеть, но это даже ближе к бейскику, чем к процедурному. и это уж точно не ооп. декомпозиции нет. попробуйте записать эту программу в несколько строк так чтобы выразить алгоритм. в псеводокоде. если вам нужна какая-то функция, которой нет в си -- ну так дайте ей осмысленное имя и вызовите. сами функции потом напишите это будет классика процедурного программирования. а так это бейскик.
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
CDE>Детально не вчитывался в алгоритм, просмотрел практически по-диагонали. Сразу бросилось в глаза, что написано в сишном стиле. Возможно, люди хотели увидеть синглтон, абстрактную фабрику или что-то подобное, труЪ C++ и все такое. Структуры, содержащие const wchar_t* режут глаз. Где конструктор копирования, оператор присваивания? Понятно, что оно, м.б. и не надо в данном случае, но... И почему const wchar_t* вместо std::string? И почему вообще wchar_t вместо TCHAR (ну, это мой личный пунктик, м.б. не все согласятся)? Легко ли расширить твой алгоритм для двоичной системы?
Наоборот, нормальные компании не хотят видеть ни синглтоны, ни абстрактные фабрики ни "труЪ С++ и все такое."
Здравствуйте, YaTL, Вы писали:
YTL>Суть не в том, что мне отказали, а что я потратил некоторое количество личного времени на написание кода, и очень хочу знать на чем "срубился". Заранее благодарен за любые комментарии.
Посмотрел очень мельком, и даже сравнительно неплохо все, без мата можно читать. За форматирование нормальное и следование code convention большой респект, очень мало народа на это обращает внимание.
Вот только CNumParser::ParseOneStep. Метод слишком большой, ИМХО надо разбивать на множество методов, сейчас разбираться как он работает нет ни малейшего желания в текущей реализации. Особенно magic number 10 там интересно смотрится, сходу я его бы не ожидал там встретить, у нас же переменное основание системы счисления, откуда оно там может взяться непонятно. Учитывая, что это единственная логика в задании, и все сделано одним методом, лично я бы расценил это как неумение декомпозировать задачу, и отсутствие навыков процедурного программирования (про ООП даже не говорю, но оно здесь и не нужно ИМХО).
Здравствуйте, Олег К., Вы писали:
CDE>>Детально не вчитывался в алгоритм, просмотрел практически по-диагонали. Сразу бросилось в глаза, что написано в сишном стиле. Возможно, люди хотели увидеть синглтон, абстрактную фабрику или что-то подобное, труЪ C++ и все такое. Структуры, содержащие const wchar_t* режут глаз. Где конструктор копирования, оператор присваивания? Понятно, что оно, м.б. и не надо в данном случае, но... И почему const wchar_t* вместо std::string? И почему вообще wchar_t вместо TCHAR (ну, это мой личный пунктик, м.б. не все согласятся)? Легко ли расширить твой алгоритм для двоичной системы? ОК>Наоборот, нормальные компании не хотят видеть ни синглтоны, ни абстрактные фабрики ни "труЪ С++ и все такое."
Здравствуйте, dilmah, Вы писали:
D>где нормальное форматирование?? D>там табы в середине строки стоят. Ты видел как этот код выглядит со стандартным размером таба 8?
Ну, у меня открылось нормально, я аж удивился . Сходу косяков не заметил.
Здравствуйте, Олег К., Вы писали:
CDE>>Детально не вчитывался в алгоритм, просмотрел практически по-диагонали. Сразу бросилось в глаза, что написано в сишном стиле. Возможно, люди хотели увидеть синглтон, абстрактную фабрику или что-то подобное, труЪ C++ и все такое. Структуры, содержащие const wchar_t* режут глаз. Где конструктор копирования, оператор присваивания? Понятно, что оно, м.б. и не надо в данном случае, но... И почему const wchar_t* вместо std::string? И почему вообще wchar_t вместо TCHAR (ну, это мой личный пунктик, м.б. не все согласятся)? Легко ли расширить твой алгоритм для двоичной системы? ОК>Наоборот, нормальные компании не хотят видеть ни синглтоны, ни абстрактные фабрики ни "труЪ С++ и все такое."
+1.
Но обычно они хотят видеть std::string std::wstring и vector вместо char[1024], wchar_t[1024], просто чтобы не было магических констант при превышении которых программа упадет.
Любая проблема дизайна может быть решена введением дополнительного абстрактного слоя, за исключением проблемы слишком большого количества дополнительных абстрактных слоев
Здравствуйте, мыщъх, Вы писали:
М>Здравствуйте, DimaX, Вы писали:
DX>>Здравствуйте, мыщъх, Вы писали:
М>>>Здравствуйте, 0xC0DE, Вы писали:
CDE>>>>Здравствуйте, DimaX, Вы писали:
DX>>>>>Лично я бы не взял за использование С++ на пустом месте. CDE>>>>Э-э-э, а если вакансия была программиста C++? М>>>а что в этом коде от плюсов кроме расширения?
DX>>Посмотрел код еще раз и не понял в чем собственно вопрос? По вашему это С-шный код? М>боюсь обидеть, но это даже ближе к бейскику, чем к процедурному. и это уж точно не ооп. декомпозиции нет. попробуйте записать эту программу в несколько строк так чтобы выразить алгоритм. в псеводокоде. если вам нужна какая-то функция, которой нет в си -- ну так дайте ей осмысленное имя и вызовите. сами функции потом напишите это будет классика процедурного программирования. а так это бейскик.
Я правильно понимаю, что задача не сложная и нужно было упор делать не на том чтобы выполнить ее "от и до", т.е. даже то чего не просили но намекали (с англ.яз.), а не думая над упрощением кода демонстрировать "а еще я знаю шаблоны, паттерны..."?
Здравствуйте, мыщъх, Вы писали:
М>Здравствуйте, YaTL, Вы писали:
YTL>>Если есть у кого время... История такая. Заинтересовался вакансией, сказали что рассмотрят меня как потенциального сотрудника только после выполненного задания. Вот текст задачи: YTL>>Суть не в том, что мне отказали, а что я потратил некоторое количество личного времени на написание кода, YTL>>и очень хочу знать на чем "срубился". Заранее благодарен за любые комментарии. М>бросилось в глаза отсутствие "нуля" (про отрицательные уже сказали), проверки на не-число, а так же несоответствие заданию. там несколько другой вывод просили сделать. на код и алгоритм не смотрел. а еще бросилось в глаза отсутствие комментариев. но имхо это не смертельно. я бы вас взял. коллеги и не такие ляпы допускают. дело ж не в ляпах, а чтобы пнули -> исправил, а не начинал оправдываться. может быть, вы не туда трудоустраиваетесь. может быть я не прав. не знаю...
Ноль там есть. Не закрывая консоли после завершения программы можно его увидеть
Здравствуйте, elmal, Вы писали:
E>Здравствуйте, YaTL, Вы писали:
YTL>>Суть не в том, что мне отказали, а что я потратил некоторое количество личного времени на написание кода, и очень хочу знать на чем "срубился". Заранее благодарен за любые комментарии. E>Посмотрел очень мельком, и даже сравнительно неплохо все, без мата можно читать. За форматирование нормальное и следование code convention большой респект, очень мало народа на это обращает внимание. E>Вот только CNumParser::ParseOneStep. Метод слишком большой, ИМХО надо разбивать на множество методов, сейчас разбираться как он работает нет ни малейшего желания в текущей реализации. Особенно magic number 10 там интересно смотрится, сходу я его бы не ожидал там встретить, у нас же переменное основание системы счисления, откуда оно там может взяться непонятно. Учитывая, что это единственная логика в задании, и все сделано одним методом, лично я бы расценил это как неумение декомпозировать задачу, и отсутствие навыков процедурного программирования (про ООП даже не говорю, но оно здесь и не нужно ИМХО).
10 не магическое число, а "человечья система счисления", а та которая требуется для вывода имеет место быть в качестве переменного параметра (10, 8) в конструкторе...
Здравствуйте, 0xC0DE, Вы писали:
CDE>Детально не вчитывался в алгоритм, просмотрел практически по-диагонали. Сразу бросилось в глаза, что написано в сишном стиле. Возможно, люди хотели увидеть синглтон, абстрактную фабрику или что-то подобное, труЪ C++ и все такое. Структуры, содержащие const wchar_t* режут глаз. Где конструктор копирования, оператор присваивания? Понятно, что оно, м.б. и не надо в данном случае, но... И почему const wchar_t* вместо std::string? И почему вообще wchar_t вместо TCHAR (ну, это мой личный пунктик, м.б. не все согласятся)? Легко ли расширить твой алгоритм для двоичной системы?
Насчет двоичной системы вопрос не совсем понятный, 8 и 10 задаются в конструкторе параметрами. А Вы не заметили что все эти const wchar_t* позволили выделить данные о языках в отдельные файлы с этими структурами, отделить их от функции/алгоритма? Других языков не знаю, но над переводом "я подумал", согласитесь.
Здравствуйте, YaTL, Вы писали:
YTL>Я правильно понимаю, что задача не сложная и нужно было упор делать не на том чтобы выполнить ее "от и до", т.е. даже то чего не просили но намекали (с англ.яз.), а не думая над упрощением кода демонстрировать "а еще я знаю шаблоны, паттерны..."?
Не надо ничего демонстрировать, упаси боже. Нужно писать так, чтобы:
1) Легко читалось, чтоб чужой человек сходу бы понял как сделано. Сходу — беглым взглядом. Если невозможно беглым из за хитрых алгоритмов — это должно быть откомментировано;
2) Не должно быть явных багов;
3) Нужно быть готовым к тому, что попросят что то дописать, нужно предугадать возможные изменения и должен был быть план как будем развивать.
Паттерны и все такое являются средством решения, а не самоцелью. На мелких задачах их применять редко когда есть смысл.
Основное — это пункт 1. После п.1 будут смотреть 2, и только потом 3 .
Скорее всего, от Вас очень хотели увидеть что-то типа небольшой пирамидки классов систем
счисления и языков, которые, возможно, конструировались бы фабриками.
А задание выполнено в чисто процедурном ключе, со структурами и некоторым количеством 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 — специальные названия).
Внезапно (по крайне мере для меня) оказалось, что в немецком и французком правила несколько другие. А следовательно подход с зашиванием порядка следования (сначала сотни, потом десятки потом единицы), и числительных (..., "шестьдесят", "семьдесят", "восемьдесят", ...) в код может не сработать с французким и немецким языком. А на русский я перевел просто для наглядности, чтоб показать правила образования числительных.