# Конвертировать английский в число [закрыто]

27

Краткое и приятное описание задачи:

Основываясь на идеях нескольких других вопросов на этом сайте, ваша задача состоит в том, чтобы написать наиболее креативный код в любой программе, который принимает в качестве входных данных число, написанное на английском языке, и преобразует его в целочисленную форму.

Действительно сухие, длинные и тщательные характеристики:

• Ваша программа получит в качестве входных данных целое число на английском языке в нижнем регистре между `zero`и `nine hundred ninety-nine thousand nine hundred ninety-nine`включительно.
• Он должен выводить только целую форму числа между `0`и `999999`и ничего больше (без пробелов).
• Вход НЕ будет содержать `,`или `and`, как в `one thousand, two hundred`или `five hundred and thirty-two`.
• Когда места десятков и единиц отличны от нуля, а места десятков больше чем `1`, они будут разделены символом HYPHEN-MINUS `-`вместо пробела. То же самое для десяти тысяч и тысяч мест. Например, `six hundred fifty-four thousand three hundred twenty-one`.
• Программа может иметь неопределенное поведение для любого другого ввода.

Некоторые примеры хорошо управляемой программы:

`zero`-> `0`
`fifteen`-> `15`
`ninety`-> `90`
`seven hundred four`-> `704`
`sixty-nine thousand four hundred eleven`-> `69411`
`five hundred twenty thousand two`->`520002`

Это не особенно креативно и не совсем соответствует спецификации здесь, но это может быть полезно в качестве отправной точки: github.com/ghewgill/text2num/blob/master/text2num.py
Грег Хьюджилл,

Я мог бы почти опубликовать свой ответ на этот вопрос .
GRC

Почему сложный разбор строк? pastebin.com/WyXevnxb
blutorange

1
Кстати, я видел запись IOCCC, которая является ответом на этот вопрос.
Закуска

2
Как насчет таких вещей, как "четыре и двадцать?"
пушистый

Ответы:

93

# Applescript

Глупое, смешное месиво, которое может расстроить некоторых людей из Купертино / Маунтин-Вью, но я думаю, что это творческое глупое, смешное месиво.

``````set myNumber to text returned of (display dialog ¬
"Enter number as text:" buttons {"Continue…"} ¬
default answer "" default button 1)
activate
end tell
delay 5
say "ok google. " & myNumber
delay 2
tell application "System Events"
set fullURL to value of text field 1 of toolbar 1 of window 1
end tell
end tell
set AppleScript's text item delimiters to "="
display alert item 2 of text items of fullURL``````

Использует OSX текст в речь, чтобы произнести текстовый номер, и поиск аудио Google, чтобы прослушать его и преобразовать в целое число.

Требования

• OSX
• Гугл Хром
• распознавание речи включено в вашем аккаунте Google
• громкость поднялась до разумного уровня

Время задержки может потребоваться изменить в зависимости от времени загрузки Chrome и времени поиска в Google.

Пример ввода: Пример вывода: 13
Я думаю, что это может быть немного креативным ...;)
Авраам

5
Lol, это круто
Justhalf

2
Может быть, слишком креативно.
Cheezey

Через неделю ваш ответ явно лидирует при 74 голосах, поэтому я думаю, что это означает, что ... вы выиграли! Кстати, не против, если я воспользуюсь этим кодом? Это было бы очень полезно для многих реальных проектов, над которыми я сейчас работаю! ;)
Авраам

3
@ Авраам Спасибо! Вы шутите об использовании этого в производственном коде, верно?
Цифровая травма

34

# Баш, 93 64 55 символов *

В фантастическом `bsd-games`пакете, который доступен в большинстве операционных систем Linux, есть маленькая игрушка командной строки под названием `number`. Он превращает числа в текст на английском языке, то есть полностью противоположен этому вопросу. Это действительно с точностью до наоборот: все правила в вопросе соблюдаются `number`. Это почти слишком хорошо, чтобы быть совпадением.

``````\$ number 42
forty-two.
``````

Конечно, `number`не отвечает на вопрос. Мы хотим это наоборот. Я немного подумал об этом, попробовал разбор строк и все такое, потом понял, что могу просто позвонить `number`на все 999,999 и посмотреть, совпадает ли что-то с вводом. Если это так, первая строка, в которой он совпадает, содержит вдвое больший номер строки, который я ищу ( `number`печатает строку точек после каждого числа). Просто как тот. Итак, без лишних слов, вот полный код для моей записи:

``````seq 0 999999|number -l|awk "/\$1/{print (NR-1)/2;exit}"
``````

Это даже короткое замыкание, поэтому преобразование «два» довольно быстро, и даже более высокие числа обычно декодируются менее чем за секунду на моем устройстве. Вот пример запуска:

