Konvertiert vom Menschen lesbare Zeitintervalle in Datumskomponenten

16

Herausforderung

Schreiben Sie das kürzeste Programm, das ein vom Menschen lesbares Zeitintervall in Datumskomponenten des Formulars konvertiert:

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

Beispielfälle

Jeder Testfall besteht aus zwei Zeilen, Eingabe gefolgt von Ausgabe:

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}

Regeln

  • Sie können strtotimekeine integrierte Funktion verwenden, die die gesamte Arbeit erledigt.
  • Kürzester Code gewinnt (Bytes)
  • Sie können Ihre Ausgabe in stdoutoder in eine Datei drucken , das Ergebnis kann auch von einer Funktion zurückgegeben werden, es liegt an Ihnen
  • Das Token kann in Singular- oder Pluralform vorliegen.
  • Die Komponenten können in zufälliger Reihenfolge sein
  • Zwischen der Nummer und dem Token darf kein Leerzeichen sein
  • Das Vorzeichen ist optional, wenn das Zeitintervall positiv ist (Eingabe und Ausgabe).
  • Wenn eine Komponente mehrmals vorkommt, sollten die Werte hinzugefügt werden
  • Jede Komponente hat ein eigenes Vorzeichen
  • Die Komponenten sollten separat behandelt werden (zB 80 minutesbleibt als 80 in der Ausgabe)
  • Die Eingabe erfolgt garantiert in Kleinbuchstaben

Viel Spaß beim Golfen!

fpg1503
quelle
2
Ich mag diese Herausforderung, aber es fällt mir schwer, irgendetwas zu finden, das in Sprachen, die für Codegolf ungeeignet sind, nicht lang und chaotisch ist. : /
Alex A.
Ist das Ausgabeformat wichtig?
Titus
Sign is optional when the time interval is positiveBedeutet das, dass die Eingabe +Zeichen enthalten kann ?
Titus

Antworten:

3

CJam, 60 Bytes

Nachdem ich lange Zeit in den 60ern feststeckte, schaffte ich es endlich, dies auf 60 Bytes zu reduzieren. Gut genug! Es versenden!

Probieren Sie es online aus

Gequetscht:

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

Erweitert und kommentiert:

'{              "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";

Ich habe anfangs mit einem tokenbasierten Ansatz begonnen, aber der blieb bei ... 61 Bytes ziemlich fest. Seufzer. Also habe ich die Gänge total gewechselt und zu diesem charakterbasierten Ansatz gewechselt, der sowieso viel interessanter ist.

Meine Parsing-Methode fügt alle gültigen numerischen Zeichen ( 0- 9und -) zu einem Puffer hinzu und analysiert den Puffer als Ganzzahl, wenn ein bestimmtes Zeichen aus einem der Zeiteinheitennamen erreicht ist. Diese Zeichen sind y, t, d, h, i, undc, die alle die Bedingungen erfüllen, die sie in einem Zeiteinheitennamen enthalten und in keinem anderen Zeiteinheitennamen vor dem Erkennungszeichen stehen. Mit anderen Worten, wenn eines dieser Zeichen für die Erkennung von Zeiteinheiten erreicht ist, wird der numerische Puffer mit der zuletzt gesehenen Zahl gefüllt, wenn dies tatsächlich eine Zeiteinheit signalisiert, oder der numerische Puffer ist leer, wenn dies nur in angezeigt wird, sollte aber nicht t Signal, eine andere Zeiteinheit. In beiden Fällen wird der numerische Puffer als Ganzzahl oder 0, wenn er leer war, analysiert und zum entsprechenden Zeiteinheitswert addiert. Somit haben Erkennungszeichen, die in anderen Zeiteinheiten nach ihrem Erkennungszeichen erscheinen, keine Wirkung.

Andere verrückte Hacks sind:

  • Durch den Missbrauch von Schleifen bleiben numerische Zeichen "kostenlos" auf dem Stapel (der als Puffer für numerische Zeichen fungiert).
  • Das null- oder mehrmalige Wiederholen eines Blocks anstelle einer bedingten Wiederholung, da die Schleife kompakter ist als eine if-Anweisung, und Iterationen nach der ersten haben keine Wirkung.

Für alle, die neugierig auf meine tokenbasierte Lösung sind, die bei 61 Byte hängen geblieben ist, werde ich sie auch hier veröffentlichen. Ich bin jedoch nie dazu gekommen, es zu erweitern oder zu kommentieren.

CJam, 61 Bytes

'{0a6*q'm-'{,64/~m*{:X/XS**}/S%2/{~0="yodhis"#_3$=@i+t}/'|*'}
Runer112
quelle
+1 Dies verdient definitiv mehr Stimmen.
oopbase
2
@ Forlan07 Danke für die Unterstützung. :) Aber ich war etwas spät dran, es ist also nicht unerwartet. Der Prozess der Erstellung dieser Antwort war auf jeden Fall zufriedenstellend genug.
Runer112
10

Perl: 61 Zeichen

Vielen Dank an @nutki.

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

Probelauf:

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}

Meine schlechten Bemühungen: 78 77 Zeichen

s/([+-]?\d+) *(..)/$a{$2}+=$1/ge;$_="{ye|mo|da|ho|mi|se}";s/\w./$a{$&}||0/ge
Mann bei der Arbeit
quelle
1
Einige Verbesserungen, die ich finden konnte:s/(-?\d+) *(..)/$$2+=$1/ge;$_="{ye|mo|da|ho|mi|se}";s/\w./${$&}+0/ge
Nutki
1
Weitere 4 Zeichen:s/-?\d+ *(m.|.)/$$1+=$&/ge;$_="{y|mo|d|h|mi|s}";s/\w+/${$&}+0/ge
Nutki
Beeindruckend. Tolle Tricks, @nutki.
Manatwork
1
Auch in anderen Lösungen gefunden, (m.|.)-> m?(.)spart zusätzliche 4.
Nutki
Doh. Das wollte ich gerade ausprobieren. So funktioniert es. :)
manatwork
5

