Перетворити людський читаний проміжок часу в компоненти дати


16

Виклик

Напишіть найкоротшу програму, яка перетворює читаний людиною часовий інтервал у компоненти форми форми:

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

Зразки кейсів

Кожен тестовий випадок - це два рядки, введення яких супроводжує вихід:

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}

Правила

  • Не можна використовувати strtotimeабо будь-яку вбудовану функцію, яка виконує всю роботу.
  • Найкоротший виграш коду (байти)
  • Ви можете надрукувати вихідний stdoutфайл або файл, результат також можна повернути функцією, це залежить від вас
  • Маркер може бути у формі однини чи множини.
  • Компоненти можуть бути у випадковому порядку
  • Між цифрою та маркером може не бути пробілів
  • Знак необов’язковий, коли інтервал часу позитивний (введення та вихід)
  • Якщо компонент відображається більше одного разу, значення слід додати
  • Кожен компонент має свій знак
  • Компоненти слід обробляти окремо (наприклад 80 minutes, у виході залишається 80)
  • Гарантія введення буде нижчою літерою

Щасливого гольфу!


2
Мені подобається цей виклик, але мені важко придумувати все, що недовго і безладно в мовах, які не підходять для кодового гольфу. : /
Олексій А.

Чи має значення вихідний формат?
Тит

Sign is optional when the time interval is positiveЧи означає це, що вхід може містити +знаки?
Тит

Відповіді:


3

CJam, 60 байт

Затримавшись у 60-х на тривалий час, я нарешті зумів стиснути це до 60 байт. Досить добре! Відправте його!

Спробуйте в Інтернеті

Похитували:

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

Розширено та прокоментовано:

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

Я спочатку почав використовувати підхід на основі лексеми, але він досить міцно застряг у ... 61 байт. Зітхнути. Тож я повністю змінив передачу і перейшов до цього підходу, заснованого на характері, який все-таки є цікавішим.

Мій метод розбору працює, додаючи будь-які дійсні числові символи, досягнуті ( 0- 9і -) до буфера, і аналізує буфер як ціле число, коли досягається певний символ з одного з назв одиниць часу. Ці символи y, t, d, h, i, іc, які всі відповідають умовам, які вони відображаються у назві одиниці часу та не відображаються перед символом розпізнавання в будь-якому іншому імені одиниці часу. Іншими словами, коли буде досягнуто одного з цих символів розпізнавання одиниць часу, цифровий буфер буде заповнений останнім числом, яке побачили, якщо це насправді сигналізує одиницю часу, або числовий буфер буде порожнім, якщо це просто відображається, але не повинно ' t сигнал, якась інша одиниця часу. У будь-якому випадку числовий буфер аналізується як ціле число, або 0, якщо воно було порожнім, і це додається до відповідного значення одиниці часу. Таким чином, символи розпізнавання, що з’являються в інших одиницях часу після їх розпізнавання, не впливають.

Інші шалені хаки включають:

  • Зловживання циклами, таким чином, числові символи залишаються на стеку (який виконує функції буфера числових символів) "безкоштовно".
  • Повторення блоку нульового або декількох разів замість умовного, тому що цикл є більш компактним, ніж оператор if, а ітерації після першого не мають ефекту.

Для всіх, хто цікавиться моїм токеновим рішенням, яке застрягло в 61 байті, я також розміщу його тут. Мені ніколи не доводилося розширювати або коментувати це.

CJam, 61 байт

'{0a6*q'm-'{,64/~m*{:X/XS**}/S%2/{~0="yodhis"#_3$=@i+t}/'|*'}

+1 Це, безумовно, має більше грошей.
oopbase

2
@ Forlan07 Дякую за підтримку. :) Але я трохи запізнився відповісти, так що це не несподівано. Процес отримання цієї відповіді в будь-якому випадку був достатньо задоволений.
Runer112

10

Perl: 61 символ

Дякуємо @nutki.

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

Проба зразка:

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}

Мої бідні зусилля: 78 77 символів

s/([+-]?\d+) *(..)/$a{$2}+=$1/ge;$_="{ye|mo|da|ho|mi|se}";s/\w./$a{$&}||0/ge

1
Деякі вдосконалення я міг знайти:s/(-?\d+) *(..)/$$2+=$1/ge;$_="{ye|mo|da|ho|mi|se}";s/\w./${$&}+0/ge
nutki