``````wn@box /tmp> bash unnumber.sh "zero"
0
wn@box /tmp> bash unnumber.sh "fifteen"
15
wn@box /tmp> bash unnumber.sh "ninety"
90
wn@box /tmp> bash unnumber.sh "seven hundred four"
704
wn@box /tmp> bash unnumber.sh "sixty-nine thousand four hundred eleven"
69411
wn@box /tmp> bash unnumber.sh "five hundred twenty thousand two"
520002
``````

Конечно, вам нужно `number`установить для этого, чтобы работать.

*: Да, я знаю, это не `code-golf`проблема, но краткость - в значительной степени единственное разборчивое качество моей работы, так что ... :)

8
+1. Для меня использование `number`обратного - самая креативная вещь в этом ответе. Хотя игра в гольф тоже хороша :)
Digital Trauma

1
Это на самом деле довольно креативно! Мне это нравится!
Sokie

13

# Javascript

``````(function parse(input) {
var pat = "ze/on/tw/th.?r/fo/fi/ix/se/ei/ni/ten/ele".split("/");
var num = "", last = 0, token = input.replace(/-/g, " ").split(" ");
for(var i in token) {
var t = token[i];
for(var p in pat) if(t.match(RegExp(pat[p])) !== null) num += "+" + p;
if(t.indexOf("een") >= 0) num += "+10";
if(t.indexOf("lve") >= 0) num += "+10";
if(t.indexOf("ty") >= 0) num += "*10";
if(t.indexOf("dr") >= 0) { last = 100; num += "*100"; }
if(t.indexOf("us") >= 0) {
if(last < 1000) num = "(" + num + ")"; last = 0;
num += "*1000";
}
}
})(prompt());``````

Тебе нравится `eval()`?

Запустите этот скрипт в консоли вашего браузера.

Изменить: Спасибо за отзыв. Исправлены ошибки (снова).

действительно хороший код ^^
zsitro

2
Когда вы
наберете

Эта программа не работает для некоторых чисел, начиная с `twelve`момента ее возвращения `23`.
Авраам

Сбой на `"twenty"`.
200_success

`seven thousand three hundred thirty five`дай мне`10335`
Малыш

7

## питон

Просто чтобы мяч катился.

``````import re
table = {'zero':0,'one':1,'two':2,'three':3,'four':4,'five':5,'six':6,'seven':7,'eight':8,'nine':9,
'ten':10,'eleven':11,'twelve':12,'thirteen':13,'fourteen':14,'fifteen':15,'sixteen':16,'seventeen':17,'eighteen':18,'nineteen':19,
'twenty':20,'thirty':30,'forty':40,'fifty':50,'sixty':60,'ninety':90}
modifier = {'hundred':100,'thousand':1000}

while True:
text = raw_input()
result = 0
tmp = 0
last_multiplier = 1
for word in re.split('[- ]', text):
multiplier = modifier.get(word, 1)
if multiplier > last_multiplier:
result = (result+tmp)*multiplier
tmp = 0
else:
tmp *= multiplier
if multiplier != 1:
last_multiplier = multiplier
tmp += table.get(word,0)
print result+tmp``````

5

# Perl + CPAN

Зачем изобретать велосипед, когда это уже сделано?

``````use feature 'say';
use Lingua::EN::Words2Nums;

say words2nums \$_ while <>;``````

Эта программа считывает английские строки из стандартного ввода (или из одного или нескольких файлов, указанных в качестве аргументов командной строки), по одному в строке, и выводит соответствующие числа в стандартный вывод.

Я протестировал этот код, используя как пример входных данных для теста, так и исчерпывающий набор тестов, состоящий из чисел от 0 до 999999, преобразованных в текст с помощью `number`утилиты bsd-games (спасибо, Wander Nauta!), И он правильно анализирует все они. В качестве бонуса он также понимает такие входные данные, как, например, `minus seven`(-7), `four and twenty`(24), `four score and seven`(87), `one gross`(144), `a baker's dozen`(13), `eleventy-one`(111) и `googol`(10 100 ).

( Примечание. В дополнение к самому интерпретатору Perl этой программе также требуется модуль CPAN Lingua :: EN :: Words2Nums . Вот несколько инструкций по установке модулей CPAN . Пользователи Debian / Ubuntu Linux могут также установить этот модуль через менеджер пакетов APT как liblingua-en-words2nums-perl .)

4

# питон

Общее рекурсивное решение с проверкой достоверности. Можно упростить для требуемого диапазона чисел, но, я думаю, здесь можно похвастаться:

``````terms = 'zero one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen'.split()
tee  = 'twenty thirty forty fifty sixty seventy eighty ninety'.split()
for t in tee:
terms.append(t)
for s in terms[1:10]:
terms.append(t+'-'+s)

