2012-07-06 70 views

Trả lời

15

Hãy thử điều này:

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

Upvote để sử dụng hiệu quả của sự khẳng định ngược chiều rộng tiêu cực khó nắm bắt. Mặc dù (đáng buồn) như Bill Ruppert chỉ ra, có một mô-đun CPAN cho điều này rồi. –

+3

Mặc dù có * là * một giải pháp CPAN, tôi cũng thích cái này. Nó cũng được suy nghĩ, dễ đọc, không phụ thuộc và chính xác như giải pháp CPAN cho bất kỳ số nguyên nào. – DavidO

27

Sử dụng Lingua::EN::Numbers::Ordinate. Từ tóm tắt:

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

Hãy thử chương trình con ngắn gọn này

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

đầu ra

42nd 
521st 
113th 
+0

Có điều gì đó tôi không hiểu đầy đủ ở đây. Tại sao một vòng lặp 'for' khi chỉ có một phần tử làm đối số? Nó cũng có thể làm việc 'return int (shift). (qw/... '. Đối với một số tham số, vòng lặp' for' sẽ không hoạt động vì câu lệnh 'return'. Nó hoạt động tốt như vậy, nhưng tôi có bỏ sót điều gì đó về vòng lặp không? – Birei

+0

@Birei: nó chỉ là một cách để đặt '$ _ [0]' vào '$ _'. Cách của bạn sẽ không hoạt động vì biểu thức chính quy cần giá trị nằm trong' $ _'. Nó giống như từ ngữ 'given' mới nhưng bạn không thể sử dụng điều đó làm công cụ sửa đổi câu lệnh như bạn có thể với 'for'. – Borodin

+0

Ah, ok. Cảm ơn bạn. Không nhận được điểm' $ _'. Nó xứng đáng là ** ** 1 **. – Birei

2

Dưới đây là một giải pháp which I originally wrote for a code golf challenge, hơi viết lại cho phù hợp với thông lệ tốt nhất thông thường cho mã phi golf :

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

Cách hoạt động của nó là regexp (1?\d)$ khớp với chữ số cuối cùng của số, cộng với số trước nếu nó là 1. Thay thế sau đó sử dụng (các) chữ số phù hợp làm chỉ mục cho danh sách (qw'th st nd rd'), ánh xạ 0 đến th, 1 đến st, 2 đến nd, 3 đến rd và bất kỳ giá trị nào khác để hoàn tác. Cuối cùng, toán tử || thay thế undef bằng th.

Nếu bạn không thích s///e, về cơ bản, giải pháp tương tự có thể được viết, ví dụ: như thế này:

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

hoặc như là một chức năng:

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

Một giải pháp khác (mặc dù tôi thích câu trả lời từ trước đó là độc lập của việc sử dụng các module tốt hơn):

use Date::Calc 'English_Ordinal'; 
print English_Ordinal $ARGV[0]; 
Các vấn đề liên quan