1
Ще 4 символи:s/-?\d+ *(m.|.)/$$1+=$&/ge;$_="{y|mo|d|h|mi|s}";s/\w+/${$&}+0/ge
nutki

Ого. Чудові хитрощі, @nutki.
манатура

1
Також знайдені в інших рішеннях, (m.|.)-> m?(.)економить зайві 4.
nutki

До. Це було вже зараз спробувати. Так це працює. :)
манатура

5

Рубі, 119 106 86 85 84 байт

Один байт збережено завдяки Sp3000.

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

Це неназвана функція, яка приймає введення як рядок і повертає результат (також як рядок). Ви можете перевірити його, призначивши його f, скажімо, і називати його так

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

5

Python 2, 99 байт

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

Це лямбда-функція, яка займає рядок і просто використовує регулярний вираз для отримання необхідних чисел.

Дякую Мартіну за те, що він зазначив, що \s*це просто може бути <space>*. Легко забути, що реджекси відповідають простору буквально ...


4

JavaScript 100 105 112

Редагувати Додавання рядків шаблонів (перший внесений грудень 2014 р., Настільки дійсний для цього виклику) - я тоді про них не знав

Редагуйте Еврика, нарешті я отримав сенс m?у всіх інших відповідях!

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

Тест

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


3

R, 197 байт

Я усвідомлюю, що це зовсім не конкурсна заявка, я здебільшого просто хотів знайти рішення в Р. Будь-яка допомога скорочення цього, звичайно, вітається.

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}

Як і відповідь Мартіна, це неназвана функція. Щоб викликати його, призначте його fта передайте рядок.

Це досить прикро, тому давайте подивимось на версію, яка не перебуває у гольфі.

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

Тільки на основі структури легко зрозуміти, що відбувається, навіть якщо ви не надто знайомі з Р. Я детально розглежу деякі аспекти, що дивляться на незнайомці.

paste0() так R поєднує рядки без роздільника.

str_extract_all()Функція виходить від Hadley Уікхем stringrпакета. Робота з регулярними виразами R в базовому пакеті залишає бажати кращого, саме тут і stringrвходить. Ця функція повертає список відповідних регулярних виразів у рядок введення. Зверніть увагу, як регекс оточений у функціїperl() - це просто говорить про те, що регулярний вираз є стилем Perl, а не R-стилем.

gsub()робить знаходження та заміну, використовуючи регулярний вираз для кожного елемента вхідного вектора. Тут ми говоримо про те, щоб замінити все, що не є знаком числа або мінусу, порожнім рядком.

І ось у вас це є. Подальші роз'яснення з радістю будуть надані на запит.


Я не думаю, що аутсорсинг витягування рядків у зовнішній пакет є гарною ідеєю. Чи це не лазівка, коли використовується зовнішня бібліотека, що підтримується спільнотою? Навіть якщо це нормально, чому ви не включили library(stringr)до свого джерела?
Андрей Костирка

2

Кобра - 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('|')]}'

2

C ++ 14, 234 229 байт

Редагувати: скоротити 5 байтів, використовуючи декларацію про старий стиль замістьauto.

Я знаю, що переможця вже обрано, і що це було б найдовше подання на даний момент, але я просто повинен був опублікувати рішення C ++, тому що я ставлю на облік, що його ніхто не очікував :)

Якщо чесно, я дуже задоволений тим, наскільки коротко це виявилося (звичайно, за допомогою вимірювань С ++), і я впевнений, що він не може отримати коротше, ніж це (лише з одним зауваженням, див. Нижче) . Це також приємна колекція функцій, нових для C ++ 11/14.

Тут немає сторонніх бібліотек, використовується лише стандартна бібліотека.

Рішення складається у формі лямбда-функції:

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

Безголівки:

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

Мені чомусь довелося писати

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

замість просто

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

тому що ітератор поверне лише одну відповідність, якщо я передаю тимчасовий об'єкт. Це не здається мені правильним, тому мені цікаво, чи є проблема з реалізацією регулярних виразів GCC.

Повний тестовий файл (зібраний з GCC 4.9.2 з -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;}
}

Вихід:

{1|2|0|0|0|3}
{5|8|-2|0|0|0}
{17|0|3|0|-5|1}

0

PHP, 141 байт

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

приймає дані з аргументу першого командного рядка; використовує [,]для виводу замість {|}. Бігайте з -r.

зламатися

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]"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.