将人类可读的时间间隔转换为日期分量


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
我喜欢这个挑战,但是我很难解决不适合代码高尔夫的语言中冗长且混乱的问题。:/
Alex A.

输出格式重要吗?
泰特斯

Sign is optional when the time interval is positive这是否意味着输入内容可能包含+符号?
泰特斯

Answers:


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-)添加到缓冲区中,并在到达某个时间单位名称之一中的某个字符时将缓冲区解析为整数。这些字符ytdhi,和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 2015年

1
另外4个字符:s/-?\d+ *(m.|.)/$$1+=$&/ge;$_="{y|mo|d|h|mi|s}";s/\w+/${$&}+0/ge
nutki

哇。很棒的技巧,@ nutki。
manatwork 2015年

1
在其它解决方案还发现,(m.|.)- > m?(.)节省了额外的4
nutki

h 那现在要尝试。这样就行了。:)
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")

这是一个lambda函数,它接受一个字符串,并仅使用一个正则表达式来提取必要的数字。

感谢马丁指出这\s*可能是正确的<space>*。很容易忘记正则表达式从字面上匹配空格...


4

的JavaScript 100105112

编辑添加模板字符串(2014年12月首次实施,因此对于此挑战有效)-当时我还不知道它们

编辑尤里卡(Eureka),最后我得到了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个字节

我意识到这根本不是一个竞争产品,我主要只是想提出一个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 Wickham的stringr软件包。R在基本程序包中对正则表达式的处理还有很多需要改进的地方,这就是其中的地方stringr。此函数返回输入字符串中正则表达式匹配项的列表。注意正则表达式如何被函数perl()包围-这只是说正则表达式是Perl样式,而不是R样式。

gsub()使用正则表达式为输入向量的每个元素执行查找和替换。在这里,我们告诉它用空字符串替换不是数字或减号的所有内容。

那里有。如有要求,将乐意提供进一步的解释。


我认为将字符串提取外包到外部软件包不是一个好主意。使用外部社区支持的库不是一个漏洞吗?即使可以,为什么还没有包含library(stringr)在源代码中?
安德烈·科斯蒂卡(AndreïKostyrka)

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 ++测量),而且我敢肯定,它不会再短了(仅一句话,请参阅下文) 。它也是C ++ 11/14新增功能的不错集合。

这里没有第三方库,仅使用标准库。

该解决方案采用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;};

取消高尔夫:

[](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]"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.