Convierta el intervalo de tiempo legible por humanos en componentes de fecha

16

Desafío

Escriba el programa más corto que convierta un intervalo de tiempo legible por humanos en componentes de fecha del formulario:

{±YEARS|±MONTHS|±DAYS|±HOURS|±MINUTES|±SECONDS}

Casos de muestra

Cada caso de prueba tiene dos líneas, entrada seguida de salida:

1 year 2 months 3 seconds
{1|2|0|0|0|3}

-2 day 5 year 8months
{5|8|-2|0|0|0}

3day 9     years 4 seconds -5 minute 4 years 4 years -3seconds
{17|0|3|0|-5|1}

Reglas

  • No puede usar strtotimeninguna función incorporada que haga todo el trabajo.
  • El código más corto gana (bytes)
  • Puede imprimir su salida stdouto un archivo, el resultado también puede ser devuelto por una función, depende de usted
  • El token puede estar en forma singular o plural.
  • Los componentes pueden estar en un orden aleatorio
  • Es posible que no haya espacios en blanco entre el número y el token
  • El signo es opcional cuando el intervalo de tiempo es positivo (entrada y salida)
  • Si un componente aparece más de una vez, se deben agregar los valores
  • Cada componente tiene su propio signo
  • Los componentes deben manejarse por separado (por ejemplo, 80 minutespermanece como 80 en la salida)
  • Se garantiza que la entrada sea minúscula

¡Feliz golf!

fpg1503
fuente
2
Me gusta este desafío, pero me cuesta trabajo encontrar algo que no sea largo y desordenado en idiomas que no son adecuados para el golf de código. : /
Alex A.
¿Importa el formato de salida?
Titus
Sign is optional when the time interval is positive¿Eso significa que la entrada puede contener +signos?
Titus

Respuestas:

3

CJam, 60 bytes

Después de estar atrapado en los años 60 durante mucho tiempo, finalmente logré reducir esto a 60 bytes. ¡Suficientemente bueno! ¡Envíalo!

Pruébalo en línea

Aplastado:

'{0a6*q[{_A,s'-+#)!{"ytdhic"#:I){]'0+iA/I_3$=@+t[}*}*}/'|*'}

Ampliado y comentado:

'{              "Add '{' to output";
0a6*            "Initialize time to a list of 6 zeros";
q               "Read the input";
[               "Open an empty numeric character buffer";
{               "For each character in the input:";
  _               "Append the character to the numeric character buffer";
  A,s'-+#)!       "Check if the character is not part of a number";
  {               "If so:";
    "ytdhic"#:I     "Remove the character from the numeric character buffer and
                     convert it to the corresponding time unit index, or -1 if
                     not recognized
                     (Time units are recognized by a character in their name
                     that does not appear before the recognition character
                     in any other name)";
    ){              "Repeat (time unit index + 1) times:";
      ]'0+iA/         "Close the numeric character buffer and parse it as an
                       integer (empty buffer is parsed as 0)";
      I_3$=@+t        "Add the integer to the value of the indexed time unit";
      [               "Open an empty numeric character buffer";
    }*              "End repeat
                     (This is used like an if statement, taking advantage of
                     the fact that iterations after the first have no effect)";
  }*              "End if";
}/              "End for";
'|*             "Insert a '|' between each time unit value (implicitly added to
                 output)";
'}              "Add '}' to output";

Inicialmente comencé a usar un enfoque basado en tokens, pero eso se atascó con bastante firmeza en ... 61 bytes. Suspiro. Así que cambié totalmente de marcha y cambié a este enfoque basado en personajes, que de todos modos es mucho más interesante.

