Конвертировать удобочитаемый интервал времени в компоненты даты

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 на выходе)
  • Ввод гарантированно будет в нижнем регистре

Счастливого гольфа!

fpg1503
источник
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, которые все удовлетворяют условиям, что они появляются в имени единицы времени и не появляются перед символом распознавания в любом другом имени единицы времени. Другими словами, когда достигается один из этих символов распознавания единиц времени, числовой буфер будет заполнен последним увиденным числом, если это фактически указывает на единицу времени, или числовой буфер будет пуст, если он только появляется, но не должен сигнал, некоторые другие единицы времени. В любом случае числовой буфер анализируется как целое число или 0, если он был пустым, и это добавляется к соответствующему значению единицы времени. Таким образом, символы распознавания, появляющиеся в других единицах времени после символа распознавания, не имеют никакого эффекта.

Другие сумасшедшие хаки включают в себя:

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

Для всех, кто интересуется моим решением на основе токенов, которое застряло на 61 байте, я также опубликую его здесь. Я никогда не удосужился расширить или прокомментировать это.

CJam, 61 байт

'{0a6*q'm-'{,64/~m*{:X/XS**}/S%2/{~0="yodhis"#_3$=@i+t}/'|*'}
Runer112
источник
+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
manatwork
источник
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.
manatwork
1
Также найдено в других решениях, (m.|.)-> m?(.)сохраняет дополнительные 4.
Nutki
Doh. Это было уже сейчас, чтобы попробовать. Так что это работает. :)
manatwork
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>*. Легко забыть, что регулярные выражения соответствуют пробелам буквально ...

Sp3000
источник
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)))

edc65
источник
3

R 197 байт

Я понимаю, что это не конкурентная запись вообще, я просто хотел найти решение в R. Любая помощь, сокращающая это, конечно, приветствуется.

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

На основе одной структуры легко увидеть, что происходит, даже если вы не слишком знакомы с R. Я подробно остановлюсь на некоторых аспектах, которые выглядят незнакомцами.

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('|')]}'
Οurous
источник
2

C ++ 14, 234 229 байт

Редактировать: сократить 5 байтов, используя объявление старого стиля вместоauto.

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

Честно говоря, я очень доволен тем, насколько коротким он оказался (по меркам 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;};

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

По какой-то причине мне пришлось написать

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]"
Titus
источник