Convertir inglés a un número [cerrado]


27

Breve y dulce descripción del desafío:

Basado en las ideas de varias otras preguntas en este sitio, su desafío es escribir el código más creativo en cualquier programa que tome como entrada un número escrito en inglés y lo convierta a un número entero.

Especificaciones realmente secas, largas y completas:

  • Su programa recibirá como entrada un número entero en inglés en minúsculas entre zeroe nine hundred ninety-nine thousand nine hundred ninety-nineinclusive.
  • Debe generar solo la forma entera del número entre 0y 999999y nada más (sin espacios en blanco).
  • La entrada NO contendrá ,o and, como en one thousand, two hundredo five hundred and thirty-two.
  • Cuando los lugares de las decenas y unidades son distintos de cero y el lugar de las decenas es mayor que 1, estarán separados por un carácter HYPHEN-MINUS en -lugar de un espacio. Lo mismo para los diez mil y miles de lugares. Por ejemplo, six hundred fifty-four thousand three hundred twenty-one.
  • El programa puede tener un comportamiento indefinido para cualquier otra entrada.

Algunos ejemplos de un programa con buen comportamiento:

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


Esto no es especialmente creativo, ni coincide exactamente con la especificación aquí, pero podría ser útil como punto de partida: github.com/ghewgill/text2num/blob/master/text2num.py
Greg Hewgill

Casi podría publicar mi respuesta a esta pregunta .
grc

¿Por qué hacer un análisis de cadenas complicado? pastebin.com/WyXevnxb
blutorange

1
Por cierto, vi una entrada de IOCCC que es la respuesta a esta pregunta.
Merienda

2
¿Qué pasa con cosas como "cuatro y veinte"?
esponjoso

Respuestas:


93

Applescript

Una combinación tonta y hacky que podría molestar a algunas personas de Cupertino / Mountain View, pero creo que es una combinación creativa tonta y hacky.

set myNumber to text returned of (display dialog ¬
    "Enter number as text:" buttons {"Continue…"} ¬
    default answer "" default button 1)
tell application "Google Chrome"
    activate
    open location "https://www.google.com"
end tell
delay 5
say "ok google. " & myNumber
delay 2
tell application "System Events"
    tell application process "Google Chrome"
        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

Utiliza texto a voz OSX para pronunciar el número de texto y la búsqueda de audio de Google para escucharlo y convertirlo en un número entero.

Requisitos

  • OSX
  • Google Chrome
  • reconocimiento de voz habilitado en su cuenta de google
  • volumen subido a un nivel razonable

Es posible que sea necesario ajustar los tiempos de retraso según el tiempo de carga de Chrome y el tiempo de búsqueda de Google.

Entrada de ejemplo:

ingrese la descripción de la imagen aquí

Salida de ejemplo:

ingrese la descripción de la imagen aquí


13
Yo creo que podría ser sólo un poco creativo ...;)
Abraham

55
Lol, esto es genial
solo el

2
Quizás demasiado creativo.
Cheezey

Después de una semana, su respuesta está claramente a la cabeza con 74 votos, así que creo que eso significa que ... ¡usted gana! Por cierto, ¿te importa si uso este código? ¡Sería realmente útil para muchos proyectos del mundo real en los que estoy trabajando ahora mismo! ;)
Abraham

3
@Abraham ¡Gracias! Estás bromeando acerca de usar esto en el código de producción, ¿verdad?
Trauma digital

34

Bash, 93 64 55 caracteres *

En el fantástico bsd-gamespaquete que está disponible en la mayoría de los sistemas operativos Linux, hay un pequeño juguete de línea de comandos llamado number. Convierte los números en texto en inglés, es decir, hace exactamente lo contrario de esta pregunta. Realmente es exactamente lo contrario: todas las reglas en la pregunta son seguidas por number. Es casi demasiado bueno para ser una coincidencia.

$ number 42
forty-two.

Por supuesto, numberno responde la pregunta. Lo queremos al revés. Pensé en esto por un tiempo, probé el análisis de cadenas y todo eso, luego me di cuenta de que puedo llamar numbera todos los números 999.999 y ver si algo coincide con la entrada. Si es así, la primera línea donde coincide tiene el doble del número de línea que estoy buscando ( numberimprime una línea de puntos después de cada número). Simple como eso. Entonces, sin más preámbulos, aquí está el código completo para mi entrada:

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

Incluso hace un cortocircuito, por lo que convertir "dos" es bastante rápido, e incluso los números más altos generalmente se decodifican en menos de un segundo en mi caja. Aquí hay un ejemplo de ejecución:

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

Por supuesto, necesitará tener numberinstalado para que esto funcione.


*: Sí, lo sé, esto no es un code-golfdesafío, pero la brevedad es la única cualidad de discernimiento de mi entrada, así que ... :)


8
+1. Para mí, usar lo numberinverso es lo más creativo de esta respuesta. El golf también es bueno :)
Digital Trauma

1
¡Esto es realmente bastante creativo! ¡Me gusta!
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";
    }
  }
  alert(eval(num));
})(prompt());

¿Te gustan algunos eval()?

Ejecute este script en la consola de su navegador.

Editar: Gracias por los comentarios. Errores solucionados (de nuevo).


código realmente agradable ^^
zsitro

2
Cuando
escribes

Este programa falla para algunos números que comienzan en twelvecuando regresa 23.
Abraham

Falla en "twenty".
200_success

seven thousand three hundred thirty fivedame10335
Bebé

7

Pitón

Solo para que la pelota ruede.

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

¿Por qué reinventar la rueda, cuando ya se ha hecho?

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

say words2nums $_ while <>;

Este programa lee cadenas en inglés de la entrada estándar (o de uno o más archivos especificados como argumentos de línea de comando), uno por línea, e imprime los números correspondientes a la salida estándar.

He probado este código utilizando las entradas de muestra del desafío, así como un exhaustivo conjunto de pruebas que consta de los números del 0 al 999999 convertidos a texto utilizando la numberutilidad bsd-games (¡gracias, Wander Nauta!), Y se analiza correctamente todos ellos. Como beneficio adicional, también comprende entradas como p minus seven. Ej. (−7), four and twenty(24), four score and seven(87), one gross(144), a baker's dozen(13), eleventy-one(111) y googol(10 100 ).

( Nota: además del propio intérprete de Perl, este programa también requiere el módulo CPAN Lingua :: ES :: Words2Nums . Aquí hay algunas instrucciones para instalar módulos CPAN . Los usuarios de Debian / Ubuntu Linux también pueden instalar este módulo a través del administrador de paquetes APT como liblingua-en-words2nums-perl .)


4

Pitón

Una solución recursiva general, con comprobación de validez. Podría simplificarse para el rango de números requerido, pero supongo que aquí hay que presumir:

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)]

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

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

while True:
    try:
        print(read_num(input().split()))
    except ValueError as e:
        print(e)

2

VBScript 474

Esta es una respuesta bastante rutinaria ... desafortunadamente, es tan rutinaria que @Snack prácticamente publicó el mismo proceso pero antes que yo.

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

Haskell

Supongo que es similar a otras soluciones recursivas, pero me tomé el tiempo para hacerlo limpio.

Aquí está la fuente completa con todas las explicaciones: 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

Lisp común, 94

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

La conversión de número a texto está integrada en CL, pero no al revés. Construye un mapeo inverso para los números y verifica la entrada en él.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.