Mi método de análisis funciona agregando todos los caracteres numéricos válidos alcanzados ( 0- 9y -) a un búfer y analizando el búfer como un entero cuando se alcanza un cierto carácter de uno de los nombres de unidades de tiempo. Esos personajes son y, t, d, h, i, yc, que satisfacen las condiciones en que aparecen en un nombre de unidad de tiempo y no aparecen antes del carácter de reconocimiento en ningún otro nombre de unidad de tiempo. En otras palabras, cuando se alcanza uno de estos caracteres de reconocimiento de unidad de tiempo, el búfer numérico se llenará con el último número visto si esto realmente indica una unidad de tiempo, o el búfer numérico estará vacío si esto simplemente aparece, pero no debería señal t, alguna otra unidad de tiempo. En cualquier caso, el búfer numérico se analiza como un entero, o 0 si estaba vacío, y esto se agrega al valor de la unidad de tiempo correspondiente. Por lo tanto, los caracteres de reconocimiento que aparecen en otras unidades de tiempo después de su carácter de reconocimiento no tienen efecto.

Otros hacks locos incluyen:

  • Abusar de los bucles para que los caracteres numéricos se dejen en la pila (que actúa como el búfer de caracteres numéricos) "gratis".
  • Repetir un bloque cero o varias veces en lugar de condicionalmente porque el ciclo es más compacto que una instrucción if, y las iteraciones posteriores a la primera no tienen efecto.

Para cualquiera que tenga curiosidad acerca de mi solución basada en token que se atascó en 61 bytes, también la publicaré aquí. Sin embargo, nunca llegué a expandirlo o comentarlo.

CJam, 61 bytes

'{0a6*q'm-'{,64/~m*{:X/XS**}/S%2/{~0="yodhis"#_3$=@i+t}/'|*'}
Runer112
fuente
+1 Esto definitivamente merece más votos a favor.
oopbase
2
@ Forlan07 Gracias por el apoyo. :) Pero llegué un poco tarde para responder, así que no es inesperado. El proceso de producir esta respuesta fue bastante satisfactorio de todos modos.
Runer112
10

Perl: 61 caracteres

Gracias a @nutki.

s/-?\d+ *m?(.)/$$1+=$&/ge;$_="{y|o|d|h|i|s}";s/\w/${$&}+0/ge

Ejecución de muestra:

bash-4.3$ perl -pe 's/-?\d+ *m?(.)/$$1+=$&/ge;$_="{y|o|d|h|i|s}";s/\w/${$&}+0/ge' <<< '1 year 2 months 3 seconds'
{1|2|0|0|0|3}

bash-4.3$ perl -pe 's/-?\d+ *m?(.)/$$1+=$&/ge;$_="{y|o|d|h|i|s}";s/\w/${$&}+0/ge' <<< '-2 day 5 year 8months'
{5|8|-2|0|0|0}

bash-4.3$ perl -pe 's/-?\d+ *m?(.)/$$1+=$&/ge;$_="{y|o|d|h|i|s}";s/\w/${$&}+0/ge' <<< '3day 9     years 4 seconds -5 minute 4 years 4 years -3seconds'
{17|0|3|0|-5|1}

Mis pobres esfuerzos: 78 77 caracteres

s/([+-]?\d+) *(..)/$a{$2}+=$1/ge;$_="{ye|mo|da|ho|mi|se}";s/\w./$a{$&}||0/ge
hombre trabajando
fuente
1
Algunas mejoras que pude encontrar:s/(-?\d+) *(..)/$$2+=$1/ge;$_="{ye|mo|da|ho|mi|se}";s/\w./${$&}+0/ge
nutki
1
Otros 4 caracteres:s/-?\d+ *(m.|.)/$$1+=$&/ge;$_="{y|mo|d|h|mi|s}";s/\w+/${$&}+0/ge
nutki
Guau. Grandes trucos, @nutki.
manatwork
1
También se encuentra en otras soluciones, (m.|.)-> m?(.)ahorra más 4.
nutki
Doh Eso estaba a punto de probar ahora. Entonces funciona. :)
manatwork
5

Rubí, 119 106 86 85 84 bytes

Un byte guardado gracias a Sp3000.

