Konwertuj czytelny dla człowieka przedział czasu na składniki daty

16

Wyzwanie

Napisz najkrótszy program, który konwertuje czytelny dla człowieka przedział czasu na datę składników formularza:

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

Przykładowe przypadki

Każdy przypadek testowy ma dwa wiersze, dane wejściowe i dane wyjściowe:

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}

Zasady

  • Nie można użyć strtotimeani żadnej wbudowanej funkcji, która wykonuje całą pracę.
  • Najkrótszy kod wygrywa (bajty)
  • Możesz wydrukować swój wynik stdoutlub plik, wynik może być również zwrócony przez funkcję, to zależy od Ciebie
  • Token może być w liczbie pojedynczej lub mnogiej.
  • Komponenty mogą być w losowej kolejności
  • Pomiędzy liczbą a tokenem może nie być białych znaków
  • Znak jest opcjonalny, gdy przedział czasu jest dodatni (wejście i wyjście)
  • Jeśli składnik pojawia się więcej niż jeden raz, należy dodać wartości
  • Każdy element ma swój własny znak
  • Elementy należy traktować osobno (np. 80 minutesPozostaje na wyjściu jako 80)
  • Dane wejściowe są gwarantowane małymi literami

Wesołego golfa!

fpg1503
źródło
2
Lubię to wyzwanie, ale ciężko mi wymyślić coś, co nie jest długie i nieporządne w językach nieodpowiednich do gry w golfa kodowego. : /
Alex A.
Czy format wyjściowy ma znaczenie?
Tytus
Sign is optional when the time interval is positiveCzy to oznacza, że ​​dane wejściowe mogą zawierać +znaki?
Tytus

Odpowiedzi:

3

CJam, 60 bajtów

Po tym, jak utknąłem w latach 60-tych, w końcu udało mi się zmniejszyć to do 60 bajtów. Wystarczająco dobry! Wyślij to!

Wypróbuj online

Squished:

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

Rozszerzony i skomentowany:

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

Początkowo zacząłem stosować podejście oparte na tokenach, ale utknęło to dość mocno w ... 61 bajtach. Westchnienie. Więc całkowicie zmieniłem biegi i przeszedłem na podejście oparte na postaciach, które i tak jest o wiele bardziej interesujące.

Moja metoda analizowania polega na dodawaniu do bufora dowolnych osiągniętych znaków numerycznych ( 0- 9i -) oraz analizowaniu bufora jako liczby całkowitej po osiągnięciu określonego znaku z jednej z nazw jednostek czasu. Te znaki są y, t, d, h, i, ic, które wszystkie spełniają warunki, które pojawiają się w nazwie jednostki czasu i nie pojawiają się przed znakiem rozpoznawczym w żadnej innej nazwie jednostki czasu. Innymi słowy, gdy osiągnięty zostanie jeden z tych znaków rozpoznawania jednostki czasu, bufor numeryczny zostanie wypełniony ostatnią widzianą liczbą, jeśli faktycznie oznacza jednostkę czasu, lub bufor numeryczny będzie pusty, jeśli pojawi się on, ale nie powinien sygnał t, inna jednostka czasu. W obu przypadkach bufor numeryczny jest analizowany jako liczba całkowita lub 0, jeśli był pusty, i jest dodawany do odpowiedniej wartości jednostki czasu. Tak więc znaki rozpoznające pojawiające się w innych jednostkach czasu po znaku rozpoznającym nie mają żadnego efektu.

Inne szalone hacki to:

  • Nadużywanie pętli, tak aby znaki numeryczne pozostały na stosie (który działa jak bufor znaków numerycznych) „za darmo”.
  • Powtarzanie bloku zero lub wiele razy zamiast warunkowo, ponieważ pętla jest bardziej zwarta niż instrukcja if, a iteracje po pierwszej nie mają żadnego efektu.

Dla każdego, kto jest ciekawy mojego rozwiązania opartego na tokenach, które utknęło na 61 bajtach, opublikuję to tutaj. Jednak nigdy nie starałem się go rozszerzać ani komentować.

CJam, 61 bajtów

'{0a6*q'm-'{,64/~m*{:X/XS**}/S%2/{~0="yodhis"#_3$=@i+t}/'|*'}
Runer112
źródło
+1 To zdecydowanie dezorientuje więcej głosów pozytywnych.
oopbase
2
@ Forlan07 Dzięki za wsparcie. :) Ale trochę spóźniłem się z odpowiedzią, więc nie jest to nieoczekiwane. Proces uzyskania tej odpowiedzi i tak był wystarczająco satysfakcjonujący.
Runer112
10

Perl: 61 znaków

Dzięki @nutki.

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

Przykładowy przebieg:

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}

Moje słabe wysiłki: 78 77 znaków

s/([+-]?\d+) *(..)/$a{$2}+=$1/ge;$_="{ye|mo|da|ho|mi|se}";s/\w./$a{$&}||0/ge
człowiek w pracy
źródło
1
Kilka ulepszeń, które udało mi się znaleźć:s/(-?\d+) *(..)/$$2+=$1/ge;$_="{ye|mo|da|ho|mi|se}";s/\w./${$&}+0/ge
nutki
1
Kolejne 4 znaki:s/-?\d+ *(m.|.)/$$1+=$&/ge;$_="{y|mo|d|h|mi|s}";s/\w+/${$&}+0/ge
nutki
Łał. Świetne sztuczki, @nutki.
manatwork
1
Znaleziony również w innych rozwiązaniach, (m.|.)-> m?(.)oszczędza dodatkowe 4.
nutki
Doh To miało się teraz wypróbować. Więc to działa. :)
manatwork
5