Ruby, 119 106 86 85 84 Bytes

Ein Byte gespart dank Sp3000.

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

Dies ist eine unbenannte Funktion, die die Eingabe als Zeichenfolge verwendet und das Ergebnis (auch als Zeichenfolge) zurückgibt. Sie können es testen, indem Sie es beispielsweise zuweisen fund wie folgt aufrufen

f["3day 9     years 4 seconds -5 minute 4 years 4 years -3seconds"]
Martin Ender
quelle
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")

Dies ist eine Lambda-Funktion, die eine Zeichenfolge aufnimmt und einfach einen regulären Ausdruck verwendet, um die erforderlichen Zahlen zu extrahieren.

Dank Martin für den Hinweis auf , dass \s*gerade sein könnte <space>*. Es ist leicht zu vergessen, dass reguläre Ausdrücke buchstäblich mit Leerzeichen übereinstimmen ...

Sp3000
quelle
4

JavaScript 100 105 112

Bearbeiten Hinzufügen von Template-Strings (erstmalig implementiert im Dezember 2014, also gültig für diese Herausforderung) - zu der Zeit war ich mir dessen nicht bewusst

Edit Eureka, endlich habe ich m?in allen anderen Antworten die Bedeutung verstanden !

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

Prüfung

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
quelle
3

R, 197 Bytes

Mir ist klar, dass dies überhaupt kein Wettbewerbsbeitrag ist. Ich wollte meistens nur eine Lösung für R finden. Jede Hilfe, die dies verkürzt, ist natürlich willkommen.

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}

Wie Martins Antwort ist dies eine unbenannte Funktion. Um es aufzurufen, weisen Sie es zuf und übergeben Sie eine Zeichenfolge.

Das ist ziemlich abscheulich, schauen wir uns also eine ungegolfte Version an.

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

Anhand der Struktur allein ist es leicht zu erkennen, was los ist, auch wenn Sie mit R nicht allzu vertraut sind. Ich werde einige der merkwürdig aussehenden Aspekte näher erläutern.

paste0() So kombiniert R Zeichenfolgen ohne Trennzeichen.

Die str_extract_all()Funktion stammt aus Hadley Wickhams stringrPaket. Rs Umgang mit regulären Ausdrücken im Basispaket lässt zu wünschen übrig, und hier stringrkommt es an. Diese Funktion gibt eine Liste von Übereinstimmungen mit regulären Ausdrücken in der Eingabezeichenfolge zurück. Beachten Sie, wie der perl()reguläre Ausdruck in einer Funktion eingeschlossen ist - dies bedeutet lediglich, dass der reguläre Ausdruck im Perl-Stil und nicht im R-Stil vorliegt.

gsub()führt ein Suchen und Ersetzen durch, wobei ein regulärer Ausdruck für jedes Element des Eingabevektors verwendet wird. Hier sagen wir, dass alles, was weder eine Zahl noch ein Minuszeichen ist, durch eine leere Zeichenfolge ersetzt werden soll.

Und da hast du es. Weitere Erläuterungen erhalten Sie gerne auf Anfrage.

Alex A.
quelle
Ich halte es nicht für eine gute Idee, die String-Extraktion an ein externes Paket auszulagern. Ist es nicht eine Lücke, wenn eine von einer externen Community unterstützte Bibliothek verwendet wird? Auch wenn es in Ordnung ist, warum haben Sie es nicht library(stringr)in Ihre Quelle aufgenommen?
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
quelle
2

C ++ 14, 234 229 Bytes

Bearbeiten: Reduzieren Sie 5 Bytes, indem Sie die alte Deklaration anstelle von verwendenauto.

Ich weiß, dass der Gewinner bereits ausgewählt wurde und dass dies die längste Einreichung bis jetzt sein würde, aber ich musste nur eine C ++ - Lösung posten, weil ich wetten, dass niemand überhaupt eine erwartet hat :)

Um ehrlich zu sein, bin ich ziemlich zufrieden damit, wie kurz es war (natürlich nach C ++ - Messungen), und ich bin mir sicher, dass es nicht kürzer werden kann als dies (mit nur einer Bemerkung, siehe unten). . Es ist auch eine schöne Sammlung von neuen Funktionen in C ++ 11/14.

Hier werden keine Bibliotheken von Drittanbietern verwendet, sondern nur Standardbibliotheken.

Die Lösung liegt in Form einer Lambda-Funktion vor:

[](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;};

Ungolfed:

[](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;
}

Aus irgendeinem Grund musste ich schreiben

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

statt nur

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

weil der Iterator nur eine Übereinstimmung zurückgeben würde, wenn ich ein temporäres Objekt übergeben würde. Dies scheint mir nicht richtig zu sein, daher frage ich mich, ob es ein Problem mit der GCC-Regex-Implementierung gibt.

Vollständige Testdatei (kompiliert mit GCC 4.9.2 mit -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;}
}

Ausgabe:

{1|2|0|0|0|3}
{5|8|-2|0|0|0}
{17|0|3|0|-5|1}
Alexander Revo
quelle
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);

Nimmt Eingaben vom ersten Befehlszeilenargument entgegen; verwendet [,]für die Ausgabe anstelle von {|}. Laufen Sie mit -r.

Nervenzusammenbruch

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
quelle