->i{?{+"yodhis".chars.map{|w|s=0;i.scan(/-?\d+(?= *m?#{w})/){|n|s+=n.to_i};s}*?|+?}}

Esta es una función sin nombre, que toma la entrada como una cadena y devuelve el resultado (también como una cadena). Puede probarlo asignándolo af , digamos, y llamándolo como

f["3day 9     years 4 seconds -5 minute 4 years 4 years -3seconds"]
Martin Ender
fuente
5

Python 2, 99 bytes

import re
f=lambda I:"{%s}"%"|".join(`sum(map(int,re.findall("(-?\d+) *m?"+t,I)))`for t in"yodhis")

Esta es una función lambda que toma una cadena y simplemente usa una expresión regular para extraer los números necesarios.

Gracias a Martin por señalar que \s*podría ser así <space>*. Es fácil olvidar que las expresiones regulares coinciden con espacios literalmente ...

Sp3000
fuente
4

JavaScript 100 105 112

Editar Agregar cadenas de plantillas (implementado por primera vez en diciembre de 2014, tan válido para este desafío): en ese momento no estaba al tanto de ellas

Editar Eureka, ¡por fin entendí el significado de m?todas las otras respuestas!

s=>s.replace(/(-?\d+) *m?(.)/g,(a,b,c)=>o['yodhis'.search(c)]-=-b,o=[0,0,0,0,0,0])&&`{${o.join`|`}}`

Prueba

F=
s=>s.replace(/(-?\d+) *m?(.)/g,(a,b,c)=>o['yodhis'.search(c)]-=-b,o=[0,0,0,0,0,0])&&`{${o.join`|`}}`

;['1 year 2 months 3 seconds','-2 day 5 year 8months'
,'3day 9     years 4 seconds -5 minute 4 years 4 years -3seconds']
.forEach(i=>console.log(i,F(i)))

edc65
fuente
3

R, 197 bytes

Me doy cuenta de que esta no es una entrada competitiva en absoluto, en su mayoría solo quería encontrar una solución en R. Cualquier ayuda para acortar esto, por supuesto, es bienvenida.

function(x){s="{";for(c in strsplit("yodhis","")[[1]])s=paste0(s,ifelse(c=="y","","|"),sum(as.numeric(gsub("[^0-9-]","",str_extract_all(x,perl(paste0("(-?\\d+) *m?",c)))[[1]]))));s=paste0(s,"}");s}

Al igual que la respuesta de Martin, esta es una función sin nombre. Para llamarlo, asígnelo af y pase una cadena.

Esto es bastante horrible, así que echemos un vistazo a una versión sin golf.

function(x) {
    s <- "{"
    for (c in strsplit("yodhis", "")[[1]]) {
        matches <- str_extract_all(x, perl(paste0("(-?\\d+) *m?", c)))[[1]]
        nums <- gsub("[^0-9-]", "", matches)
        y <- sum(as.numeric(nums))
        s <- paste0(s, ifelse(c == "y", "", "|"), y)
    }
    s <- paste0(s, "}")
    return(s)
}

Basándonos solo en la estructura, es fácil ver lo que está sucediendo, incluso si no estás muy familiarizado con R. Explicaré algunos de los aspectos más extraños.

paste0() así es como R combina cadenas sin separador.

La str_extract_all()función proviene del stringrpaquete de Hadley Wickham . El manejo de R de las expresiones regulares en el paquete base deja mucho que desear, que es donde stringrentra. Esta función devuelve una lista de coincidencias de expresiones regulares en la cadena de entrada. Observe cómo la expresión regular está rodeada en una función: perl()esto solo dice que la expresión regular es de estilo Perl, no de estilo R.

gsub()realiza una búsqueda y reemplazo utilizando una expresión regular para cada elemento del vector de entrada. Aquí le estamos diciendo que reemplace todo lo que no sea un número o un signo menos con una cadena vacía.

Y ahí lo tienes. Se proporcionará una explicación adicional con gusto a pedido.

Alex A.
fuente
No creo que la extracción de cadenas de outsourcing a un paquete externo sea una buena idea. ¿No es una laguna cuando se usa una biblioteca externa compatible con la comunidad? Incluso si está bien, ¿por qué no lo incluiste library(stringr)en tu fuente?
Andreï Kostyrka
2

Cobra - 165

def f(s='')
    l=int[](6)
    for i in 6,for n in RegularExpressions.Regex.matches(s,'(-?\\d+) *m?['yodhis'[i]]'),l[i]+=int.parse('[n.groups[1]]')
    print'{[l.join('|')]}'
Οurous
fuente
2

C ++ 14, 234 229 bytes

Editar: reduzca 5 bytes utilizando una declaración de estilo antiguo en lugar deauto.

Sé que el ganador ya ha sido elegido, y que esta sería la presentación más larga hasta ahora, pero solo tuve que publicar una solución C ++, porque apuesto a que nadie esperaba una en absoluto :)

Para ser honesto, estoy bastante contento con lo corto que resultó ser (por mediciones de C ++, por supuesto), y estoy seguro de que no puede ser más corto que esto (con solo un comentario, ver a continuación) . También es una buena colección de características nuevas para C ++ 11/14.

Aquí no hay bibliotecas de terceros, solo se usa la biblioteca estándar.

La solución está en forma de función lambda:

[](auto&s){sregex_iterator e;auto r="{"s;for(auto&t:{"y","mo","d","h","mi","s"}){int a=0;regex g("-?\\d+ *"s+t);decltype(e)i(begin(s),end(s),g);for_each(i,e,[&](auto&b){a+=stoi(b.str());});r+=to_string(a)+"|";}r.back()='}';s=r;};

Sin golf:

[](auto&s)
{
    sregex_iterator e;
    auto r="{"s;
    for(auto&t:{"y","mo","d","h","mi","s"})
    {
        int a=0;
        regex g("-?\\d+\\s*"s+t);
        decltype(e)i(begin(s),end(s),g);
        for_each(i,e,[&](auto&b)
        {
            a+=stoi(b.str());
        });
        r+=to_string(a)+"|";
    }
    r.back()='}';
    s=r;
}

Por alguna razón, tuve que escribir

regex g("-?\\d+\\s*"s+t);
decltype(e)i(begin(s),end(s),g);

en lugar de solo

decltype(e)i(begin(s),end(s),regex("-?\\d+\\s*"s+t));

porque el iterador solo devolvería una coincidencia si paso un objeto temporal. Esto no me parece correcto, así que me pregunto si hay un problema con la implementación de expresiones regulares de GCC.

Archivo de prueba completo (compilado con GCC 4.9.2 con -std=c++14):

#include <iostream>
#include <string>
#include <regex>

using namespace std;

int main()
{
    string arr[] = {"1 year 2 months 3 seconds",
                    "-2 day 5 year 8months",
                    "3day 9     years 4 seconds -5 minute 4 years 4 years -3seconds"};
    for_each(begin(arr), end(arr), [](auto&s){sregex_iterator e;auto r="{"s;for(auto&t:{"y","mo","d","h","mi","s"}){int a=0;auto g=regex("-?\\d+ *"s+t);decltype(e)i(begin(s),end(s),g);for_each(i,e,[&](auto&b){a+=stoi(b.str());});r+=to_string(a)+"|";}r.back()='}';s=r;});
    for(auto &s : arr) {cout << s << endl;}
}

Salida:

{1|2|0|0|0|3}
{5|8|-2|0|0|0}
{17|0|3|0|-5|1}
Alexander Revo
fuente
0

PHP, 141 bytes

preg_match_all("#(.?\d+)\s*m?(.)#",$argv[1],$m);$r=[0,0,0,0,0,0];foreach($m[1]as$i=>$n)$r[strpos(yodhis,$m[2][$i])]+=$n;echo json_encode($r);

toma la entrada del primer argumento de la línea de comando; utiliza [,]para salida en lugar de {|}. Corre con -r.

Descompostura

preg_match_all("#(.?\d+)\s*m?(.)#",$argv[1],$m);    # find intervals.
# (The initial dot will match the sign, the space before the number or a first digit.)
$r=[0,0,0,0,0,0];                   # init result
foreach($m[1]as$i=>$n)              # loop through matches
    $r[strpos(yodhis,$m[2][$i])]+=$n;   # map token to result index, increase value
echo json_encode($r);               # print result: "[1,2,3,4,5,6]"
Titus
fuente