Ruby, 119 106 86 85 84 bajtów

Jeden bajt zapisany dzięki Sp3000.

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

Jest to funkcja bez nazwy, która przyjmuje dane wejściowe jako ciąg znaków i zwraca wynik (również jako ciąg znaków). Możesz to przetestować, przypisując go f, powiedzmy, i nazywając tak

f["3day 9     years 4 seconds -5 minute 4 years 4 years -3seconds"]
Martin Ender
źródło
5

Python 2, 99 bajtów

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

Jest to funkcja lambda, która pobiera ciąg znaków i po prostu używa wyrażenia regularnego, aby wyodrębnić niezbędne liczby.

Dzięki Martinowi za wskazanie, że to \s*może być po prostu <space>*. Łatwo zapomnieć, że wyrażenia regularne pasują do spacji dosłownie ...

Sp3000
źródło
4

JavaScript 100 105 112

Edytuj Dodawanie ciągów szablonów (pierwszy wdrożony grudzień 2014, tak ważny dla tego wyzwania) - wtedy ich nie znałem

Edytuj Eurekę, w końcu zrozumiałem m?wszystkie pozostałe odpowiedzi!

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

Test

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
źródło
3

R 197 bajtów

Zdaję sobie sprawę, że to wcale nie jest konkurencja, w większości chciałem tylko znaleźć rozwiązanie w R. Wszelkie skrócenie pomocy jest oczywiście mile widziane.

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}

Podobnie jak odpowiedź Martina, jest to funkcja bez nazwy. Aby to nazwać, przypisz go dof i przekaż ciąg.

Jest to dość ohydne, więc spójrzmy na wersję bez gry w golfa.

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

W oparciu o samą strukturę łatwo jest zobaczyć, co się dzieje, nawet jeśli nie jesteś zbyt zaznajomiony z R. Omówię niektóre z dziwniejszych aspektów.

paste0() jest jak R łączy ciągi bez separatora.

str_extract_all()Funkcja pochodzi z Hadley Wickhama stringropakowaniu. Obsługa wyrażeń regularnych przez R w pakiecie podstawowym pozostawia wiele do życzenia, i to tam, gdzie stringrprzychodzi. Ta funkcja zwraca listę dopasowań wyrażeń regularnych w ciągu wejściowym. Zauważ, jak regex jest otoczony funkcją - perl()to tylko mówi, że regex jest w stylu Perla, a nie w stylu R.

gsub()wykonuje wyszukiwanie i zamianę za pomocą wyrażenia regularnego dla każdego elementu wektora wejściowego. Mówimy tutaj, aby zastąpić wszystko, co nie jest liczbą lub znakiem minus, pustym ciągiem.

I masz to. Dalsze wyjaśnienia z przyjemnością udzielimy na żądanie.

Alex A.
źródło
Nie sądzę, aby outsourcing ekstrakcji ciągów do zewnętrznego pakietu był dobrym pomysłem. Czy nie jest to luka, gdy używana jest zewnętrzna biblioteka wspierana przez społeczność? Nawet jeśli jest to w porządku, dlaczego nie umieściłeś library(stringr)w swoim źródle?
Andreï Kostyrka,
2

Kobra - 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('|')]}'
Obrzydliwe
źródło
2

C ++ 14, 234 229 bajtów

Edytować: zmniejsz 5 bajtów, używając deklaracji w starym stylu zamiast auto.

Wiem, że zwycięzca został już wybrany i że będzie to najdłuższy jak dotąd zgłoszenie, ale po prostu musiałem opublikować rozwiązanie C ++, ponieważ założę się, że nikt się w ogóle nie spodziewał :)

Szczerze mówiąc, jestem bardzo zadowolony z tego, jak krótki okazał się (według pomiarów C ++, oczywiście) i jestem pewien, że nie może być krótszy (tylko z jedną uwagą, patrz poniżej) . Jest to także całkiem niezła kolekcja nowych funkcji w C ++ 11/14.

Nie ma tu bibliotek stron trzecich, używana jest tylko biblioteka standardowa.

Rozwiązanie ma postać funkcji 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;};

Nie golfowany:

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

Z jakiegoś powodu musiałem pisać

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

zamiast po prostu

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

ponieważ iterator zwróciłby tylko jedno dopasowanie, jeśli przekażę obiekt tymczasowy. Nie wydaje mi się to właściwe, więc zastanawiam się, czy jest problem z implementacją wyrażenia regularnego GCC.

Pełny plik testowy (skompilowany z GCC 4.9.2 z -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;}
}

Wynik:

{1|2|0|0|0|3}
{5|8|-2|0|0|0}
{17|0|3|0|-5|1}
Alexander Revo
źródło
0

PHP, 141 bajtów

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

pobiera dane wejściowe z argumentu pierwszego wiersza poleceń; używa [,]dla danych wyjściowych zamiast {|}. Uruchom z -r.

awaria

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]"
Tytus
źródło