Perl 6 - 日期難題

reddit 上使用 Perl 6 處理日期

描述


下面的日期, 有些使用了 M D Y 格式, 有些使用了 Y M D 格式, 還使用了任意分隔符! 請把這些散亂的文本解析成合適的 ISO 8601 (YYYY-MM-DD) 格式化日期。

假設(shè)只有以 4 個數(shù)字開頭的日期使用 Y M D 格式, 其它的使用 M D Y 格式扩灯。

輸入樣本


2/13/15
1-31-10
5 10 2015
2012 3 17
2001-01-01
2008/01/07

輸出樣本


2015-02-13
2010-01-31
2015-05-10
2012-03-17
2001-01-01
2008-01-07

擴(kuò)展挑戰(zhàn) [中級]


使用 2014-12-24 作為相對日期的基準(zhǔn)媚赖。

當(dāng)添加 days(天數(shù)) 時, 要考慮到每月會有不同的天數(shù), 忽略閏年。

當(dāng)添加月和年時, 使用整個 units, 以至于:

one month before october 10 is september 10

one year after 2001-04-02 is 2002-04-02

one month after january 30 is february 28 (not march 1)

Sally's inputs:


tomorrow
2010-dec-7
OCT 23
1 week ago
next Monday
last sunDAY
1 year ago
1 month ago
last week
LAST MONTH
10 October 2010
an year ago
2 years from tomoRRow
1 month from 2016-01-31
4 DAYS FROM today
9 weeks from yesterday

Sally's expected outputs:


2014-12-25
2010-12-01
2014-10-23
2014-12-17
2014-12-29
2014-12-21
2013-12-24
2014-11-24
2014-12-15
2014-11-24
2010-10-10
2013-12-24
2016-12-25
2016-02-28
2014-12-28
2015-02-25

smls 大神給出了完整的 grammar:

my $today = Date.new(2014, 12, 24);

