2012-07-06 37 views

回答

15

試試這個:

my $ordinal; 
if ($foo =~ /(?<!1)1$/) { 
    $ordinal = 'st'; 
} elsif ($foo =~ /(?<!1)2$/) { 
    $ordinal = 'nd'; 
} elsif ($foo =~ /(?<!1)3$/) { 
    $ordinal = 'rd'; 
} else { 
    $ordinal = 'th'; 
} 
+2

給予好評用於生產的難以捉摸的零寬度負面後顧斷言。雖然(可惜)比爾·魯珀特指出,已經有一個CPAN模塊。 – 2012-07-06 23:35:43

+3

儘管*有* CPAN解決方案,我也喜歡這個。它經過深思熟慮,可讀性強,沒有依賴關係,並且與任何整數的CPAN解決方案一樣精確。 – DavidO 2012-07-07 08:20:31

27

使用Lingua::EN::Numbers::Ordinate。從簡介:

use Lingua::EN::Numbers::Ordinate; 
print ordinate(4), "\n"; 
# prints 4th 
print ordinate(-342), "\n"; 
# prints -342nd 

# Example of actual use: 
... 
for(my $i = 0; $i < @records; $i++) { 
    unless(is_valid($record[$i]) { 
    warn "The ", ordinate($i), " record is invalid!\n"; 
    next; 
    } 
    ... 
} 
7

試試這個簡單的子程序

use strict; 
use warnings; 

sub ordinal { 
    return $_.(qw/th st nd rd/)[/(?<!1)([123])$/ ? $1 : 0] for int shift; 
} 

for (42, 521, 113) { 
    print ordinal($_), "\n"; 
} 

輸出

42nd 
521st 
113th 
+0

這裏有一些我不完全明白的地方。爲什麼在只有一個元素作爲參數時使用'for'循環?它也可以工作'返回int(shift)。 (qw/...''。對於幾個參數,'for'循環不會因爲return語句而工作,它可以正常工作,但是我錯過了一些關於循環的東西嗎? – Birei 2012-07-23 14:00:43

+0

@Birei:它只是一種將'$ _ [0]'放入'$ _'中的方法,因爲正則表達式需要值在'$ _'中,所以你的方法不行,這很像新的'given'語言詞,但是你不能用'as'作爲語句修飾符。 – Borodin 2012-07-23 14:19:24

+0

好的,謝謝。沒有得到'$ _'的意義,它值得** + 1 **。 – Birei 2012-07-23 14:25:35

2

這裏有一個解決方案which I originally wrote for a code golf challenge,稍微改寫符合非高爾夫代碼通常的最佳實踐:

$number =~ s/(1?\d)$/$1 . ((qw'th st nd rd')[$1] || 'th')/e; 

它的工作方式是正則表達式(1?\d)$匹配數字的最後一位數字加上前面的數字,如果它是1。該替換然後使用匹配的數字作爲列表(qw'th st nd rd')的索引,將0到th,1到st,2到nd,3到rd以及任何其他值到undef。最後,||運營商用th替換undef。

如果您不喜歡s///e,則可以編寫基本相同的解決方案,例如,像這樣:

for ($number) { 
    /(1?\d)$/ or next; 
    $_ .= (qw'th st nd rd')[$1] || 'th'; 
} 

或作爲一個函數:

sub ordinal ($) { 
    $_[0] =~ /(1?\d)$/ or return; 
    return $_[0] . ((qw'th st nd rd')[$1] || 'th'); 
} 
0

另一種解決方案(雖然我喜歡的預先存在的答案是獨立於使用模塊更好的):

use Date::Calc 'English_Ordinal'; 
print English_Ordinal $ARGV[0]; 
相關問題