terms = dict(zip(terms, range(100)))

modifiers = [('hundred', 100), ('thousand', 1000), ('million', 10**6), ('billion', 10**9)]

if len(words) == 0: return 0
elif len(words) == 1:
if words in terms:
return terms[words]
else:
raise ValueError(words+' is not a valid english number.')
else:
for word, value in reversed(modifiers):
if word in words:
i = words.index(word)

raise ValueError(' '.join(words)+' is not a valid english number.')

while True:
try:
except ValueError as e:
print(e)``````

2

## VBScript 474

Это довольно рутинный ответ ... к сожалению, такой рутинный, что @Snack опубликовал тот же процесс, но до меня.

``````i=split(REPLACE(REPLACE(inputbox(""),"lve","een"),"tho","k"))
o=split("z on tw th fo fi si se ei ni ten ele")
y=split("red *100) k )*1000 ty *10) een +10)")
z=""
p=0
for t=0 to UBOUND(i)
s=split(i(t),"-")
u=ubound(s)
r=s(0)
for x=0 to UBOUND(o)
IF INSTR(r,o(x)) THEN
z=z+"+"+CSTR(x)
END IF
IF u Then
IF INSTR(s(1),o(x)) THEN
z=z+CSTR(x)
END IF
END IF
next
for m=0 to UBOUND(y)
IF INSTR(r,y(m))AND u=0 THEN
z=z+y(m+1)
p=p+1
END IF
next
next
Execute("MSGBOX "+String(p,"(")+z)
``````

1

Я думаю, как и в других рекурсивных решениях, но я потратил время на то, чтобы привести его в порядок

Вот полный источник со всеми объяснениями: http://ideone.com/fc8zcB

``````-- Define a type for a parser from a list of tokens to the value they represent.
type NParse = [Token] -> Int

-- Map of literal tokens (0-9, 11-19 and tens) to their names.
literals = [
("zero", 0), ("one", 1), ("two", 2), ("three", 3), ("four", 4), ("five", 5), ("six", 6), ("seven", 7), ("eight", 8), ("nine", 9),
("eleven", 11), ("twelve", 12), ("thirteen", 13), ("fourteen", 14), ("fifteen", 15), ("sixteen", 16), ("seventeen", 17), ("eighteen", 18), ("nineteen", 19),
("ten", 10), ("twenty", 20), ("thirty", 30), ("fourty", 40), ("fifty", 50), ("sixty", 60), ("seventy", 70), ("eighty", 80), ("ninety", 90)
]

-- Splits the input string into tokens.
-- We do one special transformation: replace dshes by a new token. Such that "fifty-three" becomes "fifty tens three".
prepare :: String -> [Token]

-- Let's do the easy stuff and just parse literals first. We just have to look them up in the literals map.
-- This is our base parser.
parseL :: NParse
parseL [tok] = case lookup tok literals of
Just x -> x

-- We're going to exploit the fact that the input strings have a tree-like structure like so
--                    thousand
--          hundred             hundred
--      ten       ten       ten         ten
--    lit   lit lit  lit   lit  lit    lit  lit
-- And recursively parse that tree until we only have literal values.
--
-- When parsing the tree
--       thousand
--     h1       h2
-- The resulting value is 1000 * h1 + h2.
-- And this works similarly for all levels of the tree.
-- So instead of writing specific parsers for all levels, let's just write a generic one :

{- genParse ::
NParse      : the sub parser
-> Int      : the left part multiplier
-> Token    : the boundary token
-> NParse   : returns a new parser -}
genParse :: NParse -> Int -> Token -> NParse
genParse delegate mul tok = newParser where
newParser [] = 0
newParser str = case splitAround str tok of
-- Split around the boundary token, sub-parse the left and right parts, and combine them
(l,r) -> (delegate l) * mul + (delegate r)

-- And so here's the result:
parseNumber :: String -> Int
parseNumber = parseM . prepare
where   -- Here are all intermediary parsers for each level
parseT = genParse   parseL  1       "tens"       -- multiplier is irregular, because the fifty in fifty-three is already multiplied by 10
parseH = genParse   parseT  100     "hundred"
parseK = genParse   parseH  1000    "thousand"
parseM = genParse   parseK  1000000 "million" -- For fun :D

test = (parseNumber "five hundred twenty-three thousand six hundred twelve million two thousand one") == 523612002001
``````

0

# Common Lisp, 94

``(write(cdr(assoc(read-line)(loop for i to 999999 collect(cons(format()"~r"i)i)):test #'equalp)))``

Преобразование числа в текст встроено в CL, но не наоборот. Создает обратное отображение для чисел и проверяет ввод на нем.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.