grammar MessyDate {
    rule TOP {
        |    <date>                 { make $<date>.made } # 跟在 regex 后面的花括號是閉包
        | :i <duration> ago         { make $today.earlier: |$<duration>.made     }
        | :i <duration> from <date> { make $<date>.made.later: |$<duration>.made }
    }

    rule date {
        | [ || <month> (<sep>?) <day>   [$0 <year>]?
            || <day>   (<sep>?) <month> [$0 <year>]?
            || <year>  (<sep>?) <month>  $0 <day>    ]
          { make Date.new: $<year>.made//$today.year, |$<month day>?.made }

        | :i today          { make $today     }
        | :i yesterday      { make $today - 1 }
        | :i tomorrow       { make $today + 1 }
        | :i last <weekday> { make $today - ($today.day-of-week - $<weekday>.made) % 7 || 7 }
        | :i next <weekday> { make $today + ($<weekday>.made - $today.day-of-week) % 7 || 7 }
        | :i last <unit>    { make $today.earlier: |($<unit>.made => 1) }
        | :i next <unit>    { make $today.later:   |($<unit>.made => 1) }
    }

    rule duration {
        <count> <unit> { make $<unit>.made => $<count>.made }
    }

    token year {
        | <number(4)>        { make +$<number>       } # <number(4)> 是擴(kuò)展的 <...> 語法, 實(shí)際是方法調(diào)用
        | <number(2, 0..49)> { make 2000 + $<number> }
        | <number(2, 50..*)> { make 1900 + $<number> }
    }

    token month {
        | <number(1..2, 1..12)> { make +$<number> }
        | :i Jan[uary]?   { make  1 } # [...] 是非捕獲分組
        | :i Feb[ruary]?  { make  2 }
        | :i Mar[ch]?     { make  3 }
        | :i Apr[il]?     { make  4 }
        | :i May          { make  5 }
        | :i Jun[e]?      { make  6 }
        | :i Jul[y]?      { make  7 }
        | :i Aug[ust]?    { make  8 }
        | :i Sep[tember]? { make  9 }
        | :i Oct[ober]?   { make 10 }
        | :i Nov[ember]?  { make 11 }
        | :i Dec[ember]?  { make 12 }
    }

    token day { <number(1..2, 1..31)> { make +$<number> } }

    token weekday {
        | :i Mon[day]?    { make 1 }
        | :i Tue[sday]?   { make 2 }
        | :i Wed[nesday]? { make 3 }
        | :i Thu[rsday]?  { make 4 }
        | :i Fri[day]?    { make 5 }
        | :i Sat[urday]?  { make 6 }
        | :i Sun[day]?    { make 7 }
    }

    token sep   { <[-/.\h]> } # <[...]> 是 Perl 6 中的字符類
    token count { (<[0..9]>+) { make +$0 }  |  an? { make 1 } }
    token unit  { :i (day|week|month|year) s? { make $0.lc } }

    multi token number ($digits)        {  <[0..9]> ** {$digits} }
    multi token number ($digits, $test) { (<[0..9]> ** {$digits}) <?{ +$0 ~~ $test }> }
}

for lines() {
    say MessyDate.parse($_).made // "failed to parse '$_'";
}

在 grammar 中, 有兩個 regex 的變體, ruletoken珠插。rule 默認(rèn)不會回溯. rule 與 token 的一個重要區(qū)別是, rule 這樣的正則采取了 :sigspace 修飾符惧磺。 rule 實(shí)際上是

    regex :ratchet :sigspace { ... }

的簡寫. ratchet 這個單詞的意思是: (防倒轉(zhuǎn)的)棘齒, 意思它是不能回溯的! 而 :sigspace 表明正則中的空白是有意義的, 而 token 實(shí)際上是

    regex :ratchet { ... }

的簡寫。 所以在 token 中, 若不是顯式的寫上 \s捻撑、\h豺妓、\n 等空白符號, 其它情況下就好像空白隱身了一樣, 雖然你寫了, 但是編譯器卻視而不見。

// 在左側(cè)匹配失敗時會在右側(cè)提供一個默認(rèn)值布讹。

<number(4)><number(2, 0..49)> 中使用了擴(kuò)展了的 <...> 元語法。 標(biāo)識符(例如左面的 number)后面的第一個字符決定了閉合尖括號之前剩余文本的處理训堆。它的底層語義是函數(shù)或方法調(diào)用, 所以, 如果標(biāo)識符后面的第一個字符是左圓括號, 那么它要么是方法調(diào)用, 要么是函數(shù)調(diào)用:

<number(4)> 等價于 <number=&number(4)>

<number(2, 0..49)> 等價于 <number=&number(2, 0..49)>

multi token number ($digits)        {  <[0..9]> ** {$digits} }
multi token number ($digits, $test) { (<[0..9]> ** {$digits}) <?{ +$0 ~~ $test }> }

在擴(kuò)展的 <...> 語法中, 一個前置的 ?{!{ 標(biāo)示著代碼斷言:

(<[0..9]> ** {$digits}) <?{ +$0 ~~ $test }>

等價于:

(<[0..9]> ** {$digits}) { +$0 ~~ $test or fail }  # + 強(qiáng)制后面的$0為數(shù)值上下文, 以匹配 $test 中的數(shù)字

上面的兩句代碼, 具名 regex, token, 或 rule 是一個子例程, 所以可以傳遞 參數(shù)給具名 token描验。

這從標(biāo)準(zhǔn)輸入里讀取散亂的日期并把對應(yīng)的 ISO 日期寫到標(biāo)準(zhǔn)輸出。

它能解析任務(wù)描述中的所有日期(包含擴(kuò)展), 還有 - 然而, 在它們中我得到 4 個人不同結(jié)果坑鱼。請弄清它們是否是錯誤的, 并且為什么是錯的:


2010-dec-7 --> 我得到 2010-12-07 而不是 2010-12-01

last week --> 我得到 2014-12-17 而不是 2014-12-15

1 month from 2016-01-31 --> 我得到 2016-02-29 而不是 2016-02-28

9 weeks from yesterday --> 我得到 2015-02-24 而不是 2015-02-25

有人在評論中問他 make/made 是類中的方法嗎膘流?

是的, 它們是 Match 類的方法。

Match objects(注意 object 是復(fù)數(shù))


每個 regex match(并且通過擴(kuò)展, 每個 grammar token match)的結(jié)果被表示為一個 Match 對象鲁沥。

通過這個對象你能訪問各種信息片段:

  • 匹配到的字符串

  • 關(guān)于輸入字符串匹配的開始和結(jié)束位置

  • 每個位置捕獲和具名捕獲的sub-matches

  • 與這個匹配有關(guān)的 AST 片段, 如果有的話

    AST 片段

token/rule 里面調(diào)用 make, 設(shè)置將會與當(dāng)前匹配關(guān)聯(lián)的 "AST 片段"呼股。然后, 你可以通過在當(dāng)前結(jié)果 Match 對象身上調(diào)用 .made 方法來獲取那個關(guān)聯(lián)數(shù)據(jù)。

這正是自由形式的插槽, 允許你使用 Match 對象存儲任何你想要的東西并在以后檢索它, 盡管顯而易見這意味著像我那樣創(chuàng)建一個 AST画恰。

在 grammar 中創(chuàng)建 "AST"

在我的 grammar 中每個 token/rule 使用 .made 來取得它的 sub-rule 匹配制造的數(shù)據(jù)塊(AST), 把它們組合成一個更大的數(shù)據(jù)塊, 這是為了讓它的父級 rule (parent rule) 能被檢索彭谁。等等。

我在每個 token/rule 里面使用這些語法簡寫來引用 sub-matches 的 Match 對象:

  • $0 是指第一個位置子匹配(由 () 捕獲組引起)的Match對象允扇。
  • $<date> 指的是名字為 "date" 的具名 sub-match 的 Match 對象 (通過 <date> 遞歸引用 名為 date 的 token 引起).
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缠局,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子考润,更是在濱河造成了極大的恐慌狭园,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糊治,死亡現(xiàn)場離奇詭異唱矛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進(jìn)店門绎谦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來管闷,“玉大人,你說我怎么就攤上這事燥滑〗ケ保” “怎么了?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵铭拧,是天一觀的道長赃蛛。 經(jīng)常有香客問我,道長搀菩,這世上最難降的妖魔是什么呕臂? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮肪跋,結(jié)果婚禮上歧蒋,老公的妹妹穿的比我還像新娘。我一直安慰自己州既,他們只是感情好谜洽,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吴叶,像睡著了一般阐虚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚌卤,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天实束,我揣著相機(jī)與錄音,去河邊找鬼逊彭。 笑死咸灿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的侮叮。 我是一名探鬼主播避矢,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼囊榜!你這毒婦竟也來了谷异?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤锦聊,失蹤者是張志新(化名)和其女友劉穎歹嘹,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體孔庭,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尺上,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年材蛛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怎抛。...
    茶點(diǎn)故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡卑吭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出马绝,到底是詐尸還是另有隱情豆赏,我是刑警寧澤,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布富稻,位于F島的核電站掷邦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏椭赋。R本人自食惡果不足惜抚岗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哪怔。 院中可真熱鬧宣蔚,春花似錦、人聲如沸认境。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叉信。三九已至亩冬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茉盏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工枢冤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸠姨,地道東北人。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓淹真,卻偏偏與公主長得像讶迁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子核蘸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評論 2 345

推薦閱讀更多精彩內